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>
37 #include "sd-messages.h"
40 #include "conf-parser.h"
41 #include "load-fragment.h"
44 #include "securebits.h"
46 #include "unit-name.h"
47 #include "unit-printf.h"
49 #include "path-util.h"
50 #include "syscall-list.h"
54 #include "bus-error.h"
56 #ifndef HAVE_SYSV_COMPAT
57 int config_parse_warn_compat(const char *unit,
61 unsigned section_line,
68 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
69 "Support for option %s= has been disabled at compile time and is ignored",
75 int config_parse_unit_deps(const char* unit,
79 unsigned section_line,
86 UnitDependency d = ltype;
96 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
97 _cleanup_free_ char *t = NULL, *k = NULL;
104 r = unit_name_printf(u, t, &k);
106 log_syntax(unit, LOG_ERR, filename, line, -r,
107 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
111 r = unit_add_dependency_by_name(u, d, k, NULL, true);
113 log_syntax(unit, LOG_ERR, filename, line, -r,
114 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
120 int config_parse_unit_string_printf(const char *unit,
121 const char *filename,
124 unsigned section_line,
132 _cleanup_free_ char *k = NULL;
140 r = unit_full_printf(u, rvalue, &k);
142 log_syntax(unit, LOG_ERR, filename, line, -r,
143 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
145 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
146 k ? k : rvalue, data, userdata);
149 int config_parse_unit_strv_printf(const char *unit,
150 const char *filename,
153 unsigned section_line,
161 _cleanup_free_ char *k = NULL;
169 r = unit_full_printf(u, rvalue, &k);
171 log_syntax(unit, LOG_ERR, filename, line, -r,
172 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
174 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
175 k ? k : rvalue, data, userdata);
178 int config_parse_unit_path_printf(const char *unit,
179 const char *filename,
182 unsigned section_line,
190 _cleanup_free_ char *k = NULL;
198 r = unit_full_printf(u, rvalue, &k);
200 log_syntax(unit, LOG_ERR, filename, line, -r,
201 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
203 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype,
204 k ? k : rvalue, data, userdata);
207 int config_parse_socket_listen(const char *unit,
208 const char *filename,
211 unsigned section_line,
218 SocketPort *p, *tail;
229 if (isempty(rvalue)) {
230 /* An empty assignment removes all ports */
231 socket_free_ports(s);
235 p = new0(SocketPort, 1);
239 if (ltype != SOCKET_SOCKET) {
242 r = unit_full_printf(UNIT(s), rvalue, &p->path);
244 p->path = strdup(rvalue);
249 log_syntax(unit, LOG_ERR, filename, line, -r,
250 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
253 path_kill_slashes(p->path);
255 } else if (streq(lvalue, "ListenNetlink")) {
256 _cleanup_free_ char *k = NULL;
258 p->type = SOCKET_SOCKET;
259 r = unit_full_printf(UNIT(s), rvalue, &k);
261 log_syntax(unit, LOG_ERR, filename, line, -r,
262 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
264 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
266 log_syntax(unit, LOG_ERR, filename, line, -r,
267 "Failed to parse address value, ignoring: %s", rvalue);
273 _cleanup_free_ char *k = NULL;
275 p->type = SOCKET_SOCKET;
276 r = unit_full_printf(UNIT(s), rvalue, &k);
278 log_syntax(unit, LOG_ERR, filename, line, -r,
279 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
281 r = socket_address_parse(&p->address, k ? k : rvalue);
283 log_syntax(unit, LOG_ERR, filename, line, -r,
284 "Failed to parse address value, ignoring: %s", rvalue);
289 if (streq(lvalue, "ListenStream"))
290 p->address.type = SOCK_STREAM;
291 else if (streq(lvalue, "ListenDatagram"))
292 p->address.type = SOCK_DGRAM;
294 assert(streq(lvalue, "ListenSequentialPacket"));
295 p->address.type = SOCK_SEQPACKET;
298 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
299 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
300 "Address family not supported, ignoring: %s", rvalue);
310 LIST_FIND_TAIL(port, s->ports, tail);
311 LIST_INSERT_AFTER(port, s->ports, tail, p);
313 LIST_PREPEND(port, s->ports, p);
318 int config_parse_socket_bind(const char *unit,
319 const char *filename,
322 unsigned section_line,
330 SocketAddressBindIPv6Only b;
339 b = socket_address_bind_ipv6_only_from_string(rvalue);
343 r = parse_boolean(rvalue);
345 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
346 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
350 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
352 s->bind_ipv6_only = b;
357 int config_parse_exec_nice(const char *unit,
358 const char *filename,
361 unsigned section_line,
368 ExecContext *c = data;
376 r = safe_atoi(rvalue, &priority);
378 log_syntax(unit, LOG_ERR, filename, line, -r,
379 "Failed to parse nice priority, ignoring: %s. ", rvalue);
383 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
384 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
385 "Nice priority out of range, ignoring: %s", rvalue);
395 int config_parse_exec_oom_score_adjust(const char* unit,
396 const char *filename,
399 unsigned section_line,
406 ExecContext *c = data;
414 r = safe_atoi(rvalue, &oa);
416 log_syntax(unit, LOG_ERR, filename, line, -r,
417 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
421 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
422 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
423 "OOM score adjust value out of range, ignoring: %s", rvalue);
427 c->oom_score_adjust = oa;
428 c->oom_score_adjust_set = true;
433 int config_parse_exec(const char *unit,
434 const char *filename,
437 unsigned section_line,
444 ExecCommand **e = data, *nce;
456 if (isempty(rvalue)) {
457 /* An empty assignment resets the list */
458 exec_command_free_list(*e);
463 /* We accept an absolute path as first argument, or
464 * alternatively an absolute prefixed with @ to allow
465 * overriding of argv[0]. */
471 bool honour_argv0 = false, ignore = false;
477 rvalue += strspn(rvalue, WHITESPACE);
482 for (i = 0; i < 2; i++) {
483 if (rvalue[0] == '-' && !ignore) {
488 if (rvalue[0] == '@' && !honour_argv0) {
494 if (*rvalue != '/') {
495 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
496 "Executable path is not absolute, ignoring: %s", rvalue);
501 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
502 if (strneq(w, ";", MAX(l, 1U)))
508 n = new(char*, k + !honour_argv0);
513 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
514 if (strneq(w, ";", MAX(l, 1U)))
516 else if (strneq(w, "\\;", MAX(l, 1U)))
519 if (honour_argv0 && w == rvalue) {
522 path = strndup(w, l);
528 if (!utf8_is_valid(path)) {
529 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
530 "Path is not UTF-8 clean, ignoring assignment: %s",
539 c = n[k++] = cunescape_length(w, l);
545 if (!utf8_is_valid(c)) {
546 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
547 "Path is not UTF-8 clean, ignoring assignment: %s",
558 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
559 "Invalid command line, ignoring: %s", rvalue);
572 assert(path_is_absolute(path));
574 nce = new0(ExecCommand, 1);
582 nce->ignore = ignore;
584 path_kill_slashes(nce->path);
586 exec_command_append_list(e, nce);
602 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
603 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
605 int config_parse_socket_bindtodevice(const char* unit,
606 const char *filename,
609 unsigned section_line,
624 if (rvalue[0] && !streq(rvalue, "*")) {
631 free(s->bind_to_device);
632 s->bind_to_device = n;
637 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
638 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
640 int config_parse_exec_io_class(const char *unit,
641 const char *filename,
644 unsigned section_line,
651 ExecContext *c = data;
659 x = ioprio_class_from_string(rvalue);
661 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
662 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
666 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
667 c->ioprio_set = true;
672 int config_parse_exec_io_priority(const char *unit,
673 const char *filename,
676 unsigned section_line,
683 ExecContext *c = data;
691 r = safe_atoi(rvalue, &i);
692 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
693 log_syntax(unit, LOG_ERR, filename, line, -r,
694 "Failed to parse IO priority, ignoring: %s", rvalue);
698 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
699 c->ioprio_set = true;
704 int config_parse_exec_cpu_sched_policy(const char *unit,
705 const char *filename,
708 unsigned section_line,
716 ExecContext *c = data;
724 x = sched_policy_from_string(rvalue);
726 log_syntax(unit, LOG_ERR, filename, line, -x,
727 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
731 c->cpu_sched_policy = x;
732 /* Moving to or from real-time policy? We need to adjust the priority */
733 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
734 c->cpu_sched_set = true;
739 int config_parse_exec_cpu_sched_prio(const char *unit,
740 const char *filename,
743 unsigned section_line,
750 ExecContext *c = data;
758 r = safe_atoi(rvalue, &i);
760 log_syntax(unit, LOG_ERR, filename, line, -r,
761 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
765 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
766 min = sched_get_priority_min(c->cpu_sched_policy);
767 max = sched_get_priority_max(c->cpu_sched_policy);
769 if (i < min || i > max) {
770 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
771 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
775 c->cpu_sched_priority = i;
776 c->cpu_sched_set = true;
781 int config_parse_exec_cpu_affinity(const char *unit,
782 const char *filename,
785 unsigned section_line,
792 ExecContext *c = data;
802 if (isempty(rvalue)) {
803 /* An empty assignment resets the CPU list */
810 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
811 _cleanup_free_ char *t = NULL;
819 r = safe_atou(t, &cpu);
822 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
827 if (r < 0 || cpu >= c->cpuset_ncpus) {
828 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
829 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
833 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
839 int config_parse_exec_capabilities(const char *unit,
840 const char *filename,
843 unsigned section_line,
850 ExecContext *c = data;
858 cap = cap_from_text(rvalue);
860 log_syntax(unit, LOG_ERR, filename, line, errno,
861 "Failed to parse capabilities, ignoring: %s", rvalue);
866 cap_free(c->capabilities);
867 c->capabilities = cap;
872 int config_parse_exec_secure_bits(const char *unit,
873 const char *filename,
876 unsigned section_line,
883 ExecContext *c = data;
893 if (isempty(rvalue)) {
894 /* An empty assignment resets the field */
899 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
900 if (first_word(w, "keep-caps"))
901 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
902 else if (first_word(w, "keep-caps-locked"))
903 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
904 else if (first_word(w, "no-setuid-fixup"))
905 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
906 else if (first_word(w, "no-setuid-fixup-locked"))
907 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
908 else if (first_word(w, "noroot"))
909 c->secure_bits |= 1<<SECURE_NOROOT;
910 else if (first_word(w, "noroot-locked"))
911 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
913 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
914 "Failed to parse secure bits, ignoring: %s", rvalue);
922 int config_parse_bounding_set(const char *unit,
923 const char *filename,
926 unsigned section_line,
933 uint64_t *capability_bounding_set_drop = data;
945 if (rvalue[0] == '~') {
950 /* Note that we store this inverted internally, since the
951 * kernel wants it like this. But we actually expose it
952 * non-inverted everywhere to have a fully normalized
955 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
956 _cleanup_free_ char *t = NULL;
964 r = cap_from_name(t, &cap);
966 log_syntax(unit, LOG_ERR, filename, line, errno,
967 "Failed to parse capability in bounding set, ignoring: %s", t);
971 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
975 *capability_bounding_set_drop |= sum;
977 *capability_bounding_set_drop |= ~sum;
982 int config_parse_limit(const char *unit,
983 const char *filename,
986 unsigned section_line,
993 struct rlimit **rl = data;
994 unsigned long long u;
1003 if (streq(rvalue, "infinity"))
1004 u = (unsigned long long) RLIM_INFINITY;
1008 r = safe_atollu(rvalue, &u);
1010 log_syntax(unit, LOG_ERR, filename, line, -r,
1011 "Failed to parse resource value, ignoring: %s", rvalue);
1017 *rl = new(struct rlimit, 1);
1022 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
1026 #ifdef HAVE_SYSV_COMPAT
1027 int config_parse_sysv_priority(const char *unit,
1028 const char *filename,
1030 const char *section,
1031 unsigned section_line,
1038 int *priority = data;
1046 r = safe_atoi(rvalue, &i);
1047 if (r < 0 || i < 0) {
1048 log_syntax(unit, LOG_ERR, filename, line, -r,
1049 "Failed to parse SysV start priority, ignoring: %s", rvalue);
1053 *priority = (int) i;
1058 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1060 int config_parse_kill_signal(const char *unit,
1061 const char *filename,
1063 const char *section,
1064 unsigned section_line,
1079 r = signal_from_string_try_harder(rvalue);
1081 log_syntax(unit, LOG_ERR, filename, line, -r,
1082 "Failed to parse kill signal, ignoring: %s", rvalue);
1090 int config_parse_exec_mount_flags(const char *unit,
1091 const char *filename,
1093 const char *section,
1094 unsigned section_line,
1101 ExecContext *c = data;
1105 unsigned long flags = 0;
1112 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
1113 _cleanup_free_ char *t;
1119 if (streq(t, "shared"))
1121 else if (streq(t, "slave"))
1123 else if (streq(w, "private"))
1124 flags |= MS_PRIVATE;
1126 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1127 "Failed to parse mount flag %s, ignoring: %s",
1133 c->mount_flags = flags;
1137 int config_parse_timer(const char *unit,
1138 const char *filename,
1140 const char *section,
1141 unsigned section_line,
1152 CalendarSpec *c = NULL;
1160 if (isempty(rvalue)) {
1161 /* Empty assignment resets list */
1162 timer_free_values(t);
1166 b = timer_base_from_string(lvalue);
1168 log_syntax(unit, LOG_ERR, filename, line, -b,
1169 "Failed to parse timer base, ignoring: %s", lvalue);
1173 if (b == TIMER_CALENDAR) {
1174 if (calendar_spec_from_string(rvalue, &c) < 0) {
1175 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1176 "Failed to parse calendar specification, ignoring: %s",
1181 id = CLOCK_REALTIME;
1183 if (parse_sec(rvalue, &u) < 0) {
1184 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1185 "Failed to parse timer value, ignoring: %s",
1190 id = CLOCK_MONOTONIC;
1193 v = new0(TimerValue, 1);
1200 v->calendar_spec = c;
1202 LIST_PREPEND(value, t->values, v);
1207 int config_parse_trigger_unit(
1209 const char *filename,
1211 const char *section,
1212 unsigned section_line,
1219 _cleanup_free_ char *p = NULL;
1229 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1230 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1231 "Multiple units to trigger specified, ignoring: %s", rvalue);
1235 r = unit_name_printf(u, rvalue, &p);
1237 log_syntax(unit, LOG_ERR, filename, line, -r,
1238 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1240 type = unit_name_to_type(p ?: rvalue);
1242 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1243 "Unit type not valid, ignoring: %s", rvalue);
1247 if (type == u->type) {
1248 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1249 "Trigger cannot be of same type, ignoring: %s", rvalue);
1253 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
1255 log_syntax(unit, LOG_ERR, filename, line, -r,
1256 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
1263 int config_parse_path_spec(const char *unit,
1264 const char *filename,
1266 const char *section,
1267 unsigned section_line,
1277 _cleanup_free_ char *k = NULL;
1285 if (isempty(rvalue)) {
1286 /* Empty assignment clears list */
1291 b = path_type_from_string(lvalue);
1293 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1294 "Failed to parse path type, ignoring: %s", lvalue);
1298 r = unit_full_printf(UNIT(p), rvalue, &k);
1304 log_syntax(unit, LOG_ERR, filename, line, -r,
1305 "Failed to resolve unit specifiers on %s. Ignoring.",
1309 if (!path_is_absolute(k)) {
1310 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1311 "Path is not absolute, ignoring: %s", k);
1315 s = new0(PathSpec, 1);
1320 s->path = path_kill_slashes(k);
1325 LIST_PREPEND(spec, p->specs, s);
1330 int config_parse_socket_service(const char *unit,
1331 const char *filename,
1333 const char *section,
1334 unsigned section_line,
1341 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1345 _cleanup_free_ char *p = NULL;
1352 r = unit_name_printf(UNIT(s), rvalue, &p);
1354 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1358 if (!endswith(p, ".service")) {
1359 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1363 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1365 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1369 unit_ref_set(&s->service, x);
1374 int config_parse_service_sockets(const char *unit,
1375 const char *filename,
1377 const char *section,
1378 unsigned section_line,
1395 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1396 _cleanup_free_ char *t = NULL, *k = NULL;
1402 r = unit_name_printf(UNIT(s), t, &k);
1404 log_syntax(unit, LOG_ERR, filename, line, -r,
1405 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1407 if (!endswith(k ?: t, ".socket")) {
1408 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1409 "Unit must be of type socket, ignoring: %s", k ?: t);
1413 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
1415 log_syntax(unit, LOG_ERR, filename, line, -r,
1416 "Failed to add dependency on %s, ignoring: %s",
1417 k ?: t, strerror(-r));
1419 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
1427 int config_parse_service_timeout(const char *unit,
1428 const char *filename,
1430 const char *section,
1431 unsigned section_line,
1438 Service *s = userdata;
1446 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
1447 rvalue, data, userdata);
1451 if (streq(lvalue, "TimeoutSec")) {
1452 s->start_timeout_defined = true;
1453 s->timeout_stop_usec = s->timeout_start_usec;
1454 } else if (streq(lvalue, "TimeoutStartSec"))
1455 s->start_timeout_defined = true;
1460 int config_parse_busname_service(
1462 const char *filename,
1464 const char *section,
1465 unsigned section_line,
1472 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1476 _cleanup_free_ char *p = NULL;
1483 r = unit_name_printf(UNIT(n), rvalue, &p);
1485 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1489 if (!endswith(p, ".service")) {
1490 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1494 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1496 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1500 unit_ref_set(&n->service, x);
1505 int config_parse_unit_env_file(const char *unit,
1506 const char *filename,
1508 const char *section,
1509 unsigned section_line,
1518 _cleanup_free_ char *n = NULL;
1527 if (isempty(rvalue)) {
1528 /* Empty assignment frees the list */
1534 r = unit_full_printf(u, rvalue, &n);
1536 log_syntax(unit, LOG_ERR, filename, line, r,
1537 "Failed to resolve specifiers, ignoring: %s", rvalue);
1540 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1541 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1542 "Path '%s' is not absolute, ignoring.", s);
1546 r = strv_extend(env, s);
1553 int config_parse_environ(const char *unit,
1554 const char *filename,
1556 const char *section,
1557 unsigned section_line,
1565 char*** env = data, *w, *state;
1567 _cleanup_free_ char *k = NULL;
1575 if (isempty(rvalue)) {
1576 /* Empty assignment resets the list */
1583 r = unit_full_printf(u, rvalue, &k);
1585 log_syntax(unit, LOG_ERR, filename, line, -r,
1586 "Failed to resolve specifiers, ignoring: %s", rvalue);
1594 FOREACH_WORD_QUOTED(w, l, k, state) {
1595 _cleanup_free_ char *n;
1598 n = cunescape_length(w, l);
1602 if (!env_assignment_is_valid(n)) {
1603 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1604 "Invalid environment assignment, ignoring: %s", rvalue);
1608 x = strv_env_set(*env, n);
1619 int config_parse_ip_tos(const char *unit,
1620 const char *filename,
1622 const char *section,
1623 unsigned section_line,
1630 int *ip_tos = data, x;
1637 x = ip_tos_from_string(rvalue);
1639 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1640 "Failed to parse IP TOS value, ignoring: %s", rvalue);
1648 int config_parse_unit_condition_path(const char *unit,
1649 const char *filename,
1651 const char *section,
1652 unsigned section_line,
1659 ConditionType cond = ltype;
1661 bool trigger, negate;
1663 _cleanup_free_ char *p = NULL;
1671 if (isempty(rvalue)) {
1672 /* Empty assignment resets the list */
1673 condition_free_list(u->conditions);
1674 u->conditions = NULL;
1678 trigger = rvalue[0] == '|';
1682 negate = rvalue[0] == '!';
1686 r = unit_full_printf(u, rvalue, &p);
1688 log_syntax(unit, LOG_ERR, filename, line, -r,
1689 "Failed to resolve specifiers, ignoring: %s", rvalue);
1696 if (!path_is_absolute(p)) {
1697 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1698 "Path in condition not absolute, ignoring: %s", p);
1702 c = condition_new(cond, p, trigger, negate);
1706 LIST_PREPEND(conditions, u->conditions, c);
1710 int config_parse_unit_condition_string(const char *unit,
1711 const char *filename,
1713 const char *section,
1714 unsigned section_line,
1721 ConditionType cond = ltype;
1723 bool trigger, negate;
1725 _cleanup_free_ char *s = NULL;
1733 if (isempty(rvalue)) {
1734 /* Empty assignment resets the list */
1735 condition_free_list(u->conditions);
1736 u->conditions = NULL;
1740 trigger = rvalue[0] == '|';
1744 negate = rvalue[0] == '!';
1748 r = unit_full_printf(u, rvalue, &s);
1750 log_syntax(unit, LOG_ERR, filename, line, -r,
1751 "Failed to resolve specifiers, ignoring: %s", rvalue);
1758 c = condition_new(cond, s, trigger, negate);
1762 LIST_PREPEND(conditions, u->conditions, c);
1766 int config_parse_unit_condition_null(const char *unit,
1767 const char *filename,
1769 const char *section,
1770 unsigned section_line,
1779 bool trigger, negate;
1787 if (isempty(rvalue)) {
1788 /* Empty assignment resets the list */
1789 condition_free_list(u->conditions);
1790 u->conditions = NULL;
1794 trigger = rvalue[0] == '|';
1798 negate = rvalue[0] == '!';
1802 b = parse_boolean(rvalue);
1804 log_syntax(unit, LOG_ERR, filename, line, -b,
1805 "Failed to parse boolean value in condition, ignoring: %s",
1813 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1817 LIST_PREPEND(conditions, u->conditions, c);
1821 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1822 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
1824 int config_parse_unit_requires_mounts_for(
1826 const char *filename,
1828 const char *section,
1829 unsigned section_line,
1846 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1848 _cleanup_free_ char *n;
1854 if (!utf8_is_valid(n)) {
1855 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1856 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
1860 r = unit_require_mounts_for(u, n);
1862 log_syntax(unit, LOG_ERR, filename, line, r,
1863 "Failed to add required mount for, ignoring: %s", rvalue);
1871 int config_parse_documentation(const char *unit,
1872 const char *filename,
1874 const char *section,
1875 unsigned section_line,
1891 if (isempty(rvalue)) {
1892 /* Empty assignment resets the list */
1893 strv_free(u->documentation);
1894 u->documentation = NULL;
1898 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
1899 rvalue, data, userdata);
1903 for (a = b = u->documentation; a && *a; a++) {
1905 if (is_valid_documentation_url(*a))
1908 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1909 "Invalid URL, ignoring: %s", *a);
1919 static void syscall_set(uint32_t *p, int nr) {
1920 nr = SYSCALL_TO_INDEX(nr);
1921 p[nr >> 4] |= 1 << (nr & 31);
1924 static void syscall_unset(uint32_t *p, int nr) {
1925 nr = SYSCALL_TO_INDEX(nr);
1926 p[nr >> 4] &= ~(1 << (nr & 31));
1929 int config_parse_syscall_filter(const char *unit,
1930 const char *filename,
1932 const char *section,
1933 unsigned section_line,
1940 ExecContext *c = data;
1942 bool invert = false;
1952 if (isempty(rvalue)) {
1953 /* Empty assignment resets the list */
1954 free(c->syscall_filter);
1955 c->syscall_filter = NULL;
1959 if (rvalue[0] == '~') {
1964 if (!c->syscall_filter) {
1967 n = (syscall_max() + 31) >> 4;
1968 c->syscall_filter = new(uint32_t, n);
1969 if (!c->syscall_filter)
1972 memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
1974 /* Add these by default */
1975 syscall_set(c->syscall_filter, __NR_execve);
1976 syscall_set(c->syscall_filter, __NR_rt_sigreturn);
1977 #ifdef __NR_sigreturn
1978 syscall_set(c->syscall_filter, __NR_sigreturn);
1980 syscall_set(c->syscall_filter, __NR_exit_group);
1981 syscall_set(c->syscall_filter, __NR_exit);
1984 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1986 _cleanup_free_ char *t = NULL;
1992 id = syscall_from_name(t);
1994 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1995 "Failed to parse syscall, ignoring: %s", t);
2000 syscall_unset(c->syscall_filter, id);
2002 syscall_set(c->syscall_filter, id);
2005 c->no_new_privileges = true;
2010 int config_parse_unit_slice(
2012 const char *filename,
2014 const char *section,
2015 unsigned section_line,
2022 _cleanup_free_ char *k = NULL;
2023 Unit *u = userdata, *slice;
2031 r = unit_name_printf(u, rvalue, &k);
2033 log_syntax(unit, LOG_ERR, filename, line, -r,
2034 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2041 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
2043 log_syntax(unit, LOG_ERR, filename, line, -r,
2044 "Failed to load slice unit %s. Ignoring.", k);
2048 if (slice->type != UNIT_SLICE) {
2049 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2050 "Slice unit %s is not a slice. Ignoring.", k);
2054 unit_ref_set(&u->slice, slice);
2058 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2060 int config_parse_cpu_shares(
2062 const char *filename,
2064 const char *section,
2065 unsigned section_line,
2072 CGroupContext *c = data;
2080 if (isempty(rvalue)) {
2081 c->cpu_shares = 1024;
2085 r = safe_atolu(rvalue, &lu);
2086 if (r < 0 || lu <= 0) {
2087 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2088 "CPU shares '%s' invalid. Ignoring.", rvalue);
2096 int config_parse_memory_limit(
2098 const char *filename,
2100 const char *section,
2101 unsigned section_line,
2108 CGroupContext *c = data;
2112 if (isempty(rvalue)) {
2113 c->memory_limit = (uint64_t) -1;
2117 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2119 r = parse_bytes(rvalue, &bytes);
2121 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2122 "Memory limit '%s' invalid. Ignoring.", rvalue);
2126 c->memory_limit = (uint64_t) bytes;
2130 int config_parse_device_allow(
2132 const char *filename,
2134 const char *section,
2135 unsigned section_line,
2142 _cleanup_free_ char *path = NULL;
2143 CGroupContext *c = data;
2144 CGroupDeviceAllow *a;
2148 if (isempty(rvalue)) {
2149 while (c->device_allow)
2150 cgroup_context_free_device_allow(c, c->device_allow);
2155 n = strcspn(rvalue, WHITESPACE);
2156 path = strndup(rvalue, n);
2160 if (!path_startswith(path, "/dev")) {
2161 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2162 "Invalid device node path '%s'. Ignoring.", path);
2166 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2170 if (!in_charset(m, "rwm")) {
2171 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2172 "Invalid device rights '%s'. Ignoring.", m);
2176 a = new0(CGroupDeviceAllow, 1);
2182 a->r = !!strchr(m, 'r');
2183 a->w = !!strchr(m, 'w');
2184 a->m = !!strchr(m, 'm');
2186 LIST_PREPEND(device_allow, c->device_allow, a);
2190 int config_parse_blockio_weight(
2192 const char *filename,
2194 const char *section,
2195 unsigned section_line,
2202 CGroupContext *c = data;
2210 if (isempty(rvalue)) {
2211 c->blockio_weight = 1000;
2215 r = safe_atolu(rvalue, &lu);
2216 if (r < 0 || lu < 10 || lu > 1000) {
2217 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2218 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2222 c->blockio_weight = lu;
2227 int config_parse_blockio_device_weight(
2229 const char *filename,
2231 const char *section,
2232 unsigned section_line,
2239 _cleanup_free_ char *path = NULL;
2240 CGroupBlockIODeviceWeight *w;
2241 CGroupContext *c = data;
2251 if (isempty(rvalue)) {
2252 while (c->blockio_device_weights)
2253 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2258 n = strcspn(rvalue, WHITESPACE);
2259 weight = rvalue + n;
2261 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2262 "Expected block device and device weight. Ignoring.");
2266 path = strndup(rvalue, n);
2270 if (!path_startswith(path, "/dev")) {
2271 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2272 "Invalid device node path '%s'. Ignoring.", path);
2276 weight += strspn(weight, WHITESPACE);
2277 r = safe_atolu(weight, &lu);
2278 if (r < 0 || lu < 10 || lu > 1000) {
2279 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2280 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2285 w = new0(CGroupBlockIODeviceWeight, 1);
2294 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
2298 int config_parse_blockio_bandwidth(
2300 const char *filename,
2302 const char *section,
2303 unsigned section_line,
2310 _cleanup_free_ char *path = NULL;
2311 CGroupBlockIODeviceBandwidth *b;
2312 CGroupContext *c = data;
2313 const char *bandwidth;
2323 read = streq("BlockIOReadBandwidth", lvalue);
2325 if (isempty(rvalue)) {
2326 CGroupBlockIODeviceBandwidth *next;
2328 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2329 if (b->read == read)
2330 cgroup_context_free_blockio_device_bandwidth(c, b);
2335 n = strcspn(rvalue, WHITESPACE);
2336 bandwidth = rvalue + n;
2337 bandwidth += strspn(bandwidth, WHITESPACE);
2340 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2341 "Expected space separated pair of device node and bandwidth. Ignoring.");
2345 path = strndup(rvalue, n);
2349 if (!path_startswith(path, "/dev")) {
2350 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2351 "Invalid device node path '%s'. Ignoring.", path);
2355 r = parse_bytes(bandwidth, &bytes);
2356 if (r < 0 || bytes <= 0) {
2357 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2358 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2362 b = new0(CGroupBlockIODeviceBandwidth, 1);
2368 b->bandwidth = (uint64_t) bytes;
2371 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
2376 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2378 int config_parse_job_mode_isolate(
2380 const char *filename,
2382 const char *section,
2383 unsigned section_line,
2397 r = parse_boolean(rvalue);
2399 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue);
2403 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2407 #define FOLLOW_MAX 8
2409 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2420 /* This will update the filename pointer if the loaded file is
2421 * reached by a symlink. The old string will be freed. */
2424 char *target, *name;
2426 if (c++ >= FOLLOW_MAX)
2429 path_kill_slashes(*filename);
2431 /* Add the file name we are currently looking at to
2432 * the names of this unit, but only if it is a valid
2434 name = basename(*filename);
2436 if (unit_name_is_valid(name, true)) {
2438 id = set_get(names, name);
2444 r = set_consume(names, id);
2450 /* Try to open the file name, but don't if its a symlink */
2451 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2458 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2459 r = readlink_and_make_absolute(*filename, &target);
2467 f = fdopen(fd, "re");
2470 close_nointr_nofail(fd);
2479 static int merge_by_names(Unit **u, Set *names, const char *id) {
2487 /* Let's try to add in all symlink names we found */
2488 while ((k = set_steal_first(names))) {
2490 /* First try to merge in the other name into our
2492 r = unit_merge_by_name(*u, k);
2496 /* Hmm, we couldn't merge the other unit into
2497 * ours? Then let's try it the other way
2500 other = manager_get_unit((*u)->manager, k);
2504 r = unit_merge(other, *u);
2507 return merge_by_names(u, names, NULL);
2515 unit_choose_id(*u, id);
2523 static int load_from_path(Unit *u, const char *path) {
2525 _cleanup_set_free_free_ Set *symlink_names = NULL;
2526 _cleanup_fclose_ FILE *f = NULL;
2527 _cleanup_free_ char *filename = NULL;
2535 symlink_names = set_new(string_hash_func, string_compare_func);
2539 if (path_is_absolute(path)) {
2541 filename = strdup(path);
2545 r = open_follow(&filename, &f, symlink_names, &id);
2557 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
2559 /* Instead of opening the path right away, we manually
2560 * follow all symlinks and add their name to our unit
2561 * name set while doing so */
2562 filename = path_make_absolute(path, *p);
2566 if (u->manager->unit_path_cache &&
2567 !set_get(u->manager->unit_path_cache, filename))
2570 r = open_follow(&filename, &f, symlink_names, &id);
2579 /* Empty the symlink names for the next run */
2580 set_clear_free(symlink_names);
2589 /* Hmm, no suitable file found? */
2593 r = merge_by_names(&merged, symlink_names, id);
2598 u->load_state = UNIT_MERGED;
2602 if (fstat(fileno(f), &st) < 0)
2605 if (null_or_empty(&st))
2606 u->load_state = UNIT_MASKED;
2608 u->load_state = UNIT_LOADED;
2610 /* Now, parse the file contents */
2611 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
2612 config_item_perf_lookup,
2613 (void*) load_fragment_gperf_lookup, false, true, u);
2618 free(u->fragment_path);
2619 u->fragment_path = filename;
2622 u->fragment_mtime = timespec_load(&st.st_mtim);
2624 if (u->source_path) {
2625 if (stat(u->source_path, &st) >= 0)
2626 u->source_mtime = timespec_load(&st.st_mtim);
2628 u->source_mtime = 0;
2634 int unit_load_fragment(Unit *u) {
2640 assert(u->load_state == UNIT_STUB);
2643 /* First, try to find the unit under its id. We always look
2644 * for unit files in the default directories, to make it easy
2645 * to override things by placing things in /etc/systemd/system */
2646 r = load_from_path(u, u->id);
2650 /* Try to find an alias we can load this with */
2651 if (u->load_state == UNIT_STUB)
2652 SET_FOREACH(t, u->names, i) {
2657 r = load_from_path(u, t);
2661 if (u->load_state != UNIT_STUB)
2665 /* And now, try looking for it under the suggested (originally linked) path */
2666 if (u->load_state == UNIT_STUB && u->fragment_path) {
2668 r = load_from_path(u, u->fragment_path);
2672 if (u->load_state == UNIT_STUB) {
2673 /* Hmm, this didn't work? Then let's get rid
2674 * of the fragment path stored for us, so that
2675 * we don't point to an invalid location. */
2676 free(u->fragment_path);
2677 u->fragment_path = NULL;
2681 /* Look for a template */
2682 if (u->load_state == UNIT_STUB && u->instance) {
2683 _cleanup_free_ char *k;
2685 k = unit_name_template(u->id);
2689 r = load_from_path(u, k);
2693 if (u->load_state == UNIT_STUB)
2694 SET_FOREACH(t, u->names, i) {
2695 _cleanup_free_ char *z = NULL;
2700 z = unit_name_template(t);
2704 r = load_from_path(u, z);
2708 if (u->load_state != UNIT_STUB)
2716 void unit_dump_config_items(FILE *f) {
2717 static const struct {
2718 const ConfigParserCallback callback;
2721 { config_parse_int, "INTEGER" },
2722 { config_parse_unsigned, "UNSIGNED" },
2723 { config_parse_bytes_size, "SIZE" },
2724 { config_parse_bool, "BOOLEAN" },
2725 { config_parse_string, "STRING" },
2726 { config_parse_path, "PATH" },
2727 { config_parse_unit_path_printf, "PATH" },
2728 { config_parse_strv, "STRING [...]" },
2729 { config_parse_exec_nice, "NICE" },
2730 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2731 { config_parse_exec_io_class, "IOCLASS" },
2732 { config_parse_exec_io_priority, "IOPRIORITY" },
2733 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2734 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2735 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2736 { config_parse_mode, "MODE" },
2737 { config_parse_unit_env_file, "FILE" },
2738 { config_parse_output, "OUTPUT" },
2739 { config_parse_input, "INPUT" },
2740 { config_parse_facility, "FACILITY" },
2741 { config_parse_level, "LEVEL" },
2742 { config_parse_exec_capabilities, "CAPABILITIES" },
2743 { config_parse_exec_secure_bits, "SECUREBITS" },
2744 { config_parse_bounding_set, "BOUNDINGSET" },
2745 { config_parse_limit, "LIMIT" },
2746 { config_parse_unit_deps, "UNIT [...]" },
2747 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2748 { config_parse_service_type, "SERVICETYPE" },
2749 { config_parse_service_restart, "SERVICERESTART" },
2750 #ifdef HAVE_SYSV_COMPAT
2751 { config_parse_sysv_priority, "SYSVPRIORITY" },
2753 { config_parse_warn_compat, "NOTSUPPORTED" },
2755 { config_parse_kill_mode, "KILLMODE" },
2756 { config_parse_kill_signal, "SIGNAL" },
2757 { config_parse_socket_listen, "SOCKET [...]" },
2758 { config_parse_socket_bind, "SOCKETBIND" },
2759 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
2760 { config_parse_sec, "SECONDS" },
2761 { config_parse_nsec, "NANOSECONDS" },
2762 { config_parse_path_strv, "PATH [...]" },
2763 { config_parse_unit_requires_mounts_for, "PATH [...]" },
2764 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2765 { config_parse_unit_string_printf, "STRING" },
2766 { config_parse_trigger_unit, "UNIT" },
2767 { config_parse_timer, "TIMER" },
2768 { config_parse_path_spec, "PATH" },
2769 { config_parse_notify_access, "ACCESS" },
2770 { config_parse_ip_tos, "TOS" },
2771 { config_parse_unit_condition_path, "CONDITION" },
2772 { config_parse_unit_condition_string, "CONDITION" },
2773 { config_parse_unit_condition_null, "CONDITION" },
2774 { config_parse_unit_slice, "SLICE" },
2775 { config_parse_documentation, "URL" },
2776 { config_parse_service_timeout, "SECONDS" },
2777 { config_parse_start_limit_action, "ACTION" },
2778 { config_parse_set_status, "STATUS" },
2779 { config_parse_service_sockets, "SOCKETS" },
2780 { config_parse_environ, "ENVIRON" },
2781 { config_parse_syscall_filter, "SYSCALL" },
2782 { config_parse_cpu_shares, "SHARES" },
2783 { config_parse_memory_limit, "LIMIT" },
2784 { config_parse_device_allow, "DEVICE" },
2785 { config_parse_device_policy, "POLICY" },
2786 { config_parse_blockio_bandwidth, "BANDWIDTH" },
2787 { config_parse_blockio_weight, "WEIGHT" },
2788 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
2789 { config_parse_long, "LONG" },
2790 { config_parse_socket_service, "SERVICE" },
2793 const char *prev = NULL;
2798 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2799 const char *rvalue = "OTHER", *lvalue;
2803 const ConfigPerfItem *p;
2805 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2807 dot = strchr(i, '.');
2808 lvalue = dot ? dot + 1 : i;
2812 if (!prev || !strneq(prev, i, prefix_len+1)) {
2816 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2819 for (j = 0; j < ELEMENTSOF(table); j++)
2820 if (p->parse == table[j].callback) {
2821 rvalue = table[j].rvalue;
2825 fprintf(f, "%s=%s\n", lvalue, rvalue);