1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
7 Copyright 2012 Holger Hans Peter Freyther
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <linux/oom.h>
30 #include <sys/prctl.h>
31 #include <sys/mount.h>
35 #include <sys/resource.h>
41 #include "sd-messages.h"
44 #include "conf-parser.h"
45 #include "load-fragment.h"
48 #include "securebits.h"
50 #include "unit-name.h"
51 #include "unit-printf.h"
53 #include "path-util.h"
57 #include "bus-error.h"
58 #include "errno-list.h"
61 #include "seccomp-util.h"
64 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP)
65 int config_parse_warn_compat(
70 unsigned section_line,
77 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
78 "Support for option %s= has been disabled at compile time and is ignored",
84 int config_parse_unit_deps(const char* unit,
88 unsigned section_line,
95 UnitDependency d = ltype;
105 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
106 _cleanup_free_ char *t = NULL, *k = NULL;
113 r = unit_name_printf(u, t, &k);
115 log_syntax(unit, LOG_ERR, filename, line, -r,
116 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
120 r = unit_add_dependency_by_name(u, d, k, NULL, true);
122 log_syntax(unit, LOG_ERR, filename, line, -r,
123 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
129 int config_parse_unit_string_printf(const char *unit,
130 const char *filename,
133 unsigned section_line,
141 _cleanup_free_ char *k = NULL;
149 r = unit_full_printf(u, rvalue, &k);
151 log_syntax(unit, LOG_ERR, filename, line, -r,
152 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
154 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
155 k ? k : rvalue, data, userdata);
158 int config_parse_unit_strv_printf(const char *unit,
159 const char *filename,
162 unsigned section_line,
170 _cleanup_free_ char *k = NULL;
178 r = unit_full_printf(u, rvalue, &k);
180 log_syntax(unit, LOG_ERR, filename, line, -r,
181 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
183 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
184 k ? k : rvalue, data, userdata);
187 int config_parse_unit_path_printf(const char *unit,
188 const char *filename,
191 unsigned section_line,
199 _cleanup_free_ char *k = NULL;
207 r = unit_full_printf(u, rvalue, &k);
209 log_syntax(unit, LOG_ERR, filename, line, -r,
210 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
212 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype,
213 k ? k : rvalue, data, userdata);
216 int config_parse_socket_listen(const char *unit,
217 const char *filename,
220 unsigned section_line,
227 SocketPort *p, *tail;
238 if (isempty(rvalue)) {
239 /* An empty assignment removes all ports */
240 socket_free_ports(s);
244 p = new0(SocketPort, 1);
248 if (ltype != SOCKET_SOCKET) {
251 r = unit_full_printf(UNIT(s), rvalue, &p->path);
253 p->path = strdup(rvalue);
258 log_syntax(unit, LOG_ERR, filename, line, -r,
259 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
262 path_kill_slashes(p->path);
264 } else if (streq(lvalue, "ListenNetlink")) {
265 _cleanup_free_ char *k = NULL;
267 p->type = SOCKET_SOCKET;
268 r = unit_full_printf(UNIT(s), rvalue, &k);
270 log_syntax(unit, LOG_ERR, filename, line, -r,
271 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
273 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
275 log_syntax(unit, LOG_ERR, filename, line, -r,
276 "Failed to parse address value, ignoring: %s", rvalue);
282 _cleanup_free_ char *k = NULL;
284 p->type = SOCKET_SOCKET;
285 r = unit_full_printf(UNIT(s), rvalue, &k);
287 log_syntax(unit, LOG_ERR, filename, line, -r,
288 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
290 r = socket_address_parse(&p->address, k ? k : rvalue);
292 log_syntax(unit, LOG_ERR, filename, line, -r,
293 "Failed to parse address value, ignoring: %s", rvalue);
298 if (streq(lvalue, "ListenStream"))
299 p->address.type = SOCK_STREAM;
300 else if (streq(lvalue, "ListenDatagram"))
301 p->address.type = SOCK_DGRAM;
303 assert(streq(lvalue, "ListenSequentialPacket"));
304 p->address.type = SOCK_SEQPACKET;
307 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
308 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
309 "Address family not supported, ignoring: %s", rvalue);
319 LIST_FIND_TAIL(port, s->ports, tail);
320 LIST_INSERT_AFTER(port, s->ports, tail, p);
322 LIST_PREPEND(port, s->ports, p);
327 int config_parse_socket_bind(const char *unit,
328 const char *filename,
331 unsigned section_line,
339 SocketAddressBindIPv6Only b;
348 b = socket_address_bind_ipv6_only_from_string(rvalue);
352 r = parse_boolean(rvalue);
354 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
355 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
359 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
361 s->bind_ipv6_only = b;
366 int config_parse_exec_nice(const char *unit,
367 const char *filename,
370 unsigned section_line,
377 ExecContext *c = data;
385 r = safe_atoi(rvalue, &priority);
387 log_syntax(unit, LOG_ERR, filename, line, -r,
388 "Failed to parse nice priority, ignoring: %s. ", rvalue);
392 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
393 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
394 "Nice priority out of range, ignoring: %s", rvalue);
404 int config_parse_exec_oom_score_adjust(const char* unit,
405 const char *filename,
408 unsigned section_line,
415 ExecContext *c = data;
423 r = safe_atoi(rvalue, &oa);
425 log_syntax(unit, LOG_ERR, filename, line, -r,
426 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
430 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
431 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
432 "OOM score adjust value out of range, ignoring: %s", rvalue);
436 c->oom_score_adjust = oa;
437 c->oom_score_adjust_set = true;
442 int config_parse_exec(const char *unit,
443 const char *filename,
446 unsigned section_line,
453 ExecCommand **e = data, *nce;
465 if (isempty(rvalue)) {
466 /* An empty assignment resets the list */
467 exec_command_free_list(*e);
472 /* We accept an absolute path as first argument, or
473 * alternatively an absolute prefixed with @ to allow
474 * overriding of argv[0]. */
480 bool honour_argv0 = false, ignore = false;
486 rvalue += strspn(rvalue, WHITESPACE);
491 for (i = 0; i < 2; i++) {
492 if (rvalue[0] == '-' && !ignore) {
497 if (rvalue[0] == '@' && !honour_argv0) {
503 if (*rvalue != '/') {
504 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
505 "Executable path is not absolute, ignoring: %s", rvalue);
510 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
511 if (strneq(w, ";", MAX(l, 1U)))
517 n = new(char*, k + !honour_argv0);
522 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
523 if (strneq(w, ";", MAX(l, 1U)))
525 else if (strneq(w, "\\;", MAX(l, 1U)))
528 if (honour_argv0 && w == rvalue) {
531 path = strndup(w, l);
537 if (!utf8_is_valid(path)) {
538 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
539 "Path is not UTF-8 clean, ignoring assignment: %s",
548 c = n[k++] = cunescape_length(w, l);
554 if (!utf8_is_valid(c)) {
555 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
556 "Path is not UTF-8 clean, ignoring assignment: %s",
567 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
568 "Invalid command line, ignoring: %s", rvalue);
581 assert(path_is_absolute(path));
583 nce = new0(ExecCommand, 1);
591 nce->ignore = ignore;
593 path_kill_slashes(nce->path);
595 exec_command_append_list(e, nce);
611 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
612 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
614 int config_parse_socket_bindtodevice(const char* unit,
615 const char *filename,
618 unsigned section_line,
633 if (rvalue[0] && !streq(rvalue, "*")) {
640 free(s->bind_to_device);
641 s->bind_to_device = n;
646 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
647 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
649 int config_parse_exec_io_class(const char *unit,
650 const char *filename,
653 unsigned section_line,
660 ExecContext *c = data;
668 x = ioprio_class_from_string(rvalue);
670 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
671 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
675 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
676 c->ioprio_set = true;
681 int config_parse_exec_io_priority(const char *unit,
682 const char *filename,
685 unsigned section_line,
692 ExecContext *c = data;
700 r = safe_atoi(rvalue, &i);
701 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
702 log_syntax(unit, LOG_ERR, filename, line, -r,
703 "Failed to parse IO priority, ignoring: %s", rvalue);
707 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
708 c->ioprio_set = true;
713 int config_parse_exec_cpu_sched_policy(const char *unit,
714 const char *filename,
717 unsigned section_line,
725 ExecContext *c = data;
733 x = sched_policy_from_string(rvalue);
735 log_syntax(unit, LOG_ERR, filename, line, -x,
736 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
740 c->cpu_sched_policy = x;
741 /* Moving to or from real-time policy? We need to adjust the priority */
742 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
743 c->cpu_sched_set = true;
748 int config_parse_exec_cpu_sched_prio(const char *unit,
749 const char *filename,
752 unsigned section_line,
759 ExecContext *c = data;
767 r = safe_atoi(rvalue, &i);
769 log_syntax(unit, LOG_ERR, filename, line, -r,
770 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
774 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
775 min = sched_get_priority_min(c->cpu_sched_policy);
776 max = sched_get_priority_max(c->cpu_sched_policy);
778 if (i < min || i > max) {
779 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
780 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
784 c->cpu_sched_priority = i;
785 c->cpu_sched_set = true;
790 int config_parse_exec_cpu_affinity(const char *unit,
791 const char *filename,
794 unsigned section_line,
801 ExecContext *c = data;
811 if (isempty(rvalue)) {
812 /* An empty assignment resets the CPU list */
819 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
820 _cleanup_free_ char *t = NULL;
828 r = safe_atou(t, &cpu);
831 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
836 if (r < 0 || cpu >= c->cpuset_ncpus) {
837 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
838 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
842 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
848 int config_parse_exec_capabilities(const char *unit,
849 const char *filename,
852 unsigned section_line,
859 ExecContext *c = data;
867 cap = cap_from_text(rvalue);
869 log_syntax(unit, LOG_ERR, filename, line, errno,
870 "Failed to parse capabilities, ignoring: %s", rvalue);
875 cap_free(c->capabilities);
876 c->capabilities = cap;
881 int config_parse_exec_secure_bits(const char *unit,
882 const char *filename,
885 unsigned section_line,
892 ExecContext *c = data;
902 if (isempty(rvalue)) {
903 /* An empty assignment resets the field */
908 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
909 if (first_word(w, "keep-caps"))
910 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
911 else if (first_word(w, "keep-caps-locked"))
912 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
913 else if (first_word(w, "no-setuid-fixup"))
914 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
915 else if (first_word(w, "no-setuid-fixup-locked"))
916 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
917 else if (first_word(w, "noroot"))
918 c->secure_bits |= 1<<SECURE_NOROOT;
919 else if (first_word(w, "noroot-locked"))
920 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
922 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
923 "Failed to parse secure bits, ignoring: %s", rvalue);
931 int config_parse_bounding_set(const char *unit,
932 const char *filename,
935 unsigned section_line,
942 uint64_t *capability_bounding_set_drop = data;
954 if (rvalue[0] == '~') {
959 /* Note that we store this inverted internally, since the
960 * kernel wants it like this. But we actually expose it
961 * non-inverted everywhere to have a fully normalized
964 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
965 _cleanup_free_ char *t = NULL;
973 r = cap_from_name(t, &cap);
975 log_syntax(unit, LOG_ERR, filename, line, errno,
976 "Failed to parse capability in bounding set, ignoring: %s", t);
980 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
984 *capability_bounding_set_drop |= sum;
986 *capability_bounding_set_drop |= ~sum;
991 int config_parse_limit(const char *unit,
992 const char *filename,
995 unsigned section_line,
1002 struct rlimit **rl = data;
1003 unsigned long long u;
1012 if (streq(rvalue, "infinity"))
1013 u = (unsigned long long) RLIM_INFINITY;
1017 r = safe_atollu(rvalue, &u);
1019 log_syntax(unit, LOG_ERR, filename, line, -r,
1020 "Failed to parse resource value, ignoring: %s", rvalue);
1026 *rl = new(struct rlimit, 1);
1031 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
1035 #ifdef HAVE_SYSV_COMPAT
1036 int config_parse_sysv_priority(const char *unit,
1037 const char *filename,
1039 const char *section,
1040 unsigned section_line,
1047 int *priority = data;
1055 r = safe_atoi(rvalue, &i);
1056 if (r < 0 || i < 0) {
1057 log_syntax(unit, LOG_ERR, filename, line, -r,
1058 "Failed to parse SysV start priority, ignoring: %s", rvalue);
1062 *priority = (int) i;
1067 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1069 int config_parse_kill_signal(const char *unit,
1070 const char *filename,
1072 const char *section,
1073 unsigned section_line,
1088 r = signal_from_string_try_harder(rvalue);
1090 log_syntax(unit, LOG_ERR, filename, line, -r,
1091 "Failed to parse kill signal, ignoring: %s", rvalue);
1099 int config_parse_exec_mount_flags(const char *unit,
1100 const char *filename,
1102 const char *section,
1103 unsigned section_line,
1110 ExecContext *c = data;
1114 unsigned long flags = 0;
1121 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
1122 _cleanup_free_ char *t;
1128 if (streq(t, "shared"))
1130 else if (streq(t, "slave"))
1132 else if (streq(w, "private"))
1133 flags |= MS_PRIVATE;
1135 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1136 "Failed to parse mount flag %s, ignoring: %s",
1142 c->mount_flags = flags;
1146 int config_parse_timer(const char *unit,
1147 const char *filename,
1149 const char *section,
1150 unsigned section_line,
1161 CalendarSpec *c = NULL;
1169 if (isempty(rvalue)) {
1170 /* Empty assignment resets list */
1171 timer_free_values(t);
1175 b = timer_base_from_string(lvalue);
1177 log_syntax(unit, LOG_ERR, filename, line, -b,
1178 "Failed to parse timer base, ignoring: %s", lvalue);
1182 if (b == TIMER_CALENDAR) {
1183 if (calendar_spec_from_string(rvalue, &c) < 0) {
1184 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1185 "Failed to parse calendar specification, ignoring: %s",
1190 id = CLOCK_REALTIME;
1192 if (parse_sec(rvalue, &u) < 0) {
1193 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1194 "Failed to parse timer value, ignoring: %s",
1199 id = CLOCK_MONOTONIC;
1202 v = new0(TimerValue, 1);
1209 v->calendar_spec = c;
1211 LIST_PREPEND(value, t->values, v);
1216 int config_parse_trigger_unit(
1218 const char *filename,
1220 const char *section,
1221 unsigned section_line,
1228 _cleanup_free_ char *p = NULL;
1238 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1239 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1240 "Multiple units to trigger specified, ignoring: %s", rvalue);
1244 r = unit_name_printf(u, rvalue, &p);
1246 log_syntax(unit, LOG_ERR, filename, line, -r,
1247 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1249 type = unit_name_to_type(p ?: rvalue);
1251 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1252 "Unit type not valid, ignoring: %s", rvalue);
1256 if (type == u->type) {
1257 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1258 "Trigger cannot be of same type, ignoring: %s", rvalue);
1262 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
1264 log_syntax(unit, LOG_ERR, filename, line, -r,
1265 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
1272 int config_parse_path_spec(const char *unit,
1273 const char *filename,
1275 const char *section,
1276 unsigned section_line,
1286 _cleanup_free_ char *k = NULL;
1294 if (isempty(rvalue)) {
1295 /* Empty assignment clears list */
1300 b = path_type_from_string(lvalue);
1302 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1303 "Failed to parse path type, ignoring: %s", lvalue);
1307 r = unit_full_printf(UNIT(p), rvalue, &k);
1313 log_syntax(unit, LOG_ERR, filename, line, -r,
1314 "Failed to resolve unit specifiers on %s. Ignoring.",
1318 if (!path_is_absolute(k)) {
1319 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1320 "Path is not absolute, ignoring: %s", k);
1324 s = new0(PathSpec, 1);
1329 s->path = path_kill_slashes(k);
1334 LIST_PREPEND(spec, p->specs, s);
1339 int config_parse_socket_service(const char *unit,
1340 const char *filename,
1342 const char *section,
1343 unsigned section_line,
1350 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1354 _cleanup_free_ char *p = NULL;
1361 r = unit_name_printf(UNIT(s), rvalue, &p);
1363 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1367 if (!endswith(p, ".service")) {
1368 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1372 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1374 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1378 unit_ref_set(&s->service, x);
1383 int config_parse_service_sockets(const char *unit,
1384 const char *filename,
1386 const char *section,
1387 unsigned section_line,
1404 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1405 _cleanup_free_ char *t = NULL, *k = NULL;
1411 r = unit_name_printf(UNIT(s), t, &k);
1413 log_syntax(unit, LOG_ERR, filename, line, -r,
1414 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1416 if (!endswith(k ?: t, ".socket")) {
1417 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1418 "Unit must be of type socket, ignoring: %s", k ?: t);
1422 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
1424 log_syntax(unit, LOG_ERR, filename, line, -r,
1425 "Failed to add dependency on %s, ignoring: %s",
1426 k ?: t, strerror(-r));
1428 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
1436 int config_parse_service_timeout(const char *unit,
1437 const char *filename,
1439 const char *section,
1440 unsigned section_line,
1447 Service *s = userdata;
1455 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
1456 rvalue, data, userdata);
1460 if (streq(lvalue, "TimeoutSec")) {
1461 s->start_timeout_defined = true;
1462 s->timeout_stop_usec = s->timeout_start_usec;
1463 } else if (streq(lvalue, "TimeoutStartSec"))
1464 s->start_timeout_defined = true;
1469 int config_parse_busname_service(
1471 const char *filename,
1473 const char *section,
1474 unsigned section_line,
1481 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1485 _cleanup_free_ char *p = NULL;
1492 r = unit_name_printf(UNIT(n), rvalue, &p);
1494 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1498 if (!endswith(p, ".service")) {
1499 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1503 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1505 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1509 unit_ref_set(&n->service, x);
1514 int config_parse_unit_env_file(const char *unit,
1515 const char *filename,
1517 const char *section,
1518 unsigned section_line,
1527 _cleanup_free_ char *n = NULL;
1536 if (isempty(rvalue)) {
1537 /* Empty assignment frees the list */
1543 r = unit_full_printf(u, rvalue, &n);
1545 log_syntax(unit, LOG_ERR, filename, line, r,
1546 "Failed to resolve specifiers, ignoring: %s", rvalue);
1549 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1550 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1551 "Path '%s' is not absolute, ignoring.", s);
1555 r = strv_extend(env, s);
1562 int config_parse_environ(const char *unit,
1563 const char *filename,
1565 const char *section,
1566 unsigned section_line,
1574 char*** env = data, *w, *state;
1576 _cleanup_free_ char *k = NULL;
1584 if (isempty(rvalue)) {
1585 /* Empty assignment resets the list */
1592 r = unit_full_printf(u, rvalue, &k);
1594 log_syntax(unit, LOG_ERR, filename, line, -r,
1595 "Failed to resolve specifiers, ignoring: %s", rvalue);
1603 FOREACH_WORD_QUOTED(w, l, k, state) {
1604 _cleanup_free_ char *n;
1607 n = cunescape_length(w, l);
1611 if (!env_assignment_is_valid(n)) {
1612 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1613 "Invalid environment assignment, ignoring: %s", rvalue);
1617 x = strv_env_set(*env, n);
1628 int config_parse_ip_tos(const char *unit,
1629 const char *filename,
1631 const char *section,
1632 unsigned section_line,
1639 int *ip_tos = data, x;
1646 x = ip_tos_from_string(rvalue);
1648 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1649 "Failed to parse IP TOS value, ignoring: %s", rvalue);
1657 int config_parse_unit_condition_path(const char *unit,
1658 const char *filename,
1660 const char *section,
1661 unsigned section_line,
1668 ConditionType cond = ltype;
1670 bool trigger, negate;
1672 _cleanup_free_ char *p = NULL;
1680 if (isempty(rvalue)) {
1681 /* Empty assignment resets the list */
1682 condition_free_list(u->conditions);
1683 u->conditions = NULL;
1687 trigger = rvalue[0] == '|';
1691 negate = rvalue[0] == '!';
1695 r = unit_full_printf(u, rvalue, &p);
1697 log_syntax(unit, LOG_ERR, filename, line, -r,
1698 "Failed to resolve specifiers, ignoring: %s", rvalue);
1705 if (!path_is_absolute(p)) {
1706 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1707 "Path in condition not absolute, ignoring: %s", p);
1711 c = condition_new(cond, p, trigger, negate);
1715 LIST_PREPEND(conditions, u->conditions, c);
1719 int config_parse_unit_condition_string(const char *unit,
1720 const char *filename,
1722 const char *section,
1723 unsigned section_line,
1730 ConditionType cond = ltype;
1732 bool trigger, negate;
1734 _cleanup_free_ char *s = NULL;
1742 if (isempty(rvalue)) {
1743 /* Empty assignment resets the list */
1744 condition_free_list(u->conditions);
1745 u->conditions = NULL;
1749 trigger = rvalue[0] == '|';
1753 negate = rvalue[0] == '!';
1757 r = unit_full_printf(u, rvalue, &s);
1759 log_syntax(unit, LOG_ERR, filename, line, -r,
1760 "Failed to resolve specifiers, ignoring: %s", rvalue);
1767 c = condition_new(cond, s, trigger, negate);
1771 LIST_PREPEND(conditions, u->conditions, c);
1775 int config_parse_unit_condition_null(const char *unit,
1776 const char *filename,
1778 const char *section,
1779 unsigned section_line,
1788 bool trigger, negate;
1796 if (isempty(rvalue)) {
1797 /* Empty assignment resets the list */
1798 condition_free_list(u->conditions);
1799 u->conditions = NULL;
1803 trigger = rvalue[0] == '|';
1807 negate = rvalue[0] == '!';
1811 b = parse_boolean(rvalue);
1813 log_syntax(unit, LOG_ERR, filename, line, -b,
1814 "Failed to parse boolean value in condition, ignoring: %s",
1822 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1826 LIST_PREPEND(conditions, u->conditions, c);
1830 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1831 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
1833 int config_parse_unit_requires_mounts_for(
1835 const char *filename,
1837 const char *section,
1838 unsigned section_line,
1855 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1857 _cleanup_free_ char *n;
1863 if (!utf8_is_valid(n)) {
1864 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1865 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
1869 r = unit_require_mounts_for(u, n);
1871 log_syntax(unit, LOG_ERR, filename, line, r,
1872 "Failed to add required mount for, ignoring: %s", rvalue);
1880 int config_parse_documentation(const char *unit,
1881 const char *filename,
1883 const char *section,
1884 unsigned section_line,
1900 if (isempty(rvalue)) {
1901 /* Empty assignment resets the list */
1902 strv_free(u->documentation);
1903 u->documentation = NULL;
1907 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
1908 rvalue, data, userdata);
1912 for (a = b = u->documentation; a && *a; a++) {
1914 if (is_valid_documentation_url(*a))
1917 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1918 "Invalid URL, ignoring: %s", *a);
1929 int config_parse_syscall_filter(
1931 const char *filename,
1933 const char *section,
1934 unsigned section_line,
1941 static const char default_syscalls[] =
1948 ExecContext *c = data;
1950 bool invert = false;
1960 if (isempty(rvalue)) {
1961 /* Empty assignment resets the list */
1962 set_free(c->syscall_filter);
1963 c->syscall_filter = NULL;
1964 c->syscall_whitelist = false;
1968 if (rvalue[0] == '~') {
1973 if (!c->syscall_filter) {
1974 c->syscall_filter = set_new(trivial_hash_func, trivial_compare_func);
1975 if (!c->syscall_filter)
1979 /* Allow everything but the ones listed */
1980 c->syscall_whitelist = false;
1984 /* Allow nothing but the ones listed */
1985 c->syscall_whitelist = true;
1987 /* Accept default syscalls if we are on a whitelist */
1988 NULSTR_FOREACH(i, default_syscalls) {
1991 id = seccomp_syscall_resolve_name(i);
1995 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2004 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2005 _cleanup_free_ char *t = NULL;
2012 id = seccomp_syscall_resolve_name(t);
2014 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call, ignoring: %s", t);
2018 /* If we previously wanted to forbid a syscall and now
2019 * we want to allow it, then remove it from the list
2021 if (!invert == c->syscall_whitelist) {
2022 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2028 set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
2031 c->no_new_privileges = true;
2036 int config_parse_syscall_archs(
2038 const char *filename,
2040 const char *section,
2041 unsigned section_line,
2053 if (isempty(rvalue)) {
2059 r = set_ensure_allocated(archs, trivial_hash_func, trivial_compare_func);
2063 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2064 _cleanup_free_ char *t = NULL;
2071 r = seccomp_arch_from_string(t, &a);
2073 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call architecture, ignoring: %s", t);
2077 r = set_put(*archs, UINT32_TO_PTR(a + 1));
2087 int config_parse_syscall_errno(
2089 const char *filename,
2091 const char *section,
2092 unsigned section_line,
2099 ExecContext *c = data;
2106 if (isempty(rvalue)) {
2107 /* Empty assignment resets to KILL */
2108 c->syscall_errno = 0;
2112 e = errno_from_name(rvalue);
2114 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse error number, ignoring: %s", rvalue);
2118 c->syscall_errno = e;
2123 int config_parse_unit_slice(
2125 const char *filename,
2127 const char *section,
2128 unsigned section_line,
2135 _cleanup_free_ char *k = NULL;
2136 Unit *u = userdata, *slice;
2144 r = unit_name_printf(u, rvalue, &k);
2146 log_syntax(unit, LOG_ERR, filename, line, -r,
2147 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2154 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
2156 log_syntax(unit, LOG_ERR, filename, line, -r,
2157 "Failed to load slice unit %s. Ignoring.", k);
2161 if (slice->type != UNIT_SLICE) {
2162 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2163 "Slice unit %s is not a slice. Ignoring.", k);
2167 unit_ref_set(&u->slice, slice);
2171 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2173 int config_parse_cpu_shares(
2175 const char *filename,
2177 const char *section,
2178 unsigned section_line,
2185 CGroupContext *c = data;
2193 if (isempty(rvalue)) {
2194 c->cpu_shares = 1024;
2198 r = safe_atolu(rvalue, &lu);
2199 if (r < 0 || lu <= 0) {
2200 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2201 "CPU shares '%s' invalid. Ignoring.", rvalue);
2209 int config_parse_memory_limit(
2211 const char *filename,
2213 const char *section,
2214 unsigned section_line,
2221 CGroupContext *c = data;
2225 if (isempty(rvalue)) {
2226 c->memory_limit = (uint64_t) -1;
2230 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2232 r = parse_bytes(rvalue, &bytes);
2234 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2235 "Memory limit '%s' invalid. Ignoring.", rvalue);
2239 c->memory_limit = (uint64_t) bytes;
2243 int config_parse_device_allow(
2245 const char *filename,
2247 const char *section,
2248 unsigned section_line,
2255 _cleanup_free_ char *path = NULL;
2256 CGroupContext *c = data;
2257 CGroupDeviceAllow *a;
2261 if (isempty(rvalue)) {
2262 while (c->device_allow)
2263 cgroup_context_free_device_allow(c, c->device_allow);
2268 n = strcspn(rvalue, WHITESPACE);
2269 path = strndup(rvalue, n);
2273 if (!path_startswith(path, "/dev")) {
2274 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2275 "Invalid device node path '%s'. Ignoring.", path);
2279 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2283 if (!in_charset(m, "rwm")) {
2284 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2285 "Invalid device rights '%s'. Ignoring.", m);
2289 a = new0(CGroupDeviceAllow, 1);
2295 a->r = !!strchr(m, 'r');
2296 a->w = !!strchr(m, 'w');
2297 a->m = !!strchr(m, 'm');
2299 LIST_PREPEND(device_allow, c->device_allow, a);
2303 int config_parse_blockio_weight(
2305 const char *filename,
2307 const char *section,
2308 unsigned section_line,
2315 CGroupContext *c = data;
2323 if (isempty(rvalue)) {
2324 c->blockio_weight = 1000;
2328 r = safe_atolu(rvalue, &lu);
2329 if (r < 0 || lu < 10 || lu > 1000) {
2330 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2331 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2335 c->blockio_weight = lu;
2340 int config_parse_blockio_device_weight(
2342 const char *filename,
2344 const char *section,
2345 unsigned section_line,
2352 _cleanup_free_ char *path = NULL;
2353 CGroupBlockIODeviceWeight *w;
2354 CGroupContext *c = data;
2364 if (isempty(rvalue)) {
2365 while (c->blockio_device_weights)
2366 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2371 n = strcspn(rvalue, WHITESPACE);
2372 weight = rvalue + n;
2374 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2375 "Expected block device and device weight. Ignoring.");
2379 path = strndup(rvalue, n);
2383 if (!path_startswith(path, "/dev")) {
2384 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2385 "Invalid device node path '%s'. Ignoring.", path);
2389 weight += strspn(weight, WHITESPACE);
2390 r = safe_atolu(weight, &lu);
2391 if (r < 0 || lu < 10 || lu > 1000) {
2392 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2393 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2398 w = new0(CGroupBlockIODeviceWeight, 1);
2407 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
2411 int config_parse_blockio_bandwidth(
2413 const char *filename,
2415 const char *section,
2416 unsigned section_line,
2423 _cleanup_free_ char *path = NULL;
2424 CGroupBlockIODeviceBandwidth *b;
2425 CGroupContext *c = data;
2426 const char *bandwidth;
2436 read = streq("BlockIOReadBandwidth", lvalue);
2438 if (isempty(rvalue)) {
2439 CGroupBlockIODeviceBandwidth *next;
2441 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2442 if (b->read == read)
2443 cgroup_context_free_blockio_device_bandwidth(c, b);
2448 n = strcspn(rvalue, WHITESPACE);
2449 bandwidth = rvalue + n;
2450 bandwidth += strspn(bandwidth, WHITESPACE);
2453 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2454 "Expected space separated pair of device node and bandwidth. Ignoring.");
2458 path = strndup(rvalue, n);
2462 if (!path_startswith(path, "/dev")) {
2463 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2464 "Invalid device node path '%s'. Ignoring.", path);
2468 r = parse_bytes(bandwidth, &bytes);
2469 if (r < 0 || bytes <= 0) {
2470 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2471 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2475 b = new0(CGroupBlockIODeviceBandwidth, 1);
2481 b->bandwidth = (uint64_t) bytes;
2484 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
2489 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2491 int config_parse_job_mode_isolate(
2493 const char *filename,
2495 const char *section,
2496 unsigned section_line,
2510 r = parse_boolean(rvalue);
2512 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue);
2516 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2520 #define FOLLOW_MAX 8
2522 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2533 /* This will update the filename pointer if the loaded file is
2534 * reached by a symlink. The old string will be freed. */
2537 char *target, *name;
2539 if (c++ >= FOLLOW_MAX)
2542 path_kill_slashes(*filename);
2544 /* Add the file name we are currently looking at to
2545 * the names of this unit, but only if it is a valid
2547 name = basename(*filename);
2549 if (unit_name_is_valid(name, TEMPLATE_VALID)) {
2551 id = set_get(names, name);
2557 r = set_consume(names, id);
2563 /* Try to open the file name, but don't if its a symlink */
2564 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2571 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2572 r = readlink_and_make_absolute(*filename, &target);
2580 f = fdopen(fd, "re");
2583 close_nointr_nofail(fd);
2592 static int merge_by_names(Unit **u, Set *names, const char *id) {
2600 /* Let's try to add in all symlink names we found */
2601 while ((k = set_steal_first(names))) {
2603 /* First try to merge in the other name into our
2605 r = unit_merge_by_name(*u, k);
2609 /* Hmm, we couldn't merge the other unit into
2610 * ours? Then let's try it the other way
2613 other = manager_get_unit((*u)->manager, k);
2617 r = unit_merge(other, *u);
2620 return merge_by_names(u, names, NULL);
2628 unit_choose_id(*u, id);
2636 static int load_from_path(Unit *u, const char *path) {
2638 _cleanup_set_free_free_ Set *symlink_names = NULL;
2639 _cleanup_fclose_ FILE *f = NULL;
2640 _cleanup_free_ char *filename = NULL;
2648 symlink_names = set_new(string_hash_func, string_compare_func);
2652 if (path_is_absolute(path)) {
2654 filename = strdup(path);
2658 r = open_follow(&filename, &f, symlink_names, &id);
2670 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
2672 /* Instead of opening the path right away, we manually
2673 * follow all symlinks and add their name to our unit
2674 * name set while doing so */
2675 filename = path_make_absolute(path, *p);
2679 if (u->manager->unit_path_cache &&
2680 !set_get(u->manager->unit_path_cache, filename))
2683 r = open_follow(&filename, &f, symlink_names, &id);
2692 /* Empty the symlink names for the next run */
2693 set_clear_free(symlink_names);
2702 /* Hmm, no suitable file found? */
2706 r = merge_by_names(&merged, symlink_names, id);
2711 u->load_state = UNIT_MERGED;
2715 if (fstat(fileno(f), &st) < 0)
2718 if (null_or_empty(&st))
2719 u->load_state = UNIT_MASKED;
2721 u->load_state = UNIT_LOADED;
2723 /* Now, parse the file contents */
2724 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
2725 config_item_perf_lookup,
2726 (void*) load_fragment_gperf_lookup, false, true, u);
2731 free(u->fragment_path);
2732 u->fragment_path = filename;
2735 u->fragment_mtime = timespec_load(&st.st_mtim);
2737 if (u->source_path) {
2738 if (stat(u->source_path, &st) >= 0)
2739 u->source_mtime = timespec_load(&st.st_mtim);
2741 u->source_mtime = 0;
2747 int unit_load_fragment(Unit *u) {
2753 assert(u->load_state == UNIT_STUB);
2756 /* First, try to find the unit under its id. We always look
2757 * for unit files in the default directories, to make it easy
2758 * to override things by placing things in /etc/systemd/system */
2759 r = load_from_path(u, u->id);
2763 /* Try to find an alias we can load this with */
2764 if (u->load_state == UNIT_STUB)
2765 SET_FOREACH(t, u->names, i) {
2770 r = load_from_path(u, t);
2774 if (u->load_state != UNIT_STUB)
2778 /* And now, try looking for it under the suggested (originally linked) path */
2779 if (u->load_state == UNIT_STUB && u->fragment_path) {
2781 r = load_from_path(u, u->fragment_path);
2785 if (u->load_state == UNIT_STUB) {
2786 /* Hmm, this didn't work? Then let's get rid
2787 * of the fragment path stored for us, so that
2788 * we don't point to an invalid location. */
2789 free(u->fragment_path);
2790 u->fragment_path = NULL;
2794 /* Look for a template */
2795 if (u->load_state == UNIT_STUB && u->instance) {
2796 _cleanup_free_ char *k;
2798 k = unit_name_template(u->id);
2802 r = load_from_path(u, k);
2806 if (u->load_state == UNIT_STUB)
2807 SET_FOREACH(t, u->names, i) {
2808 _cleanup_free_ char *z = NULL;
2813 z = unit_name_template(t);
2817 r = load_from_path(u, z);
2821 if (u->load_state != UNIT_STUB)
2829 void unit_dump_config_items(FILE *f) {
2830 static const struct {
2831 const ConfigParserCallback callback;
2834 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP)
2835 { config_parse_warn_compat, "NOTSUPPORTED" },
2837 { config_parse_int, "INTEGER" },
2838 { config_parse_unsigned, "UNSIGNED" },
2839 { config_parse_bytes_size, "SIZE" },
2840 { config_parse_bool, "BOOLEAN" },
2841 { config_parse_string, "STRING" },
2842 { config_parse_path, "PATH" },
2843 { config_parse_unit_path_printf, "PATH" },
2844 { config_parse_strv, "STRING [...]" },
2845 { config_parse_exec_nice, "NICE" },
2846 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2847 { config_parse_exec_io_class, "IOCLASS" },
2848 { config_parse_exec_io_priority, "IOPRIORITY" },
2849 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2850 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2851 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2852 { config_parse_mode, "MODE" },
2853 { config_parse_unit_env_file, "FILE" },
2854 { config_parse_output, "OUTPUT" },
2855 { config_parse_input, "INPUT" },
2856 { config_parse_facility, "FACILITY" },
2857 { config_parse_level, "LEVEL" },
2858 { config_parse_exec_capabilities, "CAPABILITIES" },
2859 { config_parse_exec_secure_bits, "SECUREBITS" },
2860 { config_parse_bounding_set, "BOUNDINGSET" },
2861 { config_parse_limit, "LIMIT" },
2862 { config_parse_unit_deps, "UNIT [...]" },
2863 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2864 { config_parse_service_type, "SERVICETYPE" },
2865 { config_parse_service_restart, "SERVICERESTART" },
2866 #ifdef HAVE_SYSV_COMPAT
2867 { config_parse_sysv_priority, "SYSVPRIORITY" },
2869 { config_parse_kill_mode, "KILLMODE" },
2870 { config_parse_kill_signal, "SIGNAL" },
2871 { config_parse_socket_listen, "SOCKET [...]" },
2872 { config_parse_socket_bind, "SOCKETBIND" },
2873 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
2874 { config_parse_sec, "SECONDS" },
2875 { config_parse_nsec, "NANOSECONDS" },
2876 { config_parse_path_strv, "PATH [...]" },
2877 { config_parse_unit_requires_mounts_for, "PATH [...]" },
2878 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2879 { config_parse_unit_string_printf, "STRING" },
2880 { config_parse_trigger_unit, "UNIT" },
2881 { config_parse_timer, "TIMER" },
2882 { config_parse_path_spec, "PATH" },
2883 { config_parse_notify_access, "ACCESS" },
2884 { config_parse_ip_tos, "TOS" },
2885 { config_parse_unit_condition_path, "CONDITION" },
2886 { config_parse_unit_condition_string, "CONDITION" },
2887 { config_parse_unit_condition_null, "CONDITION" },
2888 { config_parse_unit_slice, "SLICE" },
2889 { config_parse_documentation, "URL" },
2890 { config_parse_service_timeout, "SECONDS" },
2891 { config_parse_start_limit_action, "ACTION" },
2892 { config_parse_set_status, "STATUS" },
2893 { config_parse_service_sockets, "SOCKETS" },
2894 { config_parse_environ, "ENVIRON" },
2896 { config_parse_syscall_filter, "SYSCALLS" },
2897 { config_parse_syscall_errno, "ERRNO" },
2899 { config_parse_cpu_shares, "SHARES" },
2900 { config_parse_memory_limit, "LIMIT" },
2901 { config_parse_device_allow, "DEVICE" },
2902 { config_parse_device_policy, "POLICY" },
2903 { config_parse_blockio_bandwidth, "BANDWIDTH" },
2904 { config_parse_blockio_weight, "WEIGHT" },
2905 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
2906 { config_parse_long, "LONG" },
2907 { config_parse_socket_service, "SERVICE" },
2910 const char *prev = NULL;
2915 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2916 const char *rvalue = "OTHER", *lvalue;
2920 const ConfigPerfItem *p;
2922 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2924 dot = strchr(i, '.');
2925 lvalue = dot ? dot + 1 : i;
2929 if (!prev || !strneq(prev, i, prefix_len+1)) {
2933 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2936 for (j = 0; j < ELEMENTSOF(table); j++)
2937 if (p->parse == table[j].callback) {
2938 rvalue = table[j].rvalue;
2942 fprintf(f, "%s=%s\n", lvalue, rvalue);