1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
7 Copyright 2012 Holger Hans Peter Freyther
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <linux/oom.h>
30 #include <sys/prctl.h>
31 #include <sys/mount.h>
35 #include <sys/resource.h>
41 #include "sd-messages.h"
44 #include "conf-parser.h"
45 #include "load-fragment.h"
48 #include "securebits.h"
50 #include "unit-name.h"
51 #include "unit-printf.h"
53 #include "path-util.h"
57 #include "bus-error.h"
58 #include "errno-list.h"
61 #include "seccomp-util.h"
64 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK)
65 int config_parse_warn_compat(
70 unsigned section_line,
77 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
78 "Support for option %s= has been disabled at compile time and is ignored",
84 int config_parse_unit_deps(const char* unit,
88 unsigned section_line,
95 UnitDependency d = ltype;
105 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
106 _cleanup_free_ char *t = NULL, *k = NULL;
113 r = unit_name_printf(u, t, &k);
115 log_syntax(unit, LOG_ERR, filename, line, -r,
116 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
120 r = unit_add_dependency_by_name(u, d, k, NULL, true);
122 log_syntax(unit, LOG_ERR, filename, line, -r,
123 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
129 int config_parse_unit_string_printf(const char *unit,
130 const char *filename,
133 unsigned section_line,
141 _cleanup_free_ char *k = NULL;
149 r = unit_full_printf(u, rvalue, &k);
151 log_syntax(unit, LOG_ERR, filename, line, -r,
152 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
154 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
155 k ? k : rvalue, data, userdata);
158 int config_parse_unit_strv_printf(const char *unit,
159 const char *filename,
162 unsigned section_line,
170 _cleanup_free_ char *k = NULL;
178 r = unit_full_printf(u, rvalue, &k);
180 log_syntax(unit, LOG_ERR, filename, line, -r,
181 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
183 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
184 k ? k : rvalue, data, userdata);
187 int config_parse_unit_path_printf(const char *unit,
188 const char *filename,
191 unsigned section_line,
199 _cleanup_free_ char *k = NULL;
207 r = unit_full_printf(u, rvalue, &k);
209 log_syntax(unit, LOG_ERR, filename, line, -r,
210 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
212 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype,
213 k ? k : rvalue, data, userdata);
216 int config_parse_socket_listen(const char *unit,
217 const char *filename,
220 unsigned section_line,
227 SocketPort *p, *tail;
238 if (isempty(rvalue)) {
239 /* An empty assignment removes all ports */
240 socket_free_ports(s);
244 p = new0(SocketPort, 1);
248 if (ltype != SOCKET_SOCKET) {
251 r = unit_full_printf(UNIT(s), rvalue, &p->path);
253 p->path = strdup(rvalue);
258 log_syntax(unit, LOG_ERR, filename, line, -r,
259 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
262 path_kill_slashes(p->path);
264 } else if (streq(lvalue, "ListenNetlink")) {
265 _cleanup_free_ char *k = NULL;
267 p->type = SOCKET_SOCKET;
268 r = unit_full_printf(UNIT(s), rvalue, &k);
270 log_syntax(unit, LOG_ERR, filename, line, -r,
271 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
273 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
275 log_syntax(unit, LOG_ERR, filename, line, -r,
276 "Failed to parse address value, ignoring: %s", rvalue);
282 _cleanup_free_ char *k = NULL;
284 p->type = SOCKET_SOCKET;
285 r = unit_full_printf(UNIT(s), rvalue, &k);
287 log_syntax(unit, LOG_ERR, filename, line, -r,
288 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
290 r = socket_address_parse(&p->address, k ? k : rvalue);
292 log_syntax(unit, LOG_ERR, filename, line, -r,
293 "Failed to parse address value, ignoring: %s", rvalue);
298 if (streq(lvalue, "ListenStream"))
299 p->address.type = SOCK_STREAM;
300 else if (streq(lvalue, "ListenDatagram"))
301 p->address.type = SOCK_DGRAM;
303 assert(streq(lvalue, "ListenSequentialPacket"));
304 p->address.type = SOCK_SEQPACKET;
307 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
308 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
309 "Address family not supported, ignoring: %s", rvalue);
319 LIST_FIND_TAIL(port, s->ports, tail);
320 LIST_INSERT_AFTER(port, s->ports, tail, p);
322 LIST_PREPEND(port, s->ports, p);
327 int config_parse_socket_bind(const char *unit,
328 const char *filename,
331 unsigned section_line,
339 SocketAddressBindIPv6Only b;
348 b = socket_address_bind_ipv6_only_from_string(rvalue);
352 r = parse_boolean(rvalue);
354 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
355 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
359 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
361 s->bind_ipv6_only = b;
366 int config_parse_exec_nice(const char *unit,
367 const char *filename,
370 unsigned section_line,
377 ExecContext *c = data;
385 r = safe_atoi(rvalue, &priority);
387 log_syntax(unit, LOG_ERR, filename, line, -r,
388 "Failed to parse nice priority, ignoring: %s. ", rvalue);
392 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
393 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
394 "Nice priority out of range, ignoring: %s", rvalue);
404 int config_parse_exec_oom_score_adjust(const char* unit,
405 const char *filename,
408 unsigned section_line,
415 ExecContext *c = data;
423 r = safe_atoi(rvalue, &oa);
425 log_syntax(unit, LOG_ERR, filename, line, -r,
426 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
430 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
431 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
432 "OOM score adjust value out of range, ignoring: %s", rvalue);
436 c->oom_score_adjust = oa;
437 c->oom_score_adjust_set = true;
442 int config_parse_exec(const char *unit,
443 const char *filename,
446 unsigned section_line,
453 ExecCommand **e = data, *nce;
465 if (isempty(rvalue)) {
466 /* An empty assignment resets the list */
467 exec_command_free_list(*e);
472 /* We accept an absolute path as first argument, or
473 * alternatively an absolute prefixed with @ to allow
474 * overriding of argv[0]. */
480 bool honour_argv0 = false, ignore = false;
486 rvalue += strspn(rvalue, WHITESPACE);
491 for (i = 0; i < 2; i++) {
492 if (rvalue[0] == '-' && !ignore) {
497 if (rvalue[0] == '@' && !honour_argv0) {
503 if (*rvalue != '/') {
504 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
505 "Executable path is not absolute, ignoring: %s", rvalue);
510 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
511 if (strneq(w, ";", MAX(l, 1U)))
517 n = new(char*, k + !honour_argv0);
522 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
523 if (strneq(w, ";", MAX(l, 1U)))
525 else if (strneq(w, "\\;", MAX(l, 1U)))
528 if (honour_argv0 && w == rvalue) {
531 path = strndup(w, l);
537 if (!utf8_is_valid(path)) {
538 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
539 "Path is not UTF-8 clean, ignoring assignment: %s",
548 c = n[k++] = cunescape_length(w, l);
554 if (!utf8_is_valid(c)) {
555 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
556 "Path is not UTF-8 clean, ignoring assignment: %s",
567 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
568 "Invalid command line, ignoring: %s", rvalue);
581 assert(path_is_absolute(path));
583 nce = new0(ExecCommand, 1);
591 nce->ignore = ignore;
593 path_kill_slashes(nce->path);
595 exec_command_append_list(e, nce);
611 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
612 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
614 int config_parse_socket_bindtodevice(const char* unit,
615 const char *filename,
618 unsigned section_line,
633 if (rvalue[0] && !streq(rvalue, "*")) {
640 free(s->bind_to_device);
641 s->bind_to_device = n;
646 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
647 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
649 int config_parse_exec_io_class(const char *unit,
650 const char *filename,
653 unsigned section_line,
660 ExecContext *c = data;
668 x = ioprio_class_from_string(rvalue);
670 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
671 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
675 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
676 c->ioprio_set = true;
681 int config_parse_exec_io_priority(const char *unit,
682 const char *filename,
685 unsigned section_line,
692 ExecContext *c = data;
700 r = safe_atoi(rvalue, &i);
701 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
702 log_syntax(unit, LOG_ERR, filename, line, -r,
703 "Failed to parse IO priority, ignoring: %s", rvalue);
707 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
708 c->ioprio_set = true;
713 int config_parse_exec_cpu_sched_policy(const char *unit,
714 const char *filename,
717 unsigned section_line,
725 ExecContext *c = data;
733 x = sched_policy_from_string(rvalue);
735 log_syntax(unit, LOG_ERR, filename, line, -x,
736 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
740 c->cpu_sched_policy = x;
741 /* Moving to or from real-time policy? We need to adjust the priority */
742 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
743 c->cpu_sched_set = true;
748 int config_parse_exec_cpu_sched_prio(const char *unit,
749 const char *filename,
752 unsigned section_line,
759 ExecContext *c = data;
767 r = safe_atoi(rvalue, &i);
769 log_syntax(unit, LOG_ERR, filename, line, -r,
770 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
774 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
775 min = sched_get_priority_min(c->cpu_sched_policy);
776 max = sched_get_priority_max(c->cpu_sched_policy);
778 if (i < min || i > max) {
779 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
780 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
784 c->cpu_sched_priority = i;
785 c->cpu_sched_set = true;
790 int config_parse_exec_cpu_affinity(const char *unit,
791 const char *filename,
794 unsigned section_line,
801 ExecContext *c = data;
811 if (isempty(rvalue)) {
812 /* An empty assignment resets the CPU list */
819 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
820 _cleanup_free_ char *t = NULL;
828 r = safe_atou(t, &cpu);
831 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
836 if (r < 0 || cpu >= c->cpuset_ncpus) {
837 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
838 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
842 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
848 int config_parse_exec_capabilities(const char *unit,
849 const char *filename,
852 unsigned section_line,
859 ExecContext *c = data;
867 cap = cap_from_text(rvalue);
869 log_syntax(unit, LOG_ERR, filename, line, errno,
870 "Failed to parse capabilities, ignoring: %s", rvalue);
875 cap_free(c->capabilities);
876 c->capabilities = cap;
881 int config_parse_exec_secure_bits(const char *unit,
882 const char *filename,
885 unsigned section_line,
892 ExecContext *c = data;
902 if (isempty(rvalue)) {
903 /* An empty assignment resets the field */
908 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
909 if (first_word(w, "keep-caps"))
910 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
911 else if (first_word(w, "keep-caps-locked"))
912 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
913 else if (first_word(w, "no-setuid-fixup"))
914 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
915 else if (first_word(w, "no-setuid-fixup-locked"))
916 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
917 else if (first_word(w, "noroot"))
918 c->secure_bits |= 1<<SECURE_NOROOT;
919 else if (first_word(w, "noroot-locked"))
920 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
922 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
923 "Failed to parse secure bits, ignoring: %s", rvalue);
931 int config_parse_bounding_set(const char *unit,
932 const char *filename,
935 unsigned section_line,
942 uint64_t *capability_bounding_set_drop = data;
954 if (rvalue[0] == '~') {
959 /* Note that we store this inverted internally, since the
960 * kernel wants it like this. But we actually expose it
961 * non-inverted everywhere to have a fully normalized
964 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
965 _cleanup_free_ char *t = NULL;
973 r = cap_from_name(t, &cap);
975 log_syntax(unit, LOG_ERR, filename, line, errno,
976 "Failed to parse capability in bounding set, ignoring: %s", t);
980 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
984 *capability_bounding_set_drop |= sum;
986 *capability_bounding_set_drop |= ~sum;
991 int config_parse_limit(const char *unit,
992 const char *filename,
995 unsigned section_line,
1002 struct rlimit **rl = data;
1003 unsigned long long u;
1012 if (streq(rvalue, "infinity"))
1013 u = (unsigned long long) RLIM_INFINITY;
1017 r = safe_atollu(rvalue, &u);
1019 log_syntax(unit, LOG_ERR, filename, line, -r,
1020 "Failed to parse resource value, ignoring: %s", rvalue);
1026 *rl = new(struct rlimit, 1);
1031 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
1035 #ifdef HAVE_SYSV_COMPAT
1036 int config_parse_sysv_priority(const char *unit,
1037 const char *filename,
1039 const char *section,
1040 unsigned section_line,
1047 int *priority = data;
1055 r = safe_atoi(rvalue, &i);
1056 if (r < 0 || i < 0) {
1057 log_syntax(unit, LOG_ERR, filename, line, -r,
1058 "Failed to parse SysV start priority, ignoring: %s", rvalue);
1062 *priority = (int) i;
1067 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1069 int config_parse_kill_signal(const char *unit,
1070 const char *filename,
1072 const char *section,
1073 unsigned section_line,
1088 r = signal_from_string_try_harder(rvalue);
1090 log_syntax(unit, LOG_ERR, filename, line, -r,
1091 "Failed to parse kill signal, ignoring: %s", rvalue);
1099 int config_parse_exec_mount_flags(const char *unit,
1100 const char *filename,
1102 const char *section,
1103 unsigned section_line,
1110 ExecContext *c = data;
1114 unsigned long flags = 0;
1121 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
1122 _cleanup_free_ char *t;
1128 if (streq(t, "shared"))
1130 else if (streq(t, "slave"))
1132 else if (streq(w, "private"))
1133 flags |= MS_PRIVATE;
1135 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1136 "Failed to parse mount flag %s, ignoring: %s",
1142 c->mount_flags = flags;
1146 int config_parse_exec_selinux_context(
1148 const char *filename,
1150 const char *section,
1151 unsigned section_line,
1158 ExecContext *c = data;
1169 if (isempty(rvalue)) {
1170 free(c->selinux_context);
1171 c->selinux_context = NULL;
1172 c->selinux_context_ignore = false;
1176 if (rvalue[0] == '-') {
1182 r = unit_name_printf(u, rvalue, &k);
1184 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1188 free(c->selinux_context);
1189 c->selinux_context = k;
1190 c->selinux_context_ignore = ignore;
1195 int config_parse_timer(const char *unit,
1196 const char *filename,
1198 const char *section,
1199 unsigned section_line,
1210 CalendarSpec *c = NULL;
1218 if (isempty(rvalue)) {
1219 /* Empty assignment resets list */
1220 timer_free_values(t);
1224 b = timer_base_from_string(lvalue);
1226 log_syntax(unit, LOG_ERR, filename, line, -b,
1227 "Failed to parse timer base, ignoring: %s", lvalue);
1231 if (b == TIMER_CALENDAR) {
1232 if (calendar_spec_from_string(rvalue, &c) < 0) {
1233 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1234 "Failed to parse calendar specification, ignoring: %s",
1239 id = CLOCK_REALTIME;
1241 if (parse_sec(rvalue, &u) < 0) {
1242 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1243 "Failed to parse timer value, ignoring: %s",
1248 id = CLOCK_MONOTONIC;
1251 v = new0(TimerValue, 1);
1258 v->calendar_spec = c;
1260 LIST_PREPEND(value, t->values, v);
1265 int config_parse_trigger_unit(
1267 const char *filename,
1269 const char *section,
1270 unsigned section_line,
1277 _cleanup_free_ char *p = NULL;
1287 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1288 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1289 "Multiple units to trigger specified, ignoring: %s", rvalue);
1293 r = unit_name_printf(u, rvalue, &p);
1295 log_syntax(unit, LOG_ERR, filename, line, -r,
1296 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1298 type = unit_name_to_type(p ?: rvalue);
1300 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1301 "Unit type not valid, ignoring: %s", rvalue);
1305 if (type == u->type) {
1306 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1307 "Trigger cannot be of same type, ignoring: %s", rvalue);
1311 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
1313 log_syntax(unit, LOG_ERR, filename, line, -r,
1314 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
1321 int config_parse_path_spec(const char *unit,
1322 const char *filename,
1324 const char *section,
1325 unsigned section_line,
1335 _cleanup_free_ char *k = NULL;
1343 if (isempty(rvalue)) {
1344 /* Empty assignment clears list */
1349 b = path_type_from_string(lvalue);
1351 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1352 "Failed to parse path type, ignoring: %s", lvalue);
1356 r = unit_full_printf(UNIT(p), rvalue, &k);
1362 log_syntax(unit, LOG_ERR, filename, line, -r,
1363 "Failed to resolve unit specifiers on %s. Ignoring.",
1367 if (!path_is_absolute(k)) {
1368 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1369 "Path is not absolute, ignoring: %s", k);
1373 s = new0(PathSpec, 1);
1378 s->path = path_kill_slashes(k);
1383 LIST_PREPEND(spec, p->specs, s);
1388 int config_parse_socket_service(const char *unit,
1389 const char *filename,
1391 const char *section,
1392 unsigned section_line,
1399 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1403 _cleanup_free_ char *p = NULL;
1410 r = unit_name_printf(UNIT(s), rvalue, &p);
1412 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1416 if (!endswith(p, ".service")) {
1417 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1421 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1423 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1427 unit_ref_set(&s->service, x);
1432 int config_parse_service_sockets(const char *unit,
1433 const char *filename,
1435 const char *section,
1436 unsigned section_line,
1453 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1454 _cleanup_free_ char *t = NULL, *k = NULL;
1460 r = unit_name_printf(UNIT(s), t, &k);
1462 log_syntax(unit, LOG_ERR, filename, line, -r,
1463 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1465 if (!endswith(k ?: t, ".socket")) {
1466 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1467 "Unit must be of type socket, ignoring: %s", k ?: t);
1471 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
1473 log_syntax(unit, LOG_ERR, filename, line, -r,
1474 "Failed to add dependency on %s, ignoring: %s",
1475 k ?: t, strerror(-r));
1477 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
1485 int config_parse_service_timeout(const char *unit,
1486 const char *filename,
1488 const char *section,
1489 unsigned section_line,
1496 Service *s = userdata;
1504 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
1505 rvalue, data, userdata);
1509 if (streq(lvalue, "TimeoutSec")) {
1510 s->start_timeout_defined = true;
1511 s->timeout_stop_usec = s->timeout_start_usec;
1512 } else if (streq(lvalue, "TimeoutStartSec"))
1513 s->start_timeout_defined = true;
1518 int config_parse_busname_service(
1520 const char *filename,
1522 const char *section,
1523 unsigned section_line,
1530 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1534 _cleanup_free_ char *p = NULL;
1541 r = unit_name_printf(UNIT(n), rvalue, &p);
1543 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1547 if (!endswith(p, ".service")) {
1548 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1552 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1554 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1558 unit_ref_set(&n->service, x);
1563 int config_parse_unit_env_file(const char *unit,
1564 const char *filename,
1566 const char *section,
1567 unsigned section_line,
1576 _cleanup_free_ char *n = NULL;
1585 if (isempty(rvalue)) {
1586 /* Empty assignment frees the list */
1592 r = unit_full_printf(u, rvalue, &n);
1594 log_syntax(unit, LOG_ERR, filename, line, r,
1595 "Failed to resolve specifiers, ignoring: %s", rvalue);
1598 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1599 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1600 "Path '%s' is not absolute, ignoring.", s);
1604 r = strv_extend(env, s);
1611 int config_parse_environ(const char *unit,
1612 const char *filename,
1614 const char *section,
1615 unsigned section_line,
1623 char*** env = data, *w, *state;
1625 _cleanup_free_ char *k = NULL;
1633 if (isempty(rvalue)) {
1634 /* Empty assignment resets the list */
1641 r = unit_full_printf(u, rvalue, &k);
1643 log_syntax(unit, LOG_ERR, filename, line, -r,
1644 "Failed to resolve specifiers, ignoring: %s", rvalue);
1652 FOREACH_WORD_QUOTED(w, l, k, state) {
1653 _cleanup_free_ char *n;
1656 n = cunescape_length(w, l);
1660 if (!env_assignment_is_valid(n)) {
1661 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1662 "Invalid environment assignment, ignoring: %s", rvalue);
1666 x = strv_env_set(*env, n);
1677 int config_parse_ip_tos(const char *unit,
1678 const char *filename,
1680 const char *section,
1681 unsigned section_line,
1688 int *ip_tos = data, x;
1695 x = ip_tos_from_string(rvalue);
1697 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1698 "Failed to parse IP TOS value, ignoring: %s", rvalue);
1706 int config_parse_unit_condition_path(const char *unit,
1707 const char *filename,
1709 const char *section,
1710 unsigned section_line,
1717 ConditionType cond = ltype;
1719 bool trigger, negate;
1721 _cleanup_free_ char *p = NULL;
1729 if (isempty(rvalue)) {
1730 /* Empty assignment resets the list */
1731 condition_free_list(u->conditions);
1732 u->conditions = NULL;
1736 trigger = rvalue[0] == '|';
1740 negate = rvalue[0] == '!';
1744 r = unit_full_printf(u, rvalue, &p);
1746 log_syntax(unit, LOG_ERR, filename, line, -r,
1747 "Failed to resolve specifiers, ignoring: %s", rvalue);
1754 if (!path_is_absolute(p)) {
1755 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1756 "Path in condition not absolute, ignoring: %s", p);
1760 c = condition_new(cond, p, trigger, negate);
1764 LIST_PREPEND(conditions, u->conditions, c);
1768 int config_parse_unit_condition_string(const char *unit,
1769 const char *filename,
1771 const char *section,
1772 unsigned section_line,
1779 ConditionType cond = ltype;
1781 bool trigger, negate;
1783 _cleanup_free_ char *s = NULL;
1791 if (isempty(rvalue)) {
1792 /* Empty assignment resets the list */
1793 condition_free_list(u->conditions);
1794 u->conditions = NULL;
1798 trigger = rvalue[0] == '|';
1802 negate = rvalue[0] == '!';
1806 r = unit_full_printf(u, rvalue, &s);
1808 log_syntax(unit, LOG_ERR, filename, line, -r,
1809 "Failed to resolve specifiers, ignoring: %s", rvalue);
1816 c = condition_new(cond, s, trigger, negate);
1820 LIST_PREPEND(conditions, u->conditions, c);
1824 int config_parse_unit_condition_null(const char *unit,
1825 const char *filename,
1827 const char *section,
1828 unsigned section_line,
1837 bool trigger, negate;
1845 if (isempty(rvalue)) {
1846 /* Empty assignment resets the list */
1847 condition_free_list(u->conditions);
1848 u->conditions = NULL;
1852 trigger = rvalue[0] == '|';
1856 negate = rvalue[0] == '!';
1860 b = parse_boolean(rvalue);
1862 log_syntax(unit, LOG_ERR, filename, line, -b,
1863 "Failed to parse boolean value in condition, ignoring: %s",
1871 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1875 LIST_PREPEND(conditions, u->conditions, c);
1879 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1880 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
1882 int config_parse_unit_requires_mounts_for(
1884 const char *filename,
1886 const char *section,
1887 unsigned section_line,
1904 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1906 _cleanup_free_ char *n;
1912 if (!utf8_is_valid(n)) {
1913 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1914 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
1918 r = unit_require_mounts_for(u, n);
1920 log_syntax(unit, LOG_ERR, filename, line, r,
1921 "Failed to add required mount for, ignoring: %s", rvalue);
1929 int config_parse_documentation(const char *unit,
1930 const char *filename,
1932 const char *section,
1933 unsigned section_line,
1949 if (isempty(rvalue)) {
1950 /* Empty assignment resets the list */
1951 strv_free(u->documentation);
1952 u->documentation = NULL;
1956 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
1957 rvalue, data, userdata);
1961 for (a = b = u->documentation; a && *a; a++) {
1963 if (is_valid_documentation_url(*a))
1966 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1967 "Invalid URL, ignoring: %s", *a);
1978 int config_parse_syscall_filter(
1980 const char *filename,
1982 const char *section,
1983 unsigned section_line,
1990 static const char default_syscalls[] =
1997 ExecContext *c = data;
1999 bool invert = false;
2009 if (isempty(rvalue)) {
2010 /* Empty assignment resets the list */
2011 set_free(c->syscall_filter);
2012 c->syscall_filter = NULL;
2013 c->syscall_whitelist = false;
2017 if (rvalue[0] == '~') {
2022 if (!c->syscall_filter) {
2023 c->syscall_filter = set_new(trivial_hash_func, trivial_compare_func);
2024 if (!c->syscall_filter)
2028 /* Allow everything but the ones listed */
2029 c->syscall_whitelist = false;
2033 /* Allow nothing but the ones listed */
2034 c->syscall_whitelist = true;
2036 /* Accept default syscalls if we are on a whitelist */
2037 NULSTR_FOREACH(i, default_syscalls) {
2040 id = seccomp_syscall_resolve_name(i);
2044 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2053 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2054 _cleanup_free_ char *t = NULL;
2061 id = seccomp_syscall_resolve_name(t);
2063 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call, ignoring: %s", t);
2067 /* If we previously wanted to forbid a syscall and now
2068 * we want to allow it, then remove it from the list
2070 if (!invert == c->syscall_whitelist) {
2071 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2077 set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
2080 c->no_new_privileges = true;
2085 int config_parse_syscall_archs(
2087 const char *filename,
2089 const char *section,
2090 unsigned section_line,
2102 if (isempty(rvalue)) {
2108 r = set_ensure_allocated(archs, trivial_hash_func, trivial_compare_func);
2112 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2113 _cleanup_free_ char *t = NULL;
2120 r = seccomp_arch_from_string(t, &a);
2122 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call architecture, ignoring: %s", t);
2126 r = set_put(*archs, UINT32_TO_PTR(a + 1));
2136 int config_parse_syscall_errno(
2138 const char *filename,
2140 const char *section,
2141 unsigned section_line,
2148 ExecContext *c = data;
2155 if (isempty(rvalue)) {
2156 /* Empty assignment resets to KILL */
2157 c->syscall_errno = 0;
2161 e = errno_from_name(rvalue);
2163 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse error number, ignoring: %s", rvalue);
2167 c->syscall_errno = e;
2172 int config_parse_unit_slice(
2174 const char *filename,
2176 const char *section,
2177 unsigned section_line,
2184 _cleanup_free_ char *k = NULL;
2185 Unit *u = userdata, *slice;
2193 r = unit_name_printf(u, rvalue, &k);
2195 log_syntax(unit, LOG_ERR, filename, line, -r,
2196 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2203 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
2205 log_syntax(unit, LOG_ERR, filename, line, -r,
2206 "Failed to load slice unit %s. Ignoring.", k);
2210 if (slice->type != UNIT_SLICE) {
2211 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2212 "Slice unit %s is not a slice. Ignoring.", k);
2216 unit_ref_set(&u->slice, slice);
2220 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2222 int config_parse_cpu_shares(
2224 const char *filename,
2226 const char *section,
2227 unsigned section_line,
2234 CGroupContext *c = data;
2242 if (isempty(rvalue)) {
2243 c->cpu_shares = 1024;
2247 r = safe_atolu(rvalue, &lu);
2248 if (r < 0 || lu <= 0) {
2249 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2250 "CPU shares '%s' invalid. Ignoring.", rvalue);
2258 int config_parse_memory_limit(
2260 const char *filename,
2262 const char *section,
2263 unsigned section_line,
2270 CGroupContext *c = data;
2274 if (isempty(rvalue)) {
2275 c->memory_limit = (uint64_t) -1;
2279 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2281 r = parse_bytes(rvalue, &bytes);
2283 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2284 "Memory limit '%s' invalid. Ignoring.", rvalue);
2288 c->memory_limit = (uint64_t) bytes;
2292 int config_parse_device_allow(
2294 const char *filename,
2296 const char *section,
2297 unsigned section_line,
2304 _cleanup_free_ char *path = NULL;
2305 CGroupContext *c = data;
2306 CGroupDeviceAllow *a;
2310 if (isempty(rvalue)) {
2311 while (c->device_allow)
2312 cgroup_context_free_device_allow(c, c->device_allow);
2317 n = strcspn(rvalue, WHITESPACE);
2318 path = strndup(rvalue, n);
2322 if (!path_startswith(path, "/dev")) {
2323 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2324 "Invalid device node path '%s'. Ignoring.", path);
2328 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2332 if (!in_charset(m, "rwm")) {
2333 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2334 "Invalid device rights '%s'. Ignoring.", m);
2338 a = new0(CGroupDeviceAllow, 1);
2344 a->r = !!strchr(m, 'r');
2345 a->w = !!strchr(m, 'w');
2346 a->m = !!strchr(m, 'm');
2348 LIST_PREPEND(device_allow, c->device_allow, a);
2352 int config_parse_blockio_weight(
2354 const char *filename,
2356 const char *section,
2357 unsigned section_line,
2364 CGroupContext *c = data;
2372 if (isempty(rvalue)) {
2373 c->blockio_weight = 1000;
2377 r = safe_atolu(rvalue, &lu);
2378 if (r < 0 || lu < 10 || lu > 1000) {
2379 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2380 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2384 c->blockio_weight = lu;
2389 int config_parse_blockio_device_weight(
2391 const char *filename,
2393 const char *section,
2394 unsigned section_line,
2401 _cleanup_free_ char *path = NULL;
2402 CGroupBlockIODeviceWeight *w;
2403 CGroupContext *c = data;
2413 if (isempty(rvalue)) {
2414 while (c->blockio_device_weights)
2415 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2420 n = strcspn(rvalue, WHITESPACE);
2421 weight = rvalue + n;
2423 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2424 "Expected block device and device weight. Ignoring.");
2428 path = strndup(rvalue, n);
2432 if (!path_startswith(path, "/dev")) {
2433 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2434 "Invalid device node path '%s'. Ignoring.", path);
2438 weight += strspn(weight, WHITESPACE);
2439 r = safe_atolu(weight, &lu);
2440 if (r < 0 || lu < 10 || lu > 1000) {
2441 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2442 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2447 w = new0(CGroupBlockIODeviceWeight, 1);
2456 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
2460 int config_parse_blockio_bandwidth(
2462 const char *filename,
2464 const char *section,
2465 unsigned section_line,
2472 _cleanup_free_ char *path = NULL;
2473 CGroupBlockIODeviceBandwidth *b;
2474 CGroupContext *c = data;
2475 const char *bandwidth;
2485 read = streq("BlockIOReadBandwidth", lvalue);
2487 if (isempty(rvalue)) {
2488 CGroupBlockIODeviceBandwidth *next;
2490 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2491 if (b->read == read)
2492 cgroup_context_free_blockio_device_bandwidth(c, b);
2497 n = strcspn(rvalue, WHITESPACE);
2498 bandwidth = rvalue + n;
2499 bandwidth += strspn(bandwidth, WHITESPACE);
2502 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2503 "Expected space separated pair of device node and bandwidth. Ignoring.");
2507 path = strndup(rvalue, n);
2511 if (!path_startswith(path, "/dev")) {
2512 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2513 "Invalid device node path '%s'. Ignoring.", path);
2517 r = parse_bytes(bandwidth, &bytes);
2518 if (r < 0 || bytes <= 0) {
2519 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2520 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2524 b = new0(CGroupBlockIODeviceBandwidth, 1);
2530 b->bandwidth = (uint64_t) bytes;
2533 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
2538 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2540 int config_parse_job_mode_isolate(
2542 const char *filename,
2544 const char *section,
2545 unsigned section_line,
2559 r = parse_boolean(rvalue);
2561 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue);
2565 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2569 int config_parse_personality(
2571 const char *filename,
2573 const char *section,
2574 unsigned section_line,
2581 unsigned long *personality = data, p;
2586 assert(personality);
2588 p = personality_from_string(rvalue);
2589 if (p == 0xffffffffUL) {
2590 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2591 "Failed to parse personality, ignoring: %s", rvalue);
2599 #define FOLLOW_MAX 8
2601 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2612 /* This will update the filename pointer if the loaded file is
2613 * reached by a symlink. The old string will be freed. */
2616 char *target, *name;
2618 if (c++ >= FOLLOW_MAX)
2621 path_kill_slashes(*filename);
2623 /* Add the file name we are currently looking at to
2624 * the names of this unit, but only if it is a valid
2626 name = basename(*filename);
2628 if (unit_name_is_valid(name, TEMPLATE_VALID)) {
2630 id = set_get(names, name);
2636 r = set_consume(names, id);
2642 /* Try to open the file name, but don't if its a symlink */
2643 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2650 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2651 r = readlink_and_make_absolute(*filename, &target);
2659 f = fdopen(fd, "re");
2662 close_nointr_nofail(fd);
2671 static int merge_by_names(Unit **u, Set *names, const char *id) {
2679 /* Let's try to add in all symlink names we found */
2680 while ((k = set_steal_first(names))) {
2682 /* First try to merge in the other name into our
2684 r = unit_merge_by_name(*u, k);
2688 /* Hmm, we couldn't merge the other unit into
2689 * ours? Then let's try it the other way
2692 other = manager_get_unit((*u)->manager, k);
2696 r = unit_merge(other, *u);
2699 return merge_by_names(u, names, NULL);
2707 unit_choose_id(*u, id);
2715 static int load_from_path(Unit *u, const char *path) {
2717 _cleanup_set_free_free_ Set *symlink_names = NULL;
2718 _cleanup_fclose_ FILE *f = NULL;
2719 _cleanup_free_ char *filename = NULL;
2727 symlink_names = set_new(string_hash_func, string_compare_func);
2731 if (path_is_absolute(path)) {
2733 filename = strdup(path);
2737 r = open_follow(&filename, &f, symlink_names, &id);
2749 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
2751 /* Instead of opening the path right away, we manually
2752 * follow all symlinks and add their name to our unit
2753 * name set while doing so */
2754 filename = path_make_absolute(path, *p);
2758 if (u->manager->unit_path_cache &&
2759 !set_get(u->manager->unit_path_cache, filename))
2762 r = open_follow(&filename, &f, symlink_names, &id);
2771 /* Empty the symlink names for the next run */
2772 set_clear_free(symlink_names);
2781 /* Hmm, no suitable file found? */
2785 r = merge_by_names(&merged, symlink_names, id);
2790 u->load_state = UNIT_MERGED;
2794 if (fstat(fileno(f), &st) < 0)
2797 if (null_or_empty(&st))
2798 u->load_state = UNIT_MASKED;
2800 u->load_state = UNIT_LOADED;
2802 /* Now, parse the file contents */
2803 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
2804 config_item_perf_lookup,
2805 (void*) load_fragment_gperf_lookup, false, true, u);
2810 free(u->fragment_path);
2811 u->fragment_path = filename;
2814 u->fragment_mtime = timespec_load(&st.st_mtim);
2816 if (u->source_path) {
2817 if (stat(u->source_path, &st) >= 0)
2818 u->source_mtime = timespec_load(&st.st_mtim);
2820 u->source_mtime = 0;
2826 int unit_load_fragment(Unit *u) {
2832 assert(u->load_state == UNIT_STUB);
2835 /* First, try to find the unit under its id. We always look
2836 * for unit files in the default directories, to make it easy
2837 * to override things by placing things in /etc/systemd/system */
2838 r = load_from_path(u, u->id);
2842 /* Try to find an alias we can load this with */
2843 if (u->load_state == UNIT_STUB)
2844 SET_FOREACH(t, u->names, i) {
2849 r = load_from_path(u, t);
2853 if (u->load_state != UNIT_STUB)
2857 /* And now, try looking for it under the suggested (originally linked) path */
2858 if (u->load_state == UNIT_STUB && u->fragment_path) {
2860 r = load_from_path(u, u->fragment_path);
2864 if (u->load_state == UNIT_STUB) {
2865 /* Hmm, this didn't work? Then let's get rid
2866 * of the fragment path stored for us, so that
2867 * we don't point to an invalid location. */
2868 free(u->fragment_path);
2869 u->fragment_path = NULL;
2873 /* Look for a template */
2874 if (u->load_state == UNIT_STUB && u->instance) {
2875 _cleanup_free_ char *k;
2877 k = unit_name_template(u->id);
2881 r = load_from_path(u, k);
2885 if (u->load_state == UNIT_STUB)
2886 SET_FOREACH(t, u->names, i) {
2887 _cleanup_free_ char *z = NULL;
2892 z = unit_name_template(t);
2896 r = load_from_path(u, z);
2900 if (u->load_state != UNIT_STUB)
2908 void unit_dump_config_items(FILE *f) {
2909 static const struct {
2910 const ConfigParserCallback callback;
2913 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK)
2914 { config_parse_warn_compat, "NOTSUPPORTED" },
2916 { config_parse_int, "INTEGER" },
2917 { config_parse_unsigned, "UNSIGNED" },
2918 { config_parse_bytes_size, "SIZE" },
2919 { config_parse_bool, "BOOLEAN" },
2920 { config_parse_string, "STRING" },
2921 { config_parse_path, "PATH" },
2922 { config_parse_unit_path_printf, "PATH" },
2923 { config_parse_strv, "STRING [...]" },
2924 { config_parse_exec_nice, "NICE" },
2925 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2926 { config_parse_exec_io_class, "IOCLASS" },
2927 { config_parse_exec_io_priority, "IOPRIORITY" },
2928 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2929 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2930 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2931 { config_parse_mode, "MODE" },
2932 { config_parse_unit_env_file, "FILE" },
2933 { config_parse_output, "OUTPUT" },
2934 { config_parse_input, "INPUT" },
2935 { config_parse_facility, "FACILITY" },
2936 { config_parse_level, "LEVEL" },
2937 { config_parse_exec_capabilities, "CAPABILITIES" },
2938 { config_parse_exec_secure_bits, "SECUREBITS" },
2939 { config_parse_bounding_set, "BOUNDINGSET" },
2940 { config_parse_limit, "LIMIT" },
2941 { config_parse_unit_deps, "UNIT [...]" },
2942 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2943 { config_parse_service_type, "SERVICETYPE" },
2944 { config_parse_service_restart, "SERVICERESTART" },
2945 #ifdef HAVE_SYSV_COMPAT
2946 { config_parse_sysv_priority, "SYSVPRIORITY" },
2948 { config_parse_kill_mode, "KILLMODE" },
2949 { config_parse_kill_signal, "SIGNAL" },
2950 { config_parse_socket_listen, "SOCKET [...]" },
2951 { config_parse_socket_bind, "SOCKETBIND" },
2952 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
2953 { config_parse_sec, "SECONDS" },
2954 { config_parse_nsec, "NANOSECONDS" },
2955 { config_parse_path_strv, "PATH [...]" },
2956 { config_parse_unit_requires_mounts_for, "PATH [...]" },
2957 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2958 { config_parse_unit_string_printf, "STRING" },
2959 { config_parse_trigger_unit, "UNIT" },
2960 { config_parse_timer, "TIMER" },
2961 { config_parse_path_spec, "PATH" },
2962 { config_parse_notify_access, "ACCESS" },
2963 { config_parse_ip_tos, "TOS" },
2964 { config_parse_unit_condition_path, "CONDITION" },
2965 { config_parse_unit_condition_string, "CONDITION" },
2966 { config_parse_unit_condition_null, "CONDITION" },
2967 { config_parse_unit_slice, "SLICE" },
2968 { config_parse_documentation, "URL" },
2969 { config_parse_service_timeout, "SECONDS" },
2970 { config_parse_start_limit_action, "ACTION" },
2971 { config_parse_set_status, "STATUS" },
2972 { config_parse_service_sockets, "SOCKETS" },
2973 { config_parse_environ, "ENVIRON" },
2975 { config_parse_syscall_filter, "SYSCALLS" },
2976 { config_parse_syscall_archs, "ARCHS" },
2977 { config_parse_syscall_errno, "ERRNO" },
2979 { config_parse_cpu_shares, "SHARES" },
2980 { config_parse_memory_limit, "LIMIT" },
2981 { config_parse_device_allow, "DEVICE" },
2982 { config_parse_device_policy, "POLICY" },
2983 { config_parse_blockio_bandwidth, "BANDWIDTH" },
2984 { config_parse_blockio_weight, "WEIGHT" },
2985 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
2986 { config_parse_long, "LONG" },
2987 { config_parse_socket_service, "SERVICE" },
2989 { config_parse_exec_selinux_context, "LABEL" },
2991 { config_parse_job_mode, "MODE" },
2992 { config_parse_job_mode_isolate, "BOOLEAN" },
2995 const char *prev = NULL;
3000 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
3001 const char *rvalue = "OTHER", *lvalue;
3005 const ConfigPerfItem *p;
3007 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
3009 dot = strchr(i, '.');
3010 lvalue = dot ? dot + 1 : i;
3014 if (!prev || !strneq(prev, i, prefix_len+1)) {
3018 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
3021 for (j = 0; j < ELEMENTSOF(table); j++)
3022 if (p->parse == table[j].callback) {
3023 rvalue = table[j].rvalue;
3027 fprintf(f, "%s=%s\n", lvalue, rvalue);