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>
42 #include "sd-messages.h"
45 #include "conf-parser.h"
46 #include "load-fragment.h"
49 #include "securebits.h"
51 #include "unit-name.h"
52 #include "unit-printf.h"
54 #include "path-util.h"
58 #include "bus-error.h"
60 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP)
61 int config_parse_warn_compat(const char *unit,
65 unsigned section_line,
72 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
73 "Support for option %s= has been disabled at compile time and is ignored",
79 int config_parse_unit_deps(const char* unit,
83 unsigned section_line,
90 UnitDependency d = ltype;
100 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
101 _cleanup_free_ char *t = NULL, *k = NULL;
108 r = unit_name_printf(u, t, &k);
110 log_syntax(unit, LOG_ERR, filename, line, -r,
111 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
115 r = unit_add_dependency_by_name(u, d, k, NULL, true);
117 log_syntax(unit, LOG_ERR, filename, line, -r,
118 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
124 int config_parse_unit_string_printf(const char *unit,
125 const char *filename,
128 unsigned section_line,
136 _cleanup_free_ char *k = NULL;
144 r = unit_full_printf(u, rvalue, &k);
146 log_syntax(unit, LOG_ERR, filename, line, -r,
147 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
149 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
150 k ? k : rvalue, data, userdata);
153 int config_parse_unit_strv_printf(const char *unit,
154 const char *filename,
157 unsigned section_line,
165 _cleanup_free_ char *k = NULL;
173 r = unit_full_printf(u, rvalue, &k);
175 log_syntax(unit, LOG_ERR, filename, line, -r,
176 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
178 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
179 k ? k : rvalue, data, userdata);
182 int config_parse_unit_path_printf(const char *unit,
183 const char *filename,
186 unsigned section_line,
194 _cleanup_free_ char *k = NULL;
202 r = unit_full_printf(u, rvalue, &k);
204 log_syntax(unit, LOG_ERR, filename, line, -r,
205 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
207 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype,
208 k ? k : rvalue, data, userdata);
211 int config_parse_socket_listen(const char *unit,
212 const char *filename,
215 unsigned section_line,
222 SocketPort *p, *tail;
233 if (isempty(rvalue)) {
234 /* An empty assignment removes all ports */
235 socket_free_ports(s);
239 p = new0(SocketPort, 1);
243 if (ltype != SOCKET_SOCKET) {
246 r = unit_full_printf(UNIT(s), rvalue, &p->path);
248 p->path = strdup(rvalue);
253 log_syntax(unit, LOG_ERR, filename, line, -r,
254 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
257 path_kill_slashes(p->path);
259 } else if (streq(lvalue, "ListenNetlink")) {
260 _cleanup_free_ char *k = NULL;
262 p->type = SOCKET_SOCKET;
263 r = unit_full_printf(UNIT(s), rvalue, &k);
265 log_syntax(unit, LOG_ERR, filename, line, -r,
266 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
268 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
270 log_syntax(unit, LOG_ERR, filename, line, -r,
271 "Failed to parse address value, ignoring: %s", rvalue);
277 _cleanup_free_ char *k = NULL;
279 p->type = SOCKET_SOCKET;
280 r = unit_full_printf(UNIT(s), rvalue, &k);
282 log_syntax(unit, LOG_ERR, filename, line, -r,
283 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
285 r = socket_address_parse(&p->address, k ? k : rvalue);
287 log_syntax(unit, LOG_ERR, filename, line, -r,
288 "Failed to parse address value, ignoring: %s", rvalue);
293 if (streq(lvalue, "ListenStream"))
294 p->address.type = SOCK_STREAM;
295 else if (streq(lvalue, "ListenDatagram"))
296 p->address.type = SOCK_DGRAM;
298 assert(streq(lvalue, "ListenSequentialPacket"));
299 p->address.type = SOCK_SEQPACKET;
302 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
303 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
304 "Address family not supported, ignoring: %s", rvalue);
314 LIST_FIND_TAIL(port, s->ports, tail);
315 LIST_INSERT_AFTER(port, s->ports, tail, p);
317 LIST_PREPEND(port, s->ports, p);
322 int config_parse_socket_bind(const char *unit,
323 const char *filename,
326 unsigned section_line,
334 SocketAddressBindIPv6Only b;
343 b = socket_address_bind_ipv6_only_from_string(rvalue);
347 r = parse_boolean(rvalue);
349 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
350 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
354 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
356 s->bind_ipv6_only = b;
361 int config_parse_exec_nice(const char *unit,
362 const char *filename,
365 unsigned section_line,
372 ExecContext *c = data;
380 r = safe_atoi(rvalue, &priority);
382 log_syntax(unit, LOG_ERR, filename, line, -r,
383 "Failed to parse nice priority, ignoring: %s. ", rvalue);
387 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
388 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
389 "Nice priority out of range, ignoring: %s", rvalue);
399 int config_parse_exec_oom_score_adjust(const char* unit,
400 const char *filename,
403 unsigned section_line,
410 ExecContext *c = data;
418 r = safe_atoi(rvalue, &oa);
420 log_syntax(unit, LOG_ERR, filename, line, -r,
421 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
425 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
426 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
427 "OOM score adjust value out of range, ignoring: %s", rvalue);
431 c->oom_score_adjust = oa;
432 c->oom_score_adjust_set = true;
437 int config_parse_exec(const char *unit,
438 const char *filename,
441 unsigned section_line,
448 ExecCommand **e = data, *nce;
460 if (isempty(rvalue)) {
461 /* An empty assignment resets the list */
462 exec_command_free_list(*e);
467 /* We accept an absolute path as first argument, or
468 * alternatively an absolute prefixed with @ to allow
469 * overriding of argv[0]. */
475 bool honour_argv0 = false, ignore = false;
481 rvalue += strspn(rvalue, WHITESPACE);
486 for (i = 0; i < 2; i++) {
487 if (rvalue[0] == '-' && !ignore) {
492 if (rvalue[0] == '@' && !honour_argv0) {
498 if (*rvalue != '/') {
499 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
500 "Executable path is not absolute, ignoring: %s", rvalue);
505 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
506 if (strneq(w, ";", MAX(l, 1U)))
512 n = new(char*, k + !honour_argv0);
517 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
518 if (strneq(w, ";", MAX(l, 1U)))
520 else if (strneq(w, "\\;", MAX(l, 1U)))
523 if (honour_argv0 && w == rvalue) {
526 path = strndup(w, l);
532 if (!utf8_is_valid(path)) {
533 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
534 "Path is not UTF-8 clean, ignoring assignment: %s",
543 c = n[k++] = cunescape_length(w, l);
549 if (!utf8_is_valid(c)) {
550 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
551 "Path is not UTF-8 clean, ignoring assignment: %s",
562 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
563 "Invalid command line, ignoring: %s", rvalue);
576 assert(path_is_absolute(path));
578 nce = new0(ExecCommand, 1);
586 nce->ignore = ignore;
588 path_kill_slashes(nce->path);
590 exec_command_append_list(e, nce);
606 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
607 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
609 int config_parse_socket_bindtodevice(const char* unit,
610 const char *filename,
613 unsigned section_line,
628 if (rvalue[0] && !streq(rvalue, "*")) {
635 free(s->bind_to_device);
636 s->bind_to_device = n;
641 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
642 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
644 int config_parse_exec_io_class(const char *unit,
645 const char *filename,
648 unsigned section_line,
655 ExecContext *c = data;
663 x = ioprio_class_from_string(rvalue);
665 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
666 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
670 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
671 c->ioprio_set = true;
676 int config_parse_exec_io_priority(const char *unit,
677 const char *filename,
680 unsigned section_line,
687 ExecContext *c = data;
695 r = safe_atoi(rvalue, &i);
696 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
697 log_syntax(unit, LOG_ERR, filename, line, -r,
698 "Failed to parse IO priority, ignoring: %s", rvalue);
702 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
703 c->ioprio_set = true;
708 int config_parse_exec_cpu_sched_policy(const char *unit,
709 const char *filename,
712 unsigned section_line,
720 ExecContext *c = data;
728 x = sched_policy_from_string(rvalue);
730 log_syntax(unit, LOG_ERR, filename, line, -x,
731 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
735 c->cpu_sched_policy = x;
736 /* Moving to or from real-time policy? We need to adjust the priority */
737 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
738 c->cpu_sched_set = true;
743 int config_parse_exec_cpu_sched_prio(const char *unit,
744 const char *filename,
747 unsigned section_line,
754 ExecContext *c = data;
762 r = safe_atoi(rvalue, &i);
764 log_syntax(unit, LOG_ERR, filename, line, -r,
765 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
769 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
770 min = sched_get_priority_min(c->cpu_sched_policy);
771 max = sched_get_priority_max(c->cpu_sched_policy);
773 if (i < min || i > max) {
774 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
775 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
779 c->cpu_sched_priority = i;
780 c->cpu_sched_set = true;
785 int config_parse_exec_cpu_affinity(const char *unit,
786 const char *filename,
789 unsigned section_line,
796 ExecContext *c = data;
806 if (isempty(rvalue)) {
807 /* An empty assignment resets the CPU list */
814 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
815 _cleanup_free_ char *t = NULL;
823 r = safe_atou(t, &cpu);
826 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
831 if (r < 0 || cpu >= c->cpuset_ncpus) {
832 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
833 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
837 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
843 int config_parse_exec_capabilities(const char *unit,
844 const char *filename,
847 unsigned section_line,
854 ExecContext *c = data;
862 cap = cap_from_text(rvalue);
864 log_syntax(unit, LOG_ERR, filename, line, errno,
865 "Failed to parse capabilities, ignoring: %s", rvalue);
870 cap_free(c->capabilities);
871 c->capabilities = cap;
876 int config_parse_exec_secure_bits(const char *unit,
877 const char *filename,
880 unsigned section_line,
887 ExecContext *c = data;
897 if (isempty(rvalue)) {
898 /* An empty assignment resets the field */
903 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
904 if (first_word(w, "keep-caps"))
905 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
906 else if (first_word(w, "keep-caps-locked"))
907 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
908 else if (first_word(w, "no-setuid-fixup"))
909 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
910 else if (first_word(w, "no-setuid-fixup-locked"))
911 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
912 else if (first_word(w, "noroot"))
913 c->secure_bits |= 1<<SECURE_NOROOT;
914 else if (first_word(w, "noroot-locked"))
915 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
917 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
918 "Failed to parse secure bits, ignoring: %s", rvalue);
926 int config_parse_bounding_set(const char *unit,
927 const char *filename,
930 unsigned section_line,
937 uint64_t *capability_bounding_set_drop = data;
949 if (rvalue[0] == '~') {
954 /* Note that we store this inverted internally, since the
955 * kernel wants it like this. But we actually expose it
956 * non-inverted everywhere to have a fully normalized
959 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
960 _cleanup_free_ char *t = NULL;
968 r = cap_from_name(t, &cap);
970 log_syntax(unit, LOG_ERR, filename, line, errno,
971 "Failed to parse capability in bounding set, ignoring: %s", t);
975 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
979 *capability_bounding_set_drop |= sum;
981 *capability_bounding_set_drop |= ~sum;
986 int config_parse_limit(const char *unit,
987 const char *filename,
990 unsigned section_line,
997 struct rlimit **rl = data;
998 unsigned long long u;
1007 if (streq(rvalue, "infinity"))
1008 u = (unsigned long long) RLIM_INFINITY;
1012 r = safe_atollu(rvalue, &u);
1014 log_syntax(unit, LOG_ERR, filename, line, -r,
1015 "Failed to parse resource value, ignoring: %s", rvalue);
1021 *rl = new(struct rlimit, 1);
1026 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
1030 #ifdef HAVE_SYSV_COMPAT
1031 int config_parse_sysv_priority(const char *unit,
1032 const char *filename,
1034 const char *section,
1035 unsigned section_line,
1042 int *priority = data;
1050 r = safe_atoi(rvalue, &i);
1051 if (r < 0 || i < 0) {
1052 log_syntax(unit, LOG_ERR, filename, line, -r,
1053 "Failed to parse SysV start priority, ignoring: %s", rvalue);
1057 *priority = (int) i;
1062 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1064 int config_parse_kill_signal(const char *unit,
1065 const char *filename,
1067 const char *section,
1068 unsigned section_line,
1083 r = signal_from_string_try_harder(rvalue);
1085 log_syntax(unit, LOG_ERR, filename, line, -r,
1086 "Failed to parse kill signal, ignoring: %s", rvalue);
1094 int config_parse_exec_mount_flags(const char *unit,
1095 const char *filename,
1097 const char *section,
1098 unsigned section_line,
1105 ExecContext *c = data;
1109 unsigned long flags = 0;
1116 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
1117 _cleanup_free_ char *t;
1123 if (streq(t, "shared"))
1125 else if (streq(t, "slave"))
1127 else if (streq(w, "private"))
1128 flags |= MS_PRIVATE;
1130 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1131 "Failed to parse mount flag %s, ignoring: %s",
1137 c->mount_flags = flags;
1141 int config_parse_timer(const char *unit,
1142 const char *filename,
1144 const char *section,
1145 unsigned section_line,
1156 CalendarSpec *c = NULL;
1164 if (isempty(rvalue)) {
1165 /* Empty assignment resets list */
1166 timer_free_values(t);
1170 b = timer_base_from_string(lvalue);
1172 log_syntax(unit, LOG_ERR, filename, line, -b,
1173 "Failed to parse timer base, ignoring: %s", lvalue);
1177 if (b == TIMER_CALENDAR) {
1178 if (calendar_spec_from_string(rvalue, &c) < 0) {
1179 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1180 "Failed to parse calendar specification, ignoring: %s",
1185 id = CLOCK_REALTIME;
1187 if (parse_sec(rvalue, &u) < 0) {
1188 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1189 "Failed to parse timer value, ignoring: %s",
1194 id = CLOCK_MONOTONIC;
1197 v = new0(TimerValue, 1);
1204 v->calendar_spec = c;
1206 LIST_PREPEND(value, t->values, v);
1211 int config_parse_trigger_unit(
1213 const char *filename,
1215 const char *section,
1216 unsigned section_line,
1223 _cleanup_free_ char *p = NULL;
1233 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1234 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1235 "Multiple units to trigger specified, ignoring: %s", rvalue);
1239 r = unit_name_printf(u, rvalue, &p);
1241 log_syntax(unit, LOG_ERR, filename, line, -r,
1242 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1244 type = unit_name_to_type(p ?: rvalue);
1246 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1247 "Unit type not valid, ignoring: %s", rvalue);
1251 if (type == u->type) {
1252 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1253 "Trigger cannot be of same type, ignoring: %s", rvalue);
1257 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
1259 log_syntax(unit, LOG_ERR, filename, line, -r,
1260 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
1267 int config_parse_path_spec(const char *unit,
1268 const char *filename,
1270 const char *section,
1271 unsigned section_line,
1281 _cleanup_free_ char *k = NULL;
1289 if (isempty(rvalue)) {
1290 /* Empty assignment clears list */
1295 b = path_type_from_string(lvalue);
1297 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1298 "Failed to parse path type, ignoring: %s", lvalue);
1302 r = unit_full_printf(UNIT(p), rvalue, &k);
1308 log_syntax(unit, LOG_ERR, filename, line, -r,
1309 "Failed to resolve unit specifiers on %s. Ignoring.",
1313 if (!path_is_absolute(k)) {
1314 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1315 "Path is not absolute, ignoring: %s", k);
1319 s = new0(PathSpec, 1);
1324 s->path = path_kill_slashes(k);
1329 LIST_PREPEND(spec, p->specs, s);
1334 int config_parse_socket_service(const char *unit,
1335 const char *filename,
1337 const char *section,
1338 unsigned section_line,
1345 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1349 _cleanup_free_ char *p = NULL;
1356 r = unit_name_printf(UNIT(s), rvalue, &p);
1358 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1362 if (!endswith(p, ".service")) {
1363 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1367 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1369 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1373 unit_ref_set(&s->service, x);
1378 int config_parse_service_sockets(const char *unit,
1379 const char *filename,
1381 const char *section,
1382 unsigned section_line,
1399 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1400 _cleanup_free_ char *t = NULL, *k = NULL;
1406 r = unit_name_printf(UNIT(s), t, &k);
1408 log_syntax(unit, LOG_ERR, filename, line, -r,
1409 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1411 if (!endswith(k ?: t, ".socket")) {
1412 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1413 "Unit must be of type socket, ignoring: %s", k ?: t);
1417 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
1419 log_syntax(unit, LOG_ERR, filename, line, -r,
1420 "Failed to add dependency on %s, ignoring: %s",
1421 k ?: t, strerror(-r));
1423 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
1431 int config_parse_service_timeout(const char *unit,
1432 const char *filename,
1434 const char *section,
1435 unsigned section_line,
1442 Service *s = userdata;
1450 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
1451 rvalue, data, userdata);
1455 if (streq(lvalue, "TimeoutSec")) {
1456 s->start_timeout_defined = true;
1457 s->timeout_stop_usec = s->timeout_start_usec;
1458 } else if (streq(lvalue, "TimeoutStartSec"))
1459 s->start_timeout_defined = true;
1464 int config_parse_busname_service(
1466 const char *filename,
1468 const char *section,
1469 unsigned section_line,
1476 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1480 _cleanup_free_ char *p = NULL;
1487 r = unit_name_printf(UNIT(n), rvalue, &p);
1489 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1493 if (!endswith(p, ".service")) {
1494 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1498 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1500 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1504 unit_ref_set(&n->service, x);
1509 int config_parse_unit_env_file(const char *unit,
1510 const char *filename,
1512 const char *section,
1513 unsigned section_line,
1522 _cleanup_free_ char *n = NULL;
1531 if (isempty(rvalue)) {
1532 /* Empty assignment frees the list */
1538 r = unit_full_printf(u, rvalue, &n);
1540 log_syntax(unit, LOG_ERR, filename, line, r,
1541 "Failed to resolve specifiers, ignoring: %s", rvalue);
1544 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1545 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1546 "Path '%s' is not absolute, ignoring.", s);
1550 r = strv_extend(env, s);
1557 int config_parse_environ(const char *unit,
1558 const char *filename,
1560 const char *section,
1561 unsigned section_line,
1569 char*** env = data, *w, *state;
1571 _cleanup_free_ char *k = NULL;
1579 if (isempty(rvalue)) {
1580 /* Empty assignment resets the list */
1587 r = unit_full_printf(u, rvalue, &k);
1589 log_syntax(unit, LOG_ERR, filename, line, -r,
1590 "Failed to resolve specifiers, ignoring: %s", rvalue);
1598 FOREACH_WORD_QUOTED(w, l, k, state) {
1599 _cleanup_free_ char *n;
1602 n = cunescape_length(w, l);
1606 if (!env_assignment_is_valid(n)) {
1607 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1608 "Invalid environment assignment, ignoring: %s", rvalue);
1612 x = strv_env_set(*env, n);
1623 int config_parse_ip_tos(const char *unit,
1624 const char *filename,
1626 const char *section,
1627 unsigned section_line,
1634 int *ip_tos = data, x;
1641 x = ip_tos_from_string(rvalue);
1643 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1644 "Failed to parse IP TOS value, ignoring: %s", rvalue);
1652 int config_parse_unit_condition_path(const char *unit,
1653 const char *filename,
1655 const char *section,
1656 unsigned section_line,
1663 ConditionType cond = ltype;
1665 bool trigger, negate;
1667 _cleanup_free_ char *p = NULL;
1675 if (isempty(rvalue)) {
1676 /* Empty assignment resets the list */
1677 condition_free_list(u->conditions);
1678 u->conditions = NULL;
1682 trigger = rvalue[0] == '|';
1686 negate = rvalue[0] == '!';
1690 r = unit_full_printf(u, rvalue, &p);
1692 log_syntax(unit, LOG_ERR, filename, line, -r,
1693 "Failed to resolve specifiers, ignoring: %s", rvalue);
1700 if (!path_is_absolute(p)) {
1701 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1702 "Path in condition not absolute, ignoring: %s", p);
1706 c = condition_new(cond, p, trigger, negate);
1710 LIST_PREPEND(conditions, u->conditions, c);
1714 int config_parse_unit_condition_string(const char *unit,
1715 const char *filename,
1717 const char *section,
1718 unsigned section_line,
1725 ConditionType cond = ltype;
1727 bool trigger, negate;
1729 _cleanup_free_ char *s = NULL;
1737 if (isempty(rvalue)) {
1738 /* Empty assignment resets the list */
1739 condition_free_list(u->conditions);
1740 u->conditions = NULL;
1744 trigger = rvalue[0] == '|';
1748 negate = rvalue[0] == '!';
1752 r = unit_full_printf(u, rvalue, &s);
1754 log_syntax(unit, LOG_ERR, filename, line, -r,
1755 "Failed to resolve specifiers, ignoring: %s", rvalue);
1762 c = condition_new(cond, s, trigger, negate);
1766 LIST_PREPEND(conditions, u->conditions, c);
1770 int config_parse_unit_condition_null(const char *unit,
1771 const char *filename,
1773 const char *section,
1774 unsigned section_line,
1783 bool trigger, negate;
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 b = parse_boolean(rvalue);
1808 log_syntax(unit, LOG_ERR, filename, line, -b,
1809 "Failed to parse boolean value in condition, ignoring: %s",
1817 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1821 LIST_PREPEND(conditions, u->conditions, c);
1825 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1826 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
1828 int config_parse_unit_requires_mounts_for(
1830 const char *filename,
1832 const char *section,
1833 unsigned section_line,
1850 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1852 _cleanup_free_ char *n;
1858 if (!utf8_is_valid(n)) {
1859 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1860 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
1864 r = unit_require_mounts_for(u, n);
1866 log_syntax(unit, LOG_ERR, filename, line, r,
1867 "Failed to add required mount for, ignoring: %s", rvalue);
1875 int config_parse_documentation(const char *unit,
1876 const char *filename,
1878 const char *section,
1879 unsigned section_line,
1895 if (isempty(rvalue)) {
1896 /* Empty assignment resets the list */
1897 strv_free(u->documentation);
1898 u->documentation = NULL;
1902 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
1903 rvalue, data, userdata);
1907 for (a = b = u->documentation; a && *a; a++) {
1909 if (is_valid_documentation_url(*a))
1912 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1913 "Invalid URL, ignoring: %s", *a);
1924 int config_parse_syscall_filter(const char *unit,
1925 const char *filename,
1927 const char *section,
1928 unsigned section_line,
1934 ExecContext *c = data;
1936 bool invert = false;
1940 _cleanup_strv_free_ char **syscalls = strv_new(NULL, NULL);
1941 _cleanup_free_ char *sorted_syscalls = NULL;
1942 uint32_t action = SCMP_ACT_ALLOW;
1945 static char const *default_syscalls[] = {"execve",
1957 if (isempty(rvalue)) {
1958 /* Empty assignment resets the list */
1959 set_free(c->filtered_syscalls);
1960 c->filtered_syscalls= NULL;
1961 free(c->syscall_filter_string);
1962 c->syscall_filter_string = NULL;
1966 if (rvalue[0] == '~') {
1968 action = SCMP_ACT_KILL;
1972 if (!c->filtered_syscalls) {
1973 c->filtered_syscalls = set_new(trivial_hash_func, trivial_compare_func);
1975 c->syscall_filter_default_action = SCMP_ACT_ALLOW;
1977 char const **syscall;
1979 c->syscall_filter_default_action = SCMP_ACT_KILL;
1981 /* accept default syscalls if we are on a whitelist */
1982 STRV_FOREACH(syscall, default_syscalls) {
1983 int id = seccomp_syscall_resolve_name(*syscall);
1987 set_replace(c->filtered_syscalls, INT_TO_PTR(id + 1));
1992 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1994 _cleanup_free_ char *t = NULL;
2000 id = seccomp_syscall_resolve_name(t);
2002 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2003 "Failed to parse syscall, ignoring: %s", t);
2007 /* If we previously wanted to forbid a syscall
2008 * and now we want to allow it, then remove it from the list
2009 * libseccomp will also return -EPERM if we try to add
2010 * a rule with the same action as the default
2012 if (action == c->syscall_filter_default_action)
2013 set_remove(c->filtered_syscalls, INT_TO_PTR(id + 1));
2015 set_replace(c->filtered_syscalls, INT_TO_PTR(id + 1));
2018 SET_FOREACH(e, c->filtered_syscalls, i) {
2019 char *name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(e) - 1);
2020 strv_push(&syscalls, name);
2023 sorted_syscalls = strv_join(strv_sort(syscalls), " ");
2025 c->syscall_filter_string = strv_join(STRV_MAKE("~", sorted_syscalls, NULL), "");
2027 c->syscall_filter_string = strdup(sorted_syscalls);
2028 c->no_new_privileges = true;
2034 int config_parse_unit_slice(
2036 const char *filename,
2038 const char *section,
2039 unsigned section_line,
2046 _cleanup_free_ char *k = NULL;
2047 Unit *u = userdata, *slice;
2055 r = unit_name_printf(u, rvalue, &k);
2057 log_syntax(unit, LOG_ERR, filename, line, -r,
2058 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2065 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
2067 log_syntax(unit, LOG_ERR, filename, line, -r,
2068 "Failed to load slice unit %s. Ignoring.", k);
2072 if (slice->type != UNIT_SLICE) {
2073 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2074 "Slice unit %s is not a slice. Ignoring.", k);
2078 unit_ref_set(&u->slice, slice);
2082 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2084 int config_parse_cpu_shares(
2086 const char *filename,
2088 const char *section,
2089 unsigned section_line,
2096 CGroupContext *c = data;
2104 if (isempty(rvalue)) {
2105 c->cpu_shares = 1024;
2109 r = safe_atolu(rvalue, &lu);
2110 if (r < 0 || lu <= 0) {
2111 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2112 "CPU shares '%s' invalid. Ignoring.", rvalue);
2120 int config_parse_memory_limit(
2122 const char *filename,
2124 const char *section,
2125 unsigned section_line,
2132 CGroupContext *c = data;
2136 if (isempty(rvalue)) {
2137 c->memory_limit = (uint64_t) -1;
2141 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2143 r = parse_bytes(rvalue, &bytes);
2145 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2146 "Memory limit '%s' invalid. Ignoring.", rvalue);
2150 c->memory_limit = (uint64_t) bytes;
2154 int config_parse_device_allow(
2156 const char *filename,
2158 const char *section,
2159 unsigned section_line,
2166 _cleanup_free_ char *path = NULL;
2167 CGroupContext *c = data;
2168 CGroupDeviceAllow *a;
2172 if (isempty(rvalue)) {
2173 while (c->device_allow)
2174 cgroup_context_free_device_allow(c, c->device_allow);
2179 n = strcspn(rvalue, WHITESPACE);
2180 path = strndup(rvalue, n);
2184 if (!path_startswith(path, "/dev")) {
2185 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2186 "Invalid device node path '%s'. Ignoring.", path);
2190 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2194 if (!in_charset(m, "rwm")) {
2195 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2196 "Invalid device rights '%s'. Ignoring.", m);
2200 a = new0(CGroupDeviceAllow, 1);
2206 a->r = !!strchr(m, 'r');
2207 a->w = !!strchr(m, 'w');
2208 a->m = !!strchr(m, 'm');
2210 LIST_PREPEND(device_allow, c->device_allow, a);
2214 int config_parse_blockio_weight(
2216 const char *filename,
2218 const char *section,
2219 unsigned section_line,
2226 CGroupContext *c = data;
2234 if (isempty(rvalue)) {
2235 c->blockio_weight = 1000;
2239 r = safe_atolu(rvalue, &lu);
2240 if (r < 0 || lu < 10 || lu > 1000) {
2241 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2242 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2246 c->blockio_weight = lu;
2251 int config_parse_blockio_device_weight(
2253 const char *filename,
2255 const char *section,
2256 unsigned section_line,
2263 _cleanup_free_ char *path = NULL;
2264 CGroupBlockIODeviceWeight *w;
2265 CGroupContext *c = data;
2275 if (isempty(rvalue)) {
2276 while (c->blockio_device_weights)
2277 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2282 n = strcspn(rvalue, WHITESPACE);
2283 weight = rvalue + n;
2285 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2286 "Expected block device and device weight. Ignoring.");
2290 path = strndup(rvalue, n);
2294 if (!path_startswith(path, "/dev")) {
2295 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2296 "Invalid device node path '%s'. Ignoring.", path);
2300 weight += strspn(weight, WHITESPACE);
2301 r = safe_atolu(weight, &lu);
2302 if (r < 0 || lu < 10 || lu > 1000) {
2303 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2304 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2309 w = new0(CGroupBlockIODeviceWeight, 1);
2318 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
2322 int config_parse_blockio_bandwidth(
2324 const char *filename,
2326 const char *section,
2327 unsigned section_line,
2334 _cleanup_free_ char *path = NULL;
2335 CGroupBlockIODeviceBandwidth *b;
2336 CGroupContext *c = data;
2337 const char *bandwidth;
2347 read = streq("BlockIOReadBandwidth", lvalue);
2349 if (isempty(rvalue)) {
2350 CGroupBlockIODeviceBandwidth *next;
2352 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2353 if (b->read == read)
2354 cgroup_context_free_blockio_device_bandwidth(c, b);
2359 n = strcspn(rvalue, WHITESPACE);
2360 bandwidth = rvalue + n;
2361 bandwidth += strspn(bandwidth, WHITESPACE);
2364 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2365 "Expected space separated pair of device node and bandwidth. Ignoring.");
2369 path = strndup(rvalue, n);
2373 if (!path_startswith(path, "/dev")) {
2374 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2375 "Invalid device node path '%s'. Ignoring.", path);
2379 r = parse_bytes(bandwidth, &bytes);
2380 if (r < 0 || bytes <= 0) {
2381 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2382 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2386 b = new0(CGroupBlockIODeviceBandwidth, 1);
2392 b->bandwidth = (uint64_t) bytes;
2395 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
2400 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2402 int config_parse_job_mode_isolate(
2404 const char *filename,
2406 const char *section,
2407 unsigned section_line,
2421 r = parse_boolean(rvalue);
2423 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue);
2427 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2431 #define FOLLOW_MAX 8
2433 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2444 /* This will update the filename pointer if the loaded file is
2445 * reached by a symlink. The old string will be freed. */
2448 char *target, *name;
2450 if (c++ >= FOLLOW_MAX)
2453 path_kill_slashes(*filename);
2455 /* Add the file name we are currently looking at to
2456 * the names of this unit, but only if it is a valid
2458 name = basename(*filename);
2460 if (unit_name_is_valid(name, TEMPLATE_VALID)) {
2462 id = set_get(names, name);
2468 r = set_consume(names, id);
2474 /* Try to open the file name, but don't if its a symlink */
2475 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2482 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2483 r = readlink_and_make_absolute(*filename, &target);
2491 f = fdopen(fd, "re");
2494 close_nointr_nofail(fd);
2503 static int merge_by_names(Unit **u, Set *names, const char *id) {
2511 /* Let's try to add in all symlink names we found */
2512 while ((k = set_steal_first(names))) {
2514 /* First try to merge in the other name into our
2516 r = unit_merge_by_name(*u, k);
2520 /* Hmm, we couldn't merge the other unit into
2521 * ours? Then let's try it the other way
2524 other = manager_get_unit((*u)->manager, k);
2528 r = unit_merge(other, *u);
2531 return merge_by_names(u, names, NULL);
2539 unit_choose_id(*u, id);
2547 static int load_from_path(Unit *u, const char *path) {
2549 _cleanup_set_free_free_ Set *symlink_names = NULL;
2550 _cleanup_fclose_ FILE *f = NULL;
2551 _cleanup_free_ char *filename = NULL;
2559 symlink_names = set_new(string_hash_func, string_compare_func);
2563 if (path_is_absolute(path)) {
2565 filename = strdup(path);
2569 r = open_follow(&filename, &f, symlink_names, &id);
2581 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
2583 /* Instead of opening the path right away, we manually
2584 * follow all symlinks and add their name to our unit
2585 * name set while doing so */
2586 filename = path_make_absolute(path, *p);
2590 if (u->manager->unit_path_cache &&
2591 !set_get(u->manager->unit_path_cache, filename))
2594 r = open_follow(&filename, &f, symlink_names, &id);
2603 /* Empty the symlink names for the next run */
2604 set_clear_free(symlink_names);
2613 /* Hmm, no suitable file found? */
2617 r = merge_by_names(&merged, symlink_names, id);
2622 u->load_state = UNIT_MERGED;
2626 if (fstat(fileno(f), &st) < 0)
2629 if (null_or_empty(&st))
2630 u->load_state = UNIT_MASKED;
2632 u->load_state = UNIT_LOADED;
2634 /* Now, parse the file contents */
2635 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
2636 config_item_perf_lookup,
2637 (void*) load_fragment_gperf_lookup, false, true, u);
2642 free(u->fragment_path);
2643 u->fragment_path = filename;
2646 u->fragment_mtime = timespec_load(&st.st_mtim);
2648 if (u->source_path) {
2649 if (stat(u->source_path, &st) >= 0)
2650 u->source_mtime = timespec_load(&st.st_mtim);
2652 u->source_mtime = 0;
2658 int unit_load_fragment(Unit *u) {
2664 assert(u->load_state == UNIT_STUB);
2667 /* First, try to find the unit under its id. We always look
2668 * for unit files in the default directories, to make it easy
2669 * to override things by placing things in /etc/systemd/system */
2670 r = load_from_path(u, u->id);
2674 /* Try to find an alias we can load this with */
2675 if (u->load_state == UNIT_STUB)
2676 SET_FOREACH(t, u->names, i) {
2681 r = load_from_path(u, t);
2685 if (u->load_state != UNIT_STUB)
2689 /* And now, try looking for it under the suggested (originally linked) path */
2690 if (u->load_state == UNIT_STUB && u->fragment_path) {
2692 r = load_from_path(u, u->fragment_path);
2696 if (u->load_state == UNIT_STUB) {
2697 /* Hmm, this didn't work? Then let's get rid
2698 * of the fragment path stored for us, so that
2699 * we don't point to an invalid location. */
2700 free(u->fragment_path);
2701 u->fragment_path = NULL;
2705 /* Look for a template */
2706 if (u->load_state == UNIT_STUB && u->instance) {
2707 _cleanup_free_ char *k;
2709 k = unit_name_template(u->id);
2713 r = load_from_path(u, k);
2717 if (u->load_state == UNIT_STUB)
2718 SET_FOREACH(t, u->names, i) {
2719 _cleanup_free_ char *z = NULL;
2724 z = unit_name_template(t);
2728 r = load_from_path(u, z);
2732 if (u->load_state != UNIT_STUB)
2740 void unit_dump_config_items(FILE *f) {
2741 static const struct {
2742 const ConfigParserCallback callback;
2745 { config_parse_int, "INTEGER" },
2746 { config_parse_unsigned, "UNSIGNED" },
2747 { config_parse_bytes_size, "SIZE" },
2748 { config_parse_bool, "BOOLEAN" },
2749 { config_parse_string, "STRING" },
2750 { config_parse_path, "PATH" },
2751 { config_parse_unit_path_printf, "PATH" },
2752 { config_parse_strv, "STRING [...]" },
2753 { config_parse_exec_nice, "NICE" },
2754 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2755 { config_parse_exec_io_class, "IOCLASS" },
2756 { config_parse_exec_io_priority, "IOPRIORITY" },
2757 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2758 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2759 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2760 { config_parse_mode, "MODE" },
2761 { config_parse_unit_env_file, "FILE" },
2762 { config_parse_output, "OUTPUT" },
2763 { config_parse_input, "INPUT" },
2764 { config_parse_facility, "FACILITY" },
2765 { config_parse_level, "LEVEL" },
2766 { config_parse_exec_capabilities, "CAPABILITIES" },
2767 { config_parse_exec_secure_bits, "SECUREBITS" },
2768 { config_parse_bounding_set, "BOUNDINGSET" },
2769 { config_parse_limit, "LIMIT" },
2770 { config_parse_unit_deps, "UNIT [...]" },
2771 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2772 { config_parse_service_type, "SERVICETYPE" },
2773 { config_parse_service_restart, "SERVICERESTART" },
2774 #ifdef HAVE_SYSV_COMPAT
2775 { config_parse_sysv_priority, "SYSVPRIORITY" },
2777 { config_parse_warn_compat, "NOTSUPPORTED" },
2779 { config_parse_kill_mode, "KILLMODE" },
2780 { config_parse_kill_signal, "SIGNAL" },
2781 { config_parse_socket_listen, "SOCKET [...]" },
2782 { config_parse_socket_bind, "SOCKETBIND" },
2783 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
2784 { config_parse_sec, "SECONDS" },
2785 { config_parse_nsec, "NANOSECONDS" },
2786 { config_parse_path_strv, "PATH [...]" },
2787 { config_parse_unit_requires_mounts_for, "PATH [...]" },
2788 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2789 { config_parse_unit_string_printf, "STRING" },
2790 { config_parse_trigger_unit, "UNIT" },
2791 { config_parse_timer, "TIMER" },
2792 { config_parse_path_spec, "PATH" },
2793 { config_parse_notify_access, "ACCESS" },
2794 { config_parse_ip_tos, "TOS" },
2795 { config_parse_unit_condition_path, "CONDITION" },
2796 { config_parse_unit_condition_string, "CONDITION" },
2797 { config_parse_unit_condition_null, "CONDITION" },
2798 { config_parse_unit_slice, "SLICE" },
2799 { config_parse_documentation, "URL" },
2800 { config_parse_service_timeout, "SECONDS" },
2801 { config_parse_start_limit_action, "ACTION" },
2802 { config_parse_set_status, "STATUS" },
2803 { config_parse_service_sockets, "SOCKETS" },
2804 { config_parse_environ, "ENVIRON" },
2806 { config_parse_syscall_filter, "SYSCALL" },
2808 { config_parse_warn_compat, "NOTSUPPORTED" },
2810 { config_parse_cpu_shares, "SHARES" },
2811 { config_parse_memory_limit, "LIMIT" },
2812 { config_parse_device_allow, "DEVICE" },
2813 { config_parse_device_policy, "POLICY" },
2814 { config_parse_blockio_bandwidth, "BANDWIDTH" },
2815 { config_parse_blockio_weight, "WEIGHT" },
2816 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
2817 { config_parse_long, "LONG" },
2818 { config_parse_socket_service, "SERVICE" },
2821 const char *prev = NULL;
2826 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2827 const char *rvalue = "OTHER", *lvalue;
2831 const ConfigPerfItem *p;
2833 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2835 dot = strchr(i, '.');
2836 lvalue = dot ? dot + 1 : i;
2840 if (!prev || !strneq(prev, i, prefix_len+1)) {
2844 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2847 for (j = 0; j < ELEMENTSOF(table); j++)
2848 if (p->parse == table[j].callback) {
2849 rvalue = table[j].rvalue;
2853 fprintf(f, "%s=%s\n", lvalue, rvalue);