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"
60 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP)
61 int config_parse_warn_compat(
66 unsigned section_line,
73 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
74 "Support for option %s= has been disabled at compile time and is ignored",
80 int config_parse_unit_deps(const char* unit,
84 unsigned section_line,
91 UnitDependency d = ltype;
101 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
102 _cleanup_free_ char *t = NULL, *k = NULL;
109 r = unit_name_printf(u, t, &k);
111 log_syntax(unit, LOG_ERR, filename, line, -r,
112 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
116 r = unit_add_dependency_by_name(u, d, k, NULL, true);
118 log_syntax(unit, LOG_ERR, filename, line, -r,
119 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
125 int config_parse_unit_string_printf(const char *unit,
126 const char *filename,
129 unsigned section_line,
137 _cleanup_free_ char *k = NULL;
145 r = unit_full_printf(u, rvalue, &k);
147 log_syntax(unit, LOG_ERR, filename, line, -r,
148 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
150 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
151 k ? k : rvalue, data, userdata);
154 int config_parse_unit_strv_printf(const char *unit,
155 const char *filename,
158 unsigned section_line,
166 _cleanup_free_ char *k = NULL;
174 r = unit_full_printf(u, rvalue, &k);
176 log_syntax(unit, LOG_ERR, filename, line, -r,
177 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
179 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
180 k ? k : rvalue, data, userdata);
183 int config_parse_unit_path_printf(const char *unit,
184 const char *filename,
187 unsigned section_line,
195 _cleanup_free_ char *k = NULL;
203 r = unit_full_printf(u, rvalue, &k);
205 log_syntax(unit, LOG_ERR, filename, line, -r,
206 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
208 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype,
209 k ? k : rvalue, data, userdata);
212 int config_parse_socket_listen(const char *unit,
213 const char *filename,
216 unsigned section_line,
223 SocketPort *p, *tail;
234 if (isempty(rvalue)) {
235 /* An empty assignment removes all ports */
236 socket_free_ports(s);
240 p = new0(SocketPort, 1);
244 if (ltype != SOCKET_SOCKET) {
247 r = unit_full_printf(UNIT(s), rvalue, &p->path);
249 p->path = strdup(rvalue);
254 log_syntax(unit, LOG_ERR, filename, line, -r,
255 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
258 path_kill_slashes(p->path);
260 } else if (streq(lvalue, "ListenNetlink")) {
261 _cleanup_free_ char *k = NULL;
263 p->type = SOCKET_SOCKET;
264 r = unit_full_printf(UNIT(s), rvalue, &k);
266 log_syntax(unit, LOG_ERR, filename, line, -r,
267 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
269 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
271 log_syntax(unit, LOG_ERR, filename, line, -r,
272 "Failed to parse address value, ignoring: %s", rvalue);
278 _cleanup_free_ char *k = NULL;
280 p->type = SOCKET_SOCKET;
281 r = unit_full_printf(UNIT(s), rvalue, &k);
283 log_syntax(unit, LOG_ERR, filename, line, -r,
284 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
286 r = socket_address_parse(&p->address, k ? k : rvalue);
288 log_syntax(unit, LOG_ERR, filename, line, -r,
289 "Failed to parse address value, ignoring: %s", rvalue);
294 if (streq(lvalue, "ListenStream"))
295 p->address.type = SOCK_STREAM;
296 else if (streq(lvalue, "ListenDatagram"))
297 p->address.type = SOCK_DGRAM;
299 assert(streq(lvalue, "ListenSequentialPacket"));
300 p->address.type = SOCK_SEQPACKET;
303 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
304 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
305 "Address family not supported, ignoring: %s", rvalue);
315 LIST_FIND_TAIL(port, s->ports, tail);
316 LIST_INSERT_AFTER(port, s->ports, tail, p);
318 LIST_PREPEND(port, s->ports, p);
323 int config_parse_socket_bind(const char *unit,
324 const char *filename,
327 unsigned section_line,
335 SocketAddressBindIPv6Only b;
344 b = socket_address_bind_ipv6_only_from_string(rvalue);
348 r = parse_boolean(rvalue);
350 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
351 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
355 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
357 s->bind_ipv6_only = b;
362 int config_parse_exec_nice(const char *unit,
363 const char *filename,
366 unsigned section_line,
373 ExecContext *c = data;
381 r = safe_atoi(rvalue, &priority);
383 log_syntax(unit, LOG_ERR, filename, line, -r,
384 "Failed to parse nice priority, ignoring: %s. ", rvalue);
388 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
389 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
390 "Nice priority out of range, ignoring: %s", rvalue);
400 int config_parse_exec_oom_score_adjust(const char* unit,
401 const char *filename,
404 unsigned section_line,
411 ExecContext *c = data;
419 r = safe_atoi(rvalue, &oa);
421 log_syntax(unit, LOG_ERR, filename, line, -r,
422 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
426 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
427 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
428 "OOM score adjust value out of range, ignoring: %s", rvalue);
432 c->oom_score_adjust = oa;
433 c->oom_score_adjust_set = true;
438 int config_parse_exec(const char *unit,
439 const char *filename,
442 unsigned section_line,
449 ExecCommand **e = data, *nce;
461 if (isempty(rvalue)) {
462 /* An empty assignment resets the list */
463 exec_command_free_list(*e);
468 /* We accept an absolute path as first argument, or
469 * alternatively an absolute prefixed with @ to allow
470 * overriding of argv[0]. */
476 bool honour_argv0 = false, ignore = false;
482 rvalue += strspn(rvalue, WHITESPACE);
487 for (i = 0; i < 2; i++) {
488 if (rvalue[0] == '-' && !ignore) {
493 if (rvalue[0] == '@' && !honour_argv0) {
499 if (*rvalue != '/') {
500 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
501 "Executable path is not absolute, ignoring: %s", rvalue);
506 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
507 if (strneq(w, ";", MAX(l, 1U)))
513 n = new(char*, k + !honour_argv0);
518 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
519 if (strneq(w, ";", MAX(l, 1U)))
521 else if (strneq(w, "\\;", MAX(l, 1U)))
524 if (honour_argv0 && w == rvalue) {
527 path = strndup(w, l);
533 if (!utf8_is_valid(path)) {
534 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
535 "Path is not UTF-8 clean, ignoring assignment: %s",
544 c = n[k++] = cunescape_length(w, l);
550 if (!utf8_is_valid(c)) {
551 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
552 "Path is not UTF-8 clean, ignoring assignment: %s",
563 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
564 "Invalid command line, ignoring: %s", rvalue);
577 assert(path_is_absolute(path));
579 nce = new0(ExecCommand, 1);
587 nce->ignore = ignore;
589 path_kill_slashes(nce->path);
591 exec_command_append_list(e, nce);
607 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
608 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
610 int config_parse_socket_bindtodevice(const char* unit,
611 const char *filename,
614 unsigned section_line,
629 if (rvalue[0] && !streq(rvalue, "*")) {
636 free(s->bind_to_device);
637 s->bind_to_device = n;
642 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
643 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
645 int config_parse_exec_io_class(const char *unit,
646 const char *filename,
649 unsigned section_line,
656 ExecContext *c = data;
664 x = ioprio_class_from_string(rvalue);
666 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
667 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
671 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
672 c->ioprio_set = true;
677 int config_parse_exec_io_priority(const char *unit,
678 const char *filename,
681 unsigned section_line,
688 ExecContext *c = data;
696 r = safe_atoi(rvalue, &i);
697 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
698 log_syntax(unit, LOG_ERR, filename, line, -r,
699 "Failed to parse IO priority, ignoring: %s", rvalue);
703 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
704 c->ioprio_set = true;
709 int config_parse_exec_cpu_sched_policy(const char *unit,
710 const char *filename,
713 unsigned section_line,
721 ExecContext *c = data;
729 x = sched_policy_from_string(rvalue);
731 log_syntax(unit, LOG_ERR, filename, line, -x,
732 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
736 c->cpu_sched_policy = x;
737 /* Moving to or from real-time policy? We need to adjust the priority */
738 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
739 c->cpu_sched_set = true;
744 int config_parse_exec_cpu_sched_prio(const char *unit,
745 const char *filename,
748 unsigned section_line,
755 ExecContext *c = data;
763 r = safe_atoi(rvalue, &i);
765 log_syntax(unit, LOG_ERR, filename, line, -r,
766 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
770 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
771 min = sched_get_priority_min(c->cpu_sched_policy);
772 max = sched_get_priority_max(c->cpu_sched_policy);
774 if (i < min || i > max) {
775 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
776 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
780 c->cpu_sched_priority = i;
781 c->cpu_sched_set = true;
786 int config_parse_exec_cpu_affinity(const char *unit,
787 const char *filename,
790 unsigned section_line,
797 ExecContext *c = data;
807 if (isempty(rvalue)) {
808 /* An empty assignment resets the CPU list */
815 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
816 _cleanup_free_ char *t = NULL;
824 r = safe_atou(t, &cpu);
827 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
832 if (r < 0 || cpu >= c->cpuset_ncpus) {
833 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
834 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
838 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
844 int config_parse_exec_capabilities(const char *unit,
845 const char *filename,
848 unsigned section_line,
855 ExecContext *c = data;
863 cap = cap_from_text(rvalue);
865 log_syntax(unit, LOG_ERR, filename, line, errno,
866 "Failed to parse capabilities, ignoring: %s", rvalue);
871 cap_free(c->capabilities);
872 c->capabilities = cap;
877 int config_parse_exec_secure_bits(const char *unit,
878 const char *filename,
881 unsigned section_line,
888 ExecContext *c = data;
898 if (isempty(rvalue)) {
899 /* An empty assignment resets the field */
904 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
905 if (first_word(w, "keep-caps"))
906 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
907 else if (first_word(w, "keep-caps-locked"))
908 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
909 else if (first_word(w, "no-setuid-fixup"))
910 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
911 else if (first_word(w, "no-setuid-fixup-locked"))
912 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
913 else if (first_word(w, "noroot"))
914 c->secure_bits |= 1<<SECURE_NOROOT;
915 else if (first_word(w, "noroot-locked"))
916 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
918 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
919 "Failed to parse secure bits, ignoring: %s", rvalue);
927 int config_parse_bounding_set(const char *unit,
928 const char *filename,
931 unsigned section_line,
938 uint64_t *capability_bounding_set_drop = data;
950 if (rvalue[0] == '~') {
955 /* Note that we store this inverted internally, since the
956 * kernel wants it like this. But we actually expose it
957 * non-inverted everywhere to have a fully normalized
960 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
961 _cleanup_free_ char *t = NULL;
969 r = cap_from_name(t, &cap);
971 log_syntax(unit, LOG_ERR, filename, line, errno,
972 "Failed to parse capability in bounding set, ignoring: %s", t);
976 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
980 *capability_bounding_set_drop |= sum;
982 *capability_bounding_set_drop |= ~sum;
987 int config_parse_limit(const char *unit,
988 const char *filename,
991 unsigned section_line,
998 struct rlimit **rl = data;
999 unsigned long long u;
1008 if (streq(rvalue, "infinity"))
1009 u = (unsigned long long) RLIM_INFINITY;
1013 r = safe_atollu(rvalue, &u);
1015 log_syntax(unit, LOG_ERR, filename, line, -r,
1016 "Failed to parse resource value, ignoring: %s", rvalue);
1022 *rl = new(struct rlimit, 1);
1027 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
1031 #ifdef HAVE_SYSV_COMPAT
1032 int config_parse_sysv_priority(const char *unit,
1033 const char *filename,
1035 const char *section,
1036 unsigned section_line,
1043 int *priority = data;
1051 r = safe_atoi(rvalue, &i);
1052 if (r < 0 || i < 0) {
1053 log_syntax(unit, LOG_ERR, filename, line, -r,
1054 "Failed to parse SysV start priority, ignoring: %s", rvalue);
1058 *priority = (int) i;
1063 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1065 int config_parse_kill_signal(const char *unit,
1066 const char *filename,
1068 const char *section,
1069 unsigned section_line,
1084 r = signal_from_string_try_harder(rvalue);
1086 log_syntax(unit, LOG_ERR, filename, line, -r,
1087 "Failed to parse kill signal, ignoring: %s", rvalue);
1095 int config_parse_exec_mount_flags(const char *unit,
1096 const char *filename,
1098 const char *section,
1099 unsigned section_line,
1106 ExecContext *c = data;
1110 unsigned long flags = 0;
1117 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
1118 _cleanup_free_ char *t;
1124 if (streq(t, "shared"))
1126 else if (streq(t, "slave"))
1128 else if (streq(w, "private"))
1129 flags |= MS_PRIVATE;
1131 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1132 "Failed to parse mount flag %s, ignoring: %s",
1138 c->mount_flags = flags;
1142 int config_parse_timer(const char *unit,
1143 const char *filename,
1145 const char *section,
1146 unsigned section_line,
1157 CalendarSpec *c = NULL;
1165 if (isempty(rvalue)) {
1166 /* Empty assignment resets list */
1167 timer_free_values(t);
1171 b = timer_base_from_string(lvalue);
1173 log_syntax(unit, LOG_ERR, filename, line, -b,
1174 "Failed to parse timer base, ignoring: %s", lvalue);
1178 if (b == TIMER_CALENDAR) {
1179 if (calendar_spec_from_string(rvalue, &c) < 0) {
1180 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1181 "Failed to parse calendar specification, ignoring: %s",
1186 id = CLOCK_REALTIME;
1188 if (parse_sec(rvalue, &u) < 0) {
1189 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1190 "Failed to parse timer value, ignoring: %s",
1195 id = CLOCK_MONOTONIC;
1198 v = new0(TimerValue, 1);
1205 v->calendar_spec = c;
1207 LIST_PREPEND(value, t->values, v);
1212 int config_parse_trigger_unit(
1214 const char *filename,
1216 const char *section,
1217 unsigned section_line,
1224 _cleanup_free_ char *p = NULL;
1234 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1235 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1236 "Multiple units to trigger specified, ignoring: %s", rvalue);
1240 r = unit_name_printf(u, rvalue, &p);
1242 log_syntax(unit, LOG_ERR, filename, line, -r,
1243 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1245 type = unit_name_to_type(p ?: rvalue);
1247 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1248 "Unit type not valid, ignoring: %s", rvalue);
1252 if (type == u->type) {
1253 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1254 "Trigger cannot be of same type, ignoring: %s", rvalue);
1258 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
1260 log_syntax(unit, LOG_ERR, filename, line, -r,
1261 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
1268 int config_parse_path_spec(const char *unit,
1269 const char *filename,
1271 const char *section,
1272 unsigned section_line,
1282 _cleanup_free_ char *k = NULL;
1290 if (isempty(rvalue)) {
1291 /* Empty assignment clears list */
1296 b = path_type_from_string(lvalue);
1298 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1299 "Failed to parse path type, ignoring: %s", lvalue);
1303 r = unit_full_printf(UNIT(p), rvalue, &k);
1309 log_syntax(unit, LOG_ERR, filename, line, -r,
1310 "Failed to resolve unit specifiers on %s. Ignoring.",
1314 if (!path_is_absolute(k)) {
1315 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1316 "Path is not absolute, ignoring: %s", k);
1320 s = new0(PathSpec, 1);
1325 s->path = path_kill_slashes(k);
1330 LIST_PREPEND(spec, p->specs, s);
1335 int config_parse_socket_service(const char *unit,
1336 const char *filename,
1338 const char *section,
1339 unsigned section_line,
1346 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1350 _cleanup_free_ char *p = NULL;
1357 r = unit_name_printf(UNIT(s), rvalue, &p);
1359 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1363 if (!endswith(p, ".service")) {
1364 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1368 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1370 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1374 unit_ref_set(&s->service, x);
1379 int config_parse_service_sockets(const char *unit,
1380 const char *filename,
1382 const char *section,
1383 unsigned section_line,
1400 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1401 _cleanup_free_ char *t = NULL, *k = NULL;
1407 r = unit_name_printf(UNIT(s), t, &k);
1409 log_syntax(unit, LOG_ERR, filename, line, -r,
1410 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1412 if (!endswith(k ?: t, ".socket")) {
1413 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1414 "Unit must be of type socket, ignoring: %s", k ?: t);
1418 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
1420 log_syntax(unit, LOG_ERR, filename, line, -r,
1421 "Failed to add dependency on %s, ignoring: %s",
1422 k ?: t, strerror(-r));
1424 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
1432 int config_parse_service_timeout(const char *unit,
1433 const char *filename,
1435 const char *section,
1436 unsigned section_line,
1443 Service *s = userdata;
1451 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
1452 rvalue, data, userdata);
1456 if (streq(lvalue, "TimeoutSec")) {
1457 s->start_timeout_defined = true;
1458 s->timeout_stop_usec = s->timeout_start_usec;
1459 } else if (streq(lvalue, "TimeoutStartSec"))
1460 s->start_timeout_defined = true;
1465 int config_parse_busname_service(
1467 const char *filename,
1469 const char *section,
1470 unsigned section_line,
1477 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1481 _cleanup_free_ char *p = NULL;
1488 r = unit_name_printf(UNIT(n), rvalue, &p);
1490 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1494 if (!endswith(p, ".service")) {
1495 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1499 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1501 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1505 unit_ref_set(&n->service, x);
1510 int config_parse_unit_env_file(const char *unit,
1511 const char *filename,
1513 const char *section,
1514 unsigned section_line,
1523 _cleanup_free_ char *n = NULL;
1532 if (isempty(rvalue)) {
1533 /* Empty assignment frees the list */
1539 r = unit_full_printf(u, rvalue, &n);
1541 log_syntax(unit, LOG_ERR, filename, line, r,
1542 "Failed to resolve specifiers, ignoring: %s", rvalue);
1545 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1546 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1547 "Path '%s' is not absolute, ignoring.", s);
1551 r = strv_extend(env, s);
1558 int config_parse_environ(const char *unit,
1559 const char *filename,
1561 const char *section,
1562 unsigned section_line,
1570 char*** env = data, *w, *state;
1572 _cleanup_free_ char *k = NULL;
1580 if (isempty(rvalue)) {
1581 /* Empty assignment resets the list */
1588 r = unit_full_printf(u, rvalue, &k);
1590 log_syntax(unit, LOG_ERR, filename, line, -r,
1591 "Failed to resolve specifiers, ignoring: %s", rvalue);
1599 FOREACH_WORD_QUOTED(w, l, k, state) {
1600 _cleanup_free_ char *n;
1603 n = cunescape_length(w, l);
1607 if (!env_assignment_is_valid(n)) {
1608 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1609 "Invalid environment assignment, ignoring: %s", rvalue);
1613 x = strv_env_set(*env, n);
1624 int config_parse_ip_tos(const char *unit,
1625 const char *filename,
1627 const char *section,
1628 unsigned section_line,
1635 int *ip_tos = data, x;
1642 x = ip_tos_from_string(rvalue);
1644 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1645 "Failed to parse IP TOS value, ignoring: %s", rvalue);
1653 int config_parse_unit_condition_path(const char *unit,
1654 const char *filename,
1656 const char *section,
1657 unsigned section_line,
1664 ConditionType cond = ltype;
1666 bool trigger, negate;
1668 _cleanup_free_ char *p = NULL;
1676 if (isempty(rvalue)) {
1677 /* Empty assignment resets the list */
1678 condition_free_list(u->conditions);
1679 u->conditions = NULL;
1683 trigger = rvalue[0] == '|';
1687 negate = rvalue[0] == '!';
1691 r = unit_full_printf(u, rvalue, &p);
1693 log_syntax(unit, LOG_ERR, filename, line, -r,
1694 "Failed to resolve specifiers, ignoring: %s", rvalue);
1701 if (!path_is_absolute(p)) {
1702 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1703 "Path in condition not absolute, ignoring: %s", p);
1707 c = condition_new(cond, p, trigger, negate);
1711 LIST_PREPEND(conditions, u->conditions, c);
1715 int config_parse_unit_condition_string(const char *unit,
1716 const char *filename,
1718 const char *section,
1719 unsigned section_line,
1726 ConditionType cond = ltype;
1728 bool trigger, negate;
1730 _cleanup_free_ char *s = NULL;
1738 if (isempty(rvalue)) {
1739 /* Empty assignment resets the list */
1740 condition_free_list(u->conditions);
1741 u->conditions = NULL;
1745 trigger = rvalue[0] == '|';
1749 negate = rvalue[0] == '!';
1753 r = unit_full_printf(u, rvalue, &s);
1755 log_syntax(unit, LOG_ERR, filename, line, -r,
1756 "Failed to resolve specifiers, ignoring: %s", rvalue);
1763 c = condition_new(cond, s, trigger, negate);
1767 LIST_PREPEND(conditions, u->conditions, c);
1771 int config_parse_unit_condition_null(const char *unit,
1772 const char *filename,
1774 const char *section,
1775 unsigned section_line,
1784 bool trigger, negate;
1792 if (isempty(rvalue)) {
1793 /* Empty assignment resets the list */
1794 condition_free_list(u->conditions);
1795 u->conditions = NULL;
1799 trigger = rvalue[0] == '|';
1803 negate = rvalue[0] == '!';
1807 b = parse_boolean(rvalue);
1809 log_syntax(unit, LOG_ERR, filename, line, -b,
1810 "Failed to parse boolean value in condition, ignoring: %s",
1818 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1822 LIST_PREPEND(conditions, u->conditions, c);
1826 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1827 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
1829 int config_parse_unit_requires_mounts_for(
1831 const char *filename,
1833 const char *section,
1834 unsigned section_line,
1851 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1853 _cleanup_free_ char *n;
1859 if (!utf8_is_valid(n)) {
1860 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1861 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
1865 r = unit_require_mounts_for(u, n);
1867 log_syntax(unit, LOG_ERR, filename, line, r,
1868 "Failed to add required mount for, ignoring: %s", rvalue);
1876 int config_parse_documentation(const char *unit,
1877 const char *filename,
1879 const char *section,
1880 unsigned section_line,
1896 if (isempty(rvalue)) {
1897 /* Empty assignment resets the list */
1898 strv_free(u->documentation);
1899 u->documentation = NULL;
1903 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
1904 rvalue, data, userdata);
1908 for (a = b = u->documentation; a && *a; a++) {
1910 if (is_valid_documentation_url(*a))
1913 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1914 "Invalid URL, ignoring: %s", *a);
1925 int config_parse_syscall_filter(
1927 const char *filename,
1929 const char *section,
1930 unsigned section_line,
1937 static const char default_syscalls[] =
1944 ExecContext *c = data;
1946 bool invert = false;
1956 if (isempty(rvalue)) {
1957 /* Empty assignment resets the list */
1958 set_free(c->syscall_filter);
1959 c->syscall_filter = NULL;
1960 c->syscall_whitelist = false;
1964 if (rvalue[0] == '~') {
1969 if (!c->syscall_filter) {
1970 c->syscall_filter = set_new(trivial_hash_func, trivial_compare_func);
1971 if (!c->syscall_filter)
1975 /* Allow everything but the ones listed */
1976 c->syscall_whitelist = false;
1980 /* Allow nothing but the ones listed */
1981 c->syscall_whitelist = true;
1983 /* Accept default syscalls if we are on a whitelist */
1984 NULSTR_FOREACH(i, default_syscalls) {
1987 id = seccomp_syscall_resolve_name(i);
1991 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2000 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2001 _cleanup_free_ char *t = NULL;
2008 id = seccomp_syscall_resolve_name(t);
2010 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call, ignoring: %s", t);
2014 /* If we previously wanted to forbid a syscall and now
2015 * we want to allow it, then remove it from the list
2017 if (!invert == c->syscall_whitelist) {
2018 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2024 set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
2027 c->no_new_privileges = true;
2032 int config_parse_syscall_errno(
2034 const char *filename,
2036 const char *section,
2037 unsigned section_line,
2044 ExecContext *c = data;
2051 if (isempty(rvalue)) {
2052 /* Empty assignment resets to KILL */
2053 c->syscall_errno = 0;
2057 e = errno_from_name(rvalue);
2059 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse error number, ignoring: %s", rvalue);
2063 c->syscall_errno = e;
2068 int config_parse_unit_slice(
2070 const char *filename,
2072 const char *section,
2073 unsigned section_line,
2080 _cleanup_free_ char *k = NULL;
2081 Unit *u = userdata, *slice;
2089 r = unit_name_printf(u, rvalue, &k);
2091 log_syntax(unit, LOG_ERR, filename, line, -r,
2092 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2099 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
2101 log_syntax(unit, LOG_ERR, filename, line, -r,
2102 "Failed to load slice unit %s. Ignoring.", k);
2106 if (slice->type != UNIT_SLICE) {
2107 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2108 "Slice unit %s is not a slice. Ignoring.", k);
2112 unit_ref_set(&u->slice, slice);
2116 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2118 int config_parse_cpu_shares(
2120 const char *filename,
2122 const char *section,
2123 unsigned section_line,
2130 CGroupContext *c = data;
2138 if (isempty(rvalue)) {
2139 c->cpu_shares = 1024;
2143 r = safe_atolu(rvalue, &lu);
2144 if (r < 0 || lu <= 0) {
2145 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2146 "CPU shares '%s' invalid. Ignoring.", rvalue);
2154 int config_parse_memory_limit(
2156 const char *filename,
2158 const char *section,
2159 unsigned section_line,
2166 CGroupContext *c = data;
2170 if (isempty(rvalue)) {
2171 c->memory_limit = (uint64_t) -1;
2175 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2177 r = parse_bytes(rvalue, &bytes);
2179 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2180 "Memory limit '%s' invalid. Ignoring.", rvalue);
2184 c->memory_limit = (uint64_t) bytes;
2188 int config_parse_device_allow(
2190 const char *filename,
2192 const char *section,
2193 unsigned section_line,
2200 _cleanup_free_ char *path = NULL;
2201 CGroupContext *c = data;
2202 CGroupDeviceAllow *a;
2206 if (isempty(rvalue)) {
2207 while (c->device_allow)
2208 cgroup_context_free_device_allow(c, c->device_allow);
2213 n = strcspn(rvalue, WHITESPACE);
2214 path = strndup(rvalue, n);
2218 if (!path_startswith(path, "/dev")) {
2219 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2220 "Invalid device node path '%s'. Ignoring.", path);
2224 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2228 if (!in_charset(m, "rwm")) {
2229 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2230 "Invalid device rights '%s'. Ignoring.", m);
2234 a = new0(CGroupDeviceAllow, 1);
2240 a->r = !!strchr(m, 'r');
2241 a->w = !!strchr(m, 'w');
2242 a->m = !!strchr(m, 'm');
2244 LIST_PREPEND(device_allow, c->device_allow, a);
2248 int config_parse_blockio_weight(
2250 const char *filename,
2252 const char *section,
2253 unsigned section_line,
2260 CGroupContext *c = data;
2268 if (isempty(rvalue)) {
2269 c->blockio_weight = 1000;
2273 r = safe_atolu(rvalue, &lu);
2274 if (r < 0 || lu < 10 || lu > 1000) {
2275 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2276 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2280 c->blockio_weight = lu;
2285 int config_parse_blockio_device_weight(
2287 const char *filename,
2289 const char *section,
2290 unsigned section_line,
2297 _cleanup_free_ char *path = NULL;
2298 CGroupBlockIODeviceWeight *w;
2299 CGroupContext *c = data;
2309 if (isempty(rvalue)) {
2310 while (c->blockio_device_weights)
2311 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2316 n = strcspn(rvalue, WHITESPACE);
2317 weight = rvalue + n;
2319 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2320 "Expected block device and device weight. Ignoring.");
2324 path = strndup(rvalue, n);
2328 if (!path_startswith(path, "/dev")) {
2329 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2330 "Invalid device node path '%s'. Ignoring.", path);
2334 weight += strspn(weight, WHITESPACE);
2335 r = safe_atolu(weight, &lu);
2336 if (r < 0 || lu < 10 || lu > 1000) {
2337 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2338 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2343 w = new0(CGroupBlockIODeviceWeight, 1);
2352 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
2356 int config_parse_blockio_bandwidth(
2358 const char *filename,
2360 const char *section,
2361 unsigned section_line,
2368 _cleanup_free_ char *path = NULL;
2369 CGroupBlockIODeviceBandwidth *b;
2370 CGroupContext *c = data;
2371 const char *bandwidth;
2381 read = streq("BlockIOReadBandwidth", lvalue);
2383 if (isempty(rvalue)) {
2384 CGroupBlockIODeviceBandwidth *next;
2386 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2387 if (b->read == read)
2388 cgroup_context_free_blockio_device_bandwidth(c, b);
2393 n = strcspn(rvalue, WHITESPACE);
2394 bandwidth = rvalue + n;
2395 bandwidth += strspn(bandwidth, WHITESPACE);
2398 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2399 "Expected space separated pair of device node and bandwidth. Ignoring.");
2403 path = strndup(rvalue, n);
2407 if (!path_startswith(path, "/dev")) {
2408 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2409 "Invalid device node path '%s'. Ignoring.", path);
2413 r = parse_bytes(bandwidth, &bytes);
2414 if (r < 0 || bytes <= 0) {
2415 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2416 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2420 b = new0(CGroupBlockIODeviceBandwidth, 1);
2426 b->bandwidth = (uint64_t) bytes;
2429 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
2434 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2436 int config_parse_job_mode_isolate(
2438 const char *filename,
2440 const char *section,
2441 unsigned section_line,
2455 r = parse_boolean(rvalue);
2457 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue);
2461 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2465 #define FOLLOW_MAX 8
2467 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2478 /* This will update the filename pointer if the loaded file is
2479 * reached by a symlink. The old string will be freed. */
2482 char *target, *name;
2484 if (c++ >= FOLLOW_MAX)
2487 path_kill_slashes(*filename);
2489 /* Add the file name we are currently looking at to
2490 * the names of this unit, but only if it is a valid
2492 name = basename(*filename);
2494 if (unit_name_is_valid(name, TEMPLATE_VALID)) {
2496 id = set_get(names, name);
2502 r = set_consume(names, id);
2508 /* Try to open the file name, but don't if its a symlink */
2509 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2516 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2517 r = readlink_and_make_absolute(*filename, &target);
2525 f = fdopen(fd, "re");
2528 close_nointr_nofail(fd);
2537 static int merge_by_names(Unit **u, Set *names, const char *id) {
2545 /* Let's try to add in all symlink names we found */
2546 while ((k = set_steal_first(names))) {
2548 /* First try to merge in the other name into our
2550 r = unit_merge_by_name(*u, k);
2554 /* Hmm, we couldn't merge the other unit into
2555 * ours? Then let's try it the other way
2558 other = manager_get_unit((*u)->manager, k);
2562 r = unit_merge(other, *u);
2565 return merge_by_names(u, names, NULL);
2573 unit_choose_id(*u, id);
2581 static int load_from_path(Unit *u, const char *path) {
2583 _cleanup_set_free_free_ Set *symlink_names = NULL;
2584 _cleanup_fclose_ FILE *f = NULL;
2585 _cleanup_free_ char *filename = NULL;
2593 symlink_names = set_new(string_hash_func, string_compare_func);
2597 if (path_is_absolute(path)) {
2599 filename = strdup(path);
2603 r = open_follow(&filename, &f, symlink_names, &id);
2615 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
2617 /* Instead of opening the path right away, we manually
2618 * follow all symlinks and add their name to our unit
2619 * name set while doing so */
2620 filename = path_make_absolute(path, *p);
2624 if (u->manager->unit_path_cache &&
2625 !set_get(u->manager->unit_path_cache, filename))
2628 r = open_follow(&filename, &f, symlink_names, &id);
2637 /* Empty the symlink names for the next run */
2638 set_clear_free(symlink_names);
2647 /* Hmm, no suitable file found? */
2651 r = merge_by_names(&merged, symlink_names, id);
2656 u->load_state = UNIT_MERGED;
2660 if (fstat(fileno(f), &st) < 0)
2663 if (null_or_empty(&st))
2664 u->load_state = UNIT_MASKED;
2666 u->load_state = UNIT_LOADED;
2668 /* Now, parse the file contents */
2669 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
2670 config_item_perf_lookup,
2671 (void*) load_fragment_gperf_lookup, false, true, u);
2676 free(u->fragment_path);
2677 u->fragment_path = filename;
2680 u->fragment_mtime = timespec_load(&st.st_mtim);
2682 if (u->source_path) {
2683 if (stat(u->source_path, &st) >= 0)
2684 u->source_mtime = timespec_load(&st.st_mtim);
2686 u->source_mtime = 0;
2692 int unit_load_fragment(Unit *u) {
2698 assert(u->load_state == UNIT_STUB);
2701 /* First, try to find the unit under its id. We always look
2702 * for unit files in the default directories, to make it easy
2703 * to override things by placing things in /etc/systemd/system */
2704 r = load_from_path(u, u->id);
2708 /* Try to find an alias we can load this with */
2709 if (u->load_state == UNIT_STUB)
2710 SET_FOREACH(t, u->names, i) {
2715 r = load_from_path(u, t);
2719 if (u->load_state != UNIT_STUB)
2723 /* And now, try looking for it under the suggested (originally linked) path */
2724 if (u->load_state == UNIT_STUB && u->fragment_path) {
2726 r = load_from_path(u, u->fragment_path);
2730 if (u->load_state == UNIT_STUB) {
2731 /* Hmm, this didn't work? Then let's get rid
2732 * of the fragment path stored for us, so that
2733 * we don't point to an invalid location. */
2734 free(u->fragment_path);
2735 u->fragment_path = NULL;
2739 /* Look for a template */
2740 if (u->load_state == UNIT_STUB && u->instance) {
2741 _cleanup_free_ char *k;
2743 k = unit_name_template(u->id);
2747 r = load_from_path(u, k);
2751 if (u->load_state == UNIT_STUB)
2752 SET_FOREACH(t, u->names, i) {
2753 _cleanup_free_ char *z = NULL;
2758 z = unit_name_template(t);
2762 r = load_from_path(u, z);
2766 if (u->load_state != UNIT_STUB)
2774 void unit_dump_config_items(FILE *f) {
2775 static const struct {
2776 const ConfigParserCallback callback;
2779 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP)
2780 { config_parse_warn_compat, "NOTSUPPORTED" },
2782 { config_parse_int, "INTEGER" },
2783 { config_parse_unsigned, "UNSIGNED" },
2784 { config_parse_bytes_size, "SIZE" },
2785 { config_parse_bool, "BOOLEAN" },
2786 { config_parse_string, "STRING" },
2787 { config_parse_path, "PATH" },
2788 { config_parse_unit_path_printf, "PATH" },
2789 { config_parse_strv, "STRING [...]" },
2790 { config_parse_exec_nice, "NICE" },
2791 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2792 { config_parse_exec_io_class, "IOCLASS" },
2793 { config_parse_exec_io_priority, "IOPRIORITY" },
2794 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2795 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2796 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2797 { config_parse_mode, "MODE" },
2798 { config_parse_unit_env_file, "FILE" },
2799 { config_parse_output, "OUTPUT" },
2800 { config_parse_input, "INPUT" },
2801 { config_parse_facility, "FACILITY" },
2802 { config_parse_level, "LEVEL" },
2803 { config_parse_exec_capabilities, "CAPABILITIES" },
2804 { config_parse_exec_secure_bits, "SECUREBITS" },
2805 { config_parse_bounding_set, "BOUNDINGSET" },
2806 { config_parse_limit, "LIMIT" },
2807 { config_parse_unit_deps, "UNIT [...]" },
2808 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2809 { config_parse_service_type, "SERVICETYPE" },
2810 { config_parse_service_restart, "SERVICERESTART" },
2811 #ifdef HAVE_SYSV_COMPAT
2812 { config_parse_sysv_priority, "SYSVPRIORITY" },
2814 { config_parse_kill_mode, "KILLMODE" },
2815 { config_parse_kill_signal, "SIGNAL" },
2816 { config_parse_socket_listen, "SOCKET [...]" },
2817 { config_parse_socket_bind, "SOCKETBIND" },
2818 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
2819 { config_parse_sec, "SECONDS" },
2820 { config_parse_nsec, "NANOSECONDS" },
2821 { config_parse_path_strv, "PATH [...]" },
2822 { config_parse_unit_requires_mounts_for, "PATH [...]" },
2823 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2824 { config_parse_unit_string_printf, "STRING" },
2825 { config_parse_trigger_unit, "UNIT" },
2826 { config_parse_timer, "TIMER" },
2827 { config_parse_path_spec, "PATH" },
2828 { config_parse_notify_access, "ACCESS" },
2829 { config_parse_ip_tos, "TOS" },
2830 { config_parse_unit_condition_path, "CONDITION" },
2831 { config_parse_unit_condition_string, "CONDITION" },
2832 { config_parse_unit_condition_null, "CONDITION" },
2833 { config_parse_unit_slice, "SLICE" },
2834 { config_parse_documentation, "URL" },
2835 { config_parse_service_timeout, "SECONDS" },
2836 { config_parse_start_limit_action, "ACTION" },
2837 { config_parse_set_status, "STATUS" },
2838 { config_parse_service_sockets, "SOCKETS" },
2839 { config_parse_environ, "ENVIRON" },
2841 { config_parse_syscall_filter, "SYSCALLS" },
2842 { config_parse_syscall_errno, "ERRNO" },
2844 { config_parse_cpu_shares, "SHARES" },
2845 { config_parse_memory_limit, "LIMIT" },
2846 { config_parse_device_allow, "DEVICE" },
2847 { config_parse_device_policy, "POLICY" },
2848 { config_parse_blockio_bandwidth, "BANDWIDTH" },
2849 { config_parse_blockio_weight, "WEIGHT" },
2850 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
2851 { config_parse_long, "LONG" },
2852 { config_parse_socket_service, "SERVICE" },
2855 const char *prev = NULL;
2860 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2861 const char *rvalue = "OTHER", *lvalue;
2865 const ConfigPerfItem *p;
2867 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2869 dot = strchr(i, '.');
2870 lvalue = dot ? dot + 1 : i;
2874 if (!prev || !strneq(prev, i, prefix_len+1)) {
2878 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2881 for (j = 0; j < ELEMENTSOF(table); j++)
2882 if (p->parse == table[j].callback) {
2883 rvalue = table[j].rvalue;
2887 fprintf(f, "%s=%s\n", lvalue, rvalue);