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/resource.h>
38 #include "conf-parser.h"
39 #include "load-fragment.h"
42 #include "securebits.h"
44 #include "unit-name.h"
45 #include "unit-printf.h"
47 #include "path-util.h"
51 #include "bus-error.h"
52 #include "errno-list.h"
55 #include "signal-util.h"
56 #include "bus-internal.h"
59 #include "seccomp-util.h"
62 int config_parse_warn_compat(
67 unsigned section_line
,
73 Disabled reason
= ltype
;
76 case DISABLED_CONFIGURATION
:
77 log_syntax(unit
, LOG_DEBUG
, filename
, line
, EINVAL
,
78 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
81 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
82 "Support for option %s= has been removed and it is ignored", lvalue
);
84 case DISABLED_EXPERIMENTAL
:
85 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
86 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
93 int config_parse_unit_deps(const char *unit
,
97 unsigned section_line
,
104 UnitDependency d
= ltype
;
106 const char *word
, *state
;
113 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
114 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
117 t
= strndup(word
, l
);
121 r
= unit_name_printf(u
, t
, &k
);
123 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
124 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
128 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
130 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
131 "Failed to add dependency on %s, ignoring: %s", k
, strerror(-r
));
134 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
139 int config_parse_unit_string_printf(
141 const char *filename
,
144 unsigned section_line
,
151 _cleanup_free_
char *k
= NULL
;
160 r
= unit_full_printf(u
, rvalue
, &k
);
162 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
166 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
169 int config_parse_unit_strv_printf(const char *unit
,
170 const char *filename
,
173 unsigned section_line
,
181 _cleanup_free_
char *k
= NULL
;
189 r
= unit_full_printf(u
, rvalue
, &k
);
191 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
192 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
194 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
195 k
? k
: rvalue
, data
, userdata
);
198 int config_parse_unit_path_printf(const char *unit
,
199 const char *filename
,
202 unsigned section_line
,
209 _cleanup_free_
char *k
= NULL
;
218 r
= unit_full_printf(u
, rvalue
, &k
);
220 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
224 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
227 int config_parse_unit_path_strv_printf(
229 const char *filename
,
232 unsigned section_line
,
240 const char *word
, *state
;
250 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
251 _cleanup_free_
char *k
= NULL
;
257 r
= unit_full_printf(u
, t
, &k
);
259 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", t
, strerror(-r
));
263 if (!utf8_is_valid(k
)) {
264 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
268 if (!path_is_absolute(k
)) {
269 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Symlink path %s is not absolute, ignoring: %s", k
, strerror(-r
));
273 path_kill_slashes(k
);
282 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
287 int config_parse_socket_listen(const char *unit
,
288 const char *filename
,
291 unsigned section_line
,
298 _cleanup_free_ SocketPort
*p
= NULL
;
310 if (isempty(rvalue
)) {
311 /* An empty assignment removes all ports */
312 socket_free_ports(s
);
316 p
= new0(SocketPort
, 1);
320 if (ltype
!= SOCKET_SOCKET
) {
323 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
325 p
->path
= strdup(rvalue
);
329 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
330 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
333 path_kill_slashes(p
->path
);
335 } else if (streq(lvalue
, "ListenNetlink")) {
336 _cleanup_free_
char *k
= NULL
;
338 p
->type
= SOCKET_SOCKET
;
339 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
341 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
342 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
344 r
= socket_address_parse_netlink(&p
->address
, k
?: rvalue
);
346 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
347 "Failed to parse address value, ignoring: %s", rvalue
);
352 _cleanup_free_
char *k
= NULL
;
354 p
->type
= SOCKET_SOCKET
;
355 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
357 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
358 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
360 r
= socket_address_parse_and_warn(&p
->address
, k
? k
: rvalue
);
362 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
363 "Failed to parse address value, ignoring: %s", rvalue
);
367 if (streq(lvalue
, "ListenStream"))
368 p
->address
.type
= SOCK_STREAM
;
369 else if (streq(lvalue
, "ListenDatagram"))
370 p
->address
.type
= SOCK_DGRAM
;
372 assert(streq(lvalue
, "ListenSequentialPacket"));
373 p
->address
.type
= SOCK_SEQPACKET
;
376 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
377 log_syntax(unit
, LOG_ERR
, filename
, line
, EOPNOTSUPP
,
378 "Address family not supported, ignoring: %s", rvalue
);
384 p
->auxiliary_fds
= NULL
;
385 p
->n_auxiliary_fds
= 0;
389 LIST_FIND_TAIL(port
, s
->ports
, tail
);
390 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
392 LIST_PREPEND(port
, s
->ports
, p
);
398 int config_parse_socket_bind(const char *unit
,
399 const char *filename
,
402 unsigned section_line
,
410 SocketAddressBindIPv6Only b
;
419 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
423 r
= parse_boolean(rvalue
);
425 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
426 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
430 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
432 s
->bind_ipv6_only
= b
;
437 int config_parse_exec_nice(const char *unit
,
438 const char *filename
,
441 unsigned section_line
,
448 ExecContext
*c
= data
;
456 r
= safe_atoi(rvalue
, &priority
);
458 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
459 "Failed to parse nice priority, ignoring: %s. ", rvalue
);
463 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
464 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
465 "Nice priority out of range, ignoring: %s", rvalue
);
475 int config_parse_exec_oom_score_adjust(const char* unit
,
476 const char *filename
,
479 unsigned section_line
,
486 ExecContext
*c
= data
;
494 r
= safe_atoi(rvalue
, &oa
);
496 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
497 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
501 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
502 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
503 "OOM score adjust value out of range, ignoring: %s", rvalue
);
507 c
->oom_score_adjust
= oa
;
508 c
->oom_score_adjust_set
= true;
513 int config_parse_exec(
515 const char *filename
,
518 unsigned section_line
,
525 ExecCommand
**e
= data
;
537 rvalue
+= strspn(rvalue
, WHITESPACE
);
540 if (isempty(rvalue
)) {
541 /* An empty assignment resets the list */
542 *e
= exec_command_free_list(*e
);
548 _cleanup_strv_free_
char **n
= NULL
;
549 size_t nlen
= 0, nbufsize
= 0;
550 _cleanup_free_ ExecCommand
*nce
= NULL
;
551 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
553 bool separate_argv0
= false, ignore
= false;
557 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
562 for (i
= 0; i
< 2; i
++) {
563 /* We accept an absolute path as first argument, or
564 * alternatively an absolute prefixed with @ to allow
565 * overriding of argv[0]. */
566 if (*f
== '-' && !ignore
)
568 else if (*f
== '@' && !separate_argv0
)
569 separate_argv0
= true;
576 /* First word is either "-" or "@" with no command. */
577 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
578 "Empty path in command line, ignoring: \"%s\"", rvalue
);
582 if (!string_is_safe(f
)) {
583 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
584 "Executable path contains special characters, ignoring: %s", rvalue
);
587 if (!path_is_absolute(f
)) {
588 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
589 "Executable path is not absolute, ignoring: %s", rvalue
);
592 if (endswith(f
, "/")) {
593 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
594 "Executable path specifies a directory, ignoring: %s", rvalue
);
598 if (f
== firstword
) {
607 if (!separate_argv0
) {
608 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
617 path_kill_slashes(path
);
619 while (!isempty(p
)) {
620 _cleanup_free_
char *word
= NULL
;
622 /* Check explicitly for an unquoted semicolon as
623 * command separator token. */
624 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
626 p
+= strspn(p
, WHITESPACE
);
631 /* Check for \; explicitly, to not confuse it with \\;
632 * or "\;" or "\\;" etc. extract_first_word would
633 * return the same for all of those. */
634 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
636 p
+= strspn(p
, WHITESPACE
);
637 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
647 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
653 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
661 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
662 "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
666 nce
= new0(ExecCommand
, 1);
672 nce
->ignore
= ignore
;
674 exec_command_append_list(e
, nce
);
676 /* Do not _cleanup_free_ these. */
687 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
688 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
690 int config_parse_socket_bindtodevice(const char* unit
,
691 const char *filename
,
694 unsigned section_line
,
709 if (rvalue
[0] && !streq(rvalue
, "*")) {
716 free(s
->bind_to_device
);
717 s
->bind_to_device
= n
;
722 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
723 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
725 int config_parse_exec_io_class(const char *unit
,
726 const char *filename
,
729 unsigned section_line
,
736 ExecContext
*c
= data
;
744 x
= ioprio_class_from_string(rvalue
);
746 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
747 "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
751 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
752 c
->ioprio_set
= true;
757 int config_parse_exec_io_priority(const char *unit
,
758 const char *filename
,
761 unsigned section_line
,
768 ExecContext
*c
= data
;
776 r
= safe_atoi(rvalue
, &i
);
777 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
778 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
779 "Failed to parse IO priority, ignoring: %s", rvalue
);
783 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
784 c
->ioprio_set
= true;
789 int config_parse_exec_cpu_sched_policy(const char *unit
,
790 const char *filename
,
793 unsigned section_line
,
801 ExecContext
*c
= data
;
809 x
= sched_policy_from_string(rvalue
);
811 log_syntax(unit
, LOG_ERR
, filename
, line
, -x
,
812 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
816 c
->cpu_sched_policy
= x
;
817 /* Moving to or from real-time policy? We need to adjust the priority */
818 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
819 c
->cpu_sched_set
= true;
824 int config_parse_exec_cpu_sched_prio(const char *unit
,
825 const char *filename
,
828 unsigned section_line
,
835 ExecContext
*c
= data
;
843 r
= safe_atoi(rvalue
, &i
);
845 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
846 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
850 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
851 min
= sched_get_priority_min(c
->cpu_sched_policy
);
852 max
= sched_get_priority_max(c
->cpu_sched_policy
);
854 if (i
< min
|| i
> max
) {
855 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
856 "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
860 c
->cpu_sched_priority
= i
;
861 c
->cpu_sched_set
= true;
866 int config_parse_exec_cpu_affinity(const char *unit
,
867 const char *filename
,
870 unsigned section_line
,
877 ExecContext
*c
= data
;
878 const char *word
, *state
;
886 if (isempty(rvalue
)) {
887 /* An empty assignment resets the CPU list */
894 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
895 _cleanup_free_
char *t
= NULL
;
899 t
= strndup(word
, l
);
903 r
= safe_atou(t
, &cpu
);
906 c
->cpuset
= cpu_set_malloc(&c
->cpuset_ncpus
);
911 if (r
< 0 || cpu
>= c
->cpuset_ncpus
) {
912 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
913 "Failed to parse CPU affinity '%s', ignoring: %s", t
, rvalue
);
917 CPU_SET_S(cpu
, CPU_ALLOC_SIZE(c
->cpuset_ncpus
), c
->cpuset
);
920 log_syntax(unit
, LOG_WARNING
, filename
, line
, EINVAL
,
921 "Trailing garbage, ignoring.");
926 int config_parse_exec_capabilities(const char *unit
,
927 const char *filename
,
930 unsigned section_line
,
937 ExecContext
*c
= data
;
945 cap
= cap_from_text(rvalue
);
947 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
,
948 "Failed to parse capabilities, ignoring: %s", rvalue
);
953 cap_free(c
->capabilities
);
954 c
->capabilities
= cap
;
959 int config_parse_exec_secure_bits(const char *unit
,
960 const char *filename
,
963 unsigned section_line
,
970 ExecContext
*c
= data
;
972 const char *word
, *state
;
979 if (isempty(rvalue
)) {
980 /* An empty assignment resets the field */
985 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
986 if (first_word(word
, "keep-caps"))
987 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
988 else if (first_word(word
, "keep-caps-locked"))
989 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
990 else if (first_word(word
, "no-setuid-fixup"))
991 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
992 else if (first_word(word
, "no-setuid-fixup-locked"))
993 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
994 else if (first_word(word
, "noroot"))
995 c
->secure_bits
|= 1<<SECURE_NOROOT
;
996 else if (first_word(word
, "noroot-locked"))
997 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
999 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1000 "Failed to parse secure bits, ignoring: %s", rvalue
);
1004 if (!isempty(state
))
1005 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1006 "Invalid syntax, garbage at the end, ignoring.");
1011 int config_parse_bounding_set(const char *unit
,
1012 const char *filename
,
1014 const char *section
,
1015 unsigned section_line
,
1022 uint64_t *capability_bounding_set_drop
= data
;
1023 const char *word
, *state
;
1025 bool invert
= false;
1033 if (rvalue
[0] == '~') {
1038 /* Note that we store this inverted internally, since the
1039 * kernel wants it like this. But we actually expose it
1040 * non-inverted everywhere to have a fully normalized
1043 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1044 _cleanup_free_
char *t
= NULL
;
1047 t
= strndup(word
, l
);
1051 cap
= capability_from_name(t
);
1053 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse capability in bounding set, ignoring: %s", t
);
1057 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1059 if (!isempty(state
))
1060 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1061 "Trailing garbage, ignoring.");
1064 *capability_bounding_set_drop
|= sum
;
1066 *capability_bounding_set_drop
|= ~sum
;
1071 int config_parse_limit(const char *unit
,
1072 const char *filename
,
1074 const char *section
,
1075 unsigned section_line
,
1082 struct rlimit
**rl
= data
;
1083 unsigned long long u
;
1092 if (streq(rvalue
, "infinity"))
1093 u
= (unsigned long long) RLIM_INFINITY
;
1097 r
= safe_atollu(rvalue
, &u
);
1099 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1100 "Failed to parse resource value, ignoring: %s", rvalue
);
1106 *rl
= new(struct rlimit
, 1);
1111 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1115 #ifdef HAVE_SYSV_COMPAT
1116 int config_parse_sysv_priority(const char *unit
,
1117 const char *filename
,
1119 const char *section
,
1120 unsigned section_line
,
1127 int *priority
= data
;
1135 r
= safe_atoi(rvalue
, &i
);
1136 if (r
< 0 || i
< 0) {
1137 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1138 "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1142 *priority
= (int) i
;
1147 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1148 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1150 int config_parse_exec_mount_flags(const char *unit
,
1151 const char *filename
,
1153 const char *section
,
1154 unsigned section_line
,
1161 ExecContext
*c
= data
;
1162 const char *word
, *state
;
1164 unsigned long flags
= 0;
1171 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1172 _cleanup_free_
char *t
;
1174 t
= strndup(word
, l
);
1178 if (streq(t
, "shared"))
1180 else if (streq(t
, "slave"))
1182 else if (streq(t
, "private"))
1185 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1189 if (!isempty(state
))
1190 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1192 c
->mount_flags
= flags
;
1196 int config_parse_exec_selinux_context(
1198 const char *filename
,
1200 const char *section
,
1201 unsigned section_line
,
1208 ExecContext
*c
= data
;
1219 if (isempty(rvalue
)) {
1220 c
->selinux_context
= mfree(c
->selinux_context
);
1221 c
->selinux_context_ignore
= false;
1225 if (rvalue
[0] == '-') {
1231 r
= unit_name_printf(u
, rvalue
, &k
);
1233 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1234 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1238 free(c
->selinux_context
);
1239 c
->selinux_context
= k
;
1240 c
->selinux_context_ignore
= ignore
;
1245 int config_parse_exec_apparmor_profile(
1247 const char *filename
,
1249 const char *section
,
1250 unsigned section_line
,
1257 ExecContext
*c
= data
;
1268 if (isempty(rvalue
)) {
1269 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1270 c
->apparmor_profile_ignore
= false;
1274 if (rvalue
[0] == '-') {
1280 r
= unit_name_printf(u
, rvalue
, &k
);
1282 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1283 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1287 free(c
->apparmor_profile
);
1288 c
->apparmor_profile
= k
;
1289 c
->apparmor_profile_ignore
= ignore
;
1294 int config_parse_exec_smack_process_label(
1296 const char *filename
,
1298 const char *section
,
1299 unsigned section_line
,
1306 ExecContext
*c
= data
;
1317 if (isempty(rvalue
)) {
1318 c
->smack_process_label
= mfree(c
->smack_process_label
);
1319 c
->smack_process_label_ignore
= false;
1323 if (rvalue
[0] == '-') {
1329 r
= unit_name_printf(u
, rvalue
, &k
);
1331 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1332 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1336 free(c
->smack_process_label
);
1337 c
->smack_process_label
= k
;
1338 c
->smack_process_label_ignore
= ignore
;
1343 int config_parse_timer(const char *unit
,
1344 const char *filename
,
1346 const char *section
,
1347 unsigned section_line
,
1358 CalendarSpec
*c
= NULL
;
1365 if (isempty(rvalue
)) {
1366 /* Empty assignment resets list */
1367 timer_free_values(t
);
1371 b
= timer_base_from_string(lvalue
);
1373 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
,
1374 "Failed to parse timer base, ignoring: %s", lvalue
);
1378 if (b
== TIMER_CALENDAR
) {
1379 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1380 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1381 "Failed to parse calendar specification, ignoring: %s",
1386 if (parse_sec(rvalue
, &u
) < 0) {
1387 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1388 "Failed to parse timer value, ignoring: %s",
1394 v
= new0(TimerValue
, 1);
1396 calendar_spec_free(c
);
1402 v
->calendar_spec
= c
;
1404 LIST_PREPEND(value
, t
->values
, v
);
1409 int config_parse_trigger_unit(
1411 const char *filename
,
1413 const char *section
,
1414 unsigned section_line
,
1421 _cleanup_free_
char *p
= NULL
;
1431 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1432 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1433 "Multiple units to trigger specified, ignoring: %s", rvalue
);
1437 r
= unit_name_printf(u
, rvalue
, &p
);
1439 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1440 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1442 type
= unit_name_to_type(p
?: rvalue
);
1444 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1445 "Unit type not valid, ignoring: %s", rvalue
);
1449 if (type
== u
->type
) {
1450 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1451 "Trigger cannot be of same type, ignoring: %s", rvalue
);
1455 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
?: rvalue
, NULL
, true);
1457 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1458 "Failed to add trigger on %s, ignoring: %s", p
?: rvalue
, strerror(-r
));
1465 int config_parse_path_spec(const char *unit
,
1466 const char *filename
,
1468 const char *section
,
1469 unsigned section_line
,
1479 _cleanup_free_
char *k
= NULL
;
1487 if (isempty(rvalue
)) {
1488 /* Empty assignment clears list */
1493 b
= path_type_from_string(lvalue
);
1495 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1496 "Failed to parse path type, ignoring: %s", lvalue
);
1500 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1506 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1507 "Failed to resolve unit specifiers on %s. Ignoring.",
1511 if (!path_is_absolute(k
)) {
1512 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1513 "Path is not absolute, ignoring: %s", k
);
1517 s
= new0(PathSpec
, 1);
1522 s
->path
= path_kill_slashes(k
);
1527 LIST_PREPEND(spec
, p
->specs
, s
);
1532 int config_parse_socket_service(
1534 const char *filename
,
1536 const char *section
,
1537 unsigned section_line
,
1544 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1548 _cleanup_free_
char *p
= NULL
;
1555 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1557 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1561 if (!endswith(p
, ".service")) {
1562 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type service, ignoring: %s", rvalue
);
1566 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1568 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1572 unit_ref_set(&s
->service
, x
);
1577 int config_parse_service_sockets(
1579 const char *filename
,
1581 const char *section
,
1582 unsigned section_line
,
1590 const char *word
, *state
;
1599 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1600 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1602 t
= strndup(word
, l
);
1606 r
= unit_name_printf(UNIT(s
), t
, &k
);
1608 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1612 if (!endswith(k
, ".socket")) {
1613 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type socket, ignoring: %s", k
);
1617 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1619 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1621 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1623 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1625 if (!isempty(state
))
1626 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1631 int config_parse_bus_name(
1633 const char *filename
,
1635 const char *section
,
1636 unsigned section_line
,
1643 _cleanup_free_
char *k
= NULL
;
1652 r
= unit_full_printf(u
, rvalue
, &k
);
1654 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1658 if (!service_name_is_valid(k
)) {
1659 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bus name %s, ignoring.", k
);
1663 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1666 int config_parse_service_timeout(const char *unit
,
1667 const char *filename
,
1669 const char *section
,
1670 unsigned section_line
,
1677 Service
*s
= userdata
;
1685 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1686 rvalue
, data
, userdata
);
1690 if (streq(lvalue
, "TimeoutSec")) {
1691 s
->start_timeout_defined
= true;
1692 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1693 } else if (streq(lvalue
, "TimeoutStartSec"))
1694 s
->start_timeout_defined
= true;
1699 int config_parse_busname_service(
1701 const char *filename
,
1703 const char *section
,
1704 unsigned section_line
,
1711 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1715 _cleanup_free_
char *p
= NULL
;
1722 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1724 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1725 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1729 if (!endswith(p
, ".service")) {
1730 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1731 "Unit must be of type service, ignoring: %s", rvalue
);
1735 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1737 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1738 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1742 unit_ref_set(&n
->service
, x
);
1747 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1749 int config_parse_bus_policy(
1751 const char *filename
,
1753 const char *section
,
1754 unsigned section_line
,
1761 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1762 _cleanup_free_
char *id_str
= NULL
;
1763 BusName
*busname
= data
;
1771 p
= new0(BusNamePolicy
, 1);
1775 if (streq(lvalue
, "AllowUser"))
1776 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1777 else if (streq(lvalue
, "AllowGroup"))
1778 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1780 assert_not_reached("Unknown lvalue");
1782 id_str
= strdup(rvalue
);
1786 access_str
= strpbrk(id_str
, WHITESPACE
);
1788 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1789 "Invalid busname policy value '%s'", rvalue
);
1795 access_str
+= strspn(access_str
, WHITESPACE
);
1797 p
->access
= bus_policy_access_from_string(access_str
);
1798 if (p
->access
< 0) {
1799 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1800 "Invalid busname policy access type '%s'", access_str
);
1807 LIST_PREPEND(policy
, busname
->policy
, p
);
1813 int config_parse_bus_endpoint_policy(
1815 const char *filename
,
1817 const char *section
,
1818 unsigned section_line
,
1825 _cleanup_free_
char *name
= NULL
;
1826 BusPolicyAccess access
;
1827 ExecContext
*c
= data
;
1836 name
= strdup(rvalue
);
1840 access_str
= strpbrk(name
, WHITESPACE
);
1842 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1843 "Invalid endpoint policy value '%s'", rvalue
);
1849 access_str
+= strspn(access_str
, WHITESPACE
);
1851 access
= bus_policy_access_from_string(access_str
);
1852 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1853 access
>= _BUS_POLICY_ACCESS_MAX
) {
1854 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1855 "Invalid endpoint policy access type '%s'", access_str
);
1859 if (!c
->bus_endpoint
) {
1860 r
= bus_endpoint_new(&c
->bus_endpoint
);
1866 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1869 int config_parse_unit_env_file(const char *unit
,
1870 const char *filename
,
1872 const char *section
,
1873 unsigned section_line
,
1882 _cleanup_free_
char *n
= NULL
;
1891 if (isempty(rvalue
)) {
1892 /* Empty assignment frees the list */
1893 *env
= strv_free(*env
);
1897 r
= unit_full_printf(u
, rvalue
, &n
);
1899 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1900 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1903 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1904 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1905 "Path '%s' is not absolute, ignoring.", s
);
1909 r
= strv_extend(env
, s
);
1916 int config_parse_environ(const char *unit
,
1917 const char *filename
,
1919 const char *section
,
1920 unsigned section_line
,
1929 const char *word
, *state
;
1931 _cleanup_free_
char *k
= NULL
;
1939 if (isempty(rvalue
)) {
1940 /* Empty assignment resets the list */
1941 *env
= strv_free(*env
);
1946 r
= unit_full_printf(u
, rvalue
, &k
);
1948 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1956 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1957 _cleanup_free_
char *n
= NULL
;
1960 r
= cunescape_length(word
, l
, 0, &n
);
1962 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
1966 if (!env_assignment_is_valid(n
)) {
1967 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid environment assignment, ignoring: %s", rvalue
);
1971 x
= strv_env_set(*env
, n
);
1978 if (!isempty(state
))
1979 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1980 "Trailing garbage, ignoring.");
1985 int config_parse_ip_tos(const char *unit
,
1986 const char *filename
,
1988 const char *section
,
1989 unsigned section_line
,
1996 int *ip_tos
= data
, x
;
2003 x
= ip_tos_from_string(rvalue
);
2005 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2006 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2014 int config_parse_unit_condition_path(
2016 const char *filename
,
2018 const char *section
,
2019 unsigned section_line
,
2026 _cleanup_free_
char *p
= NULL
;
2027 Condition
**list
= data
, *c
;
2028 ConditionType t
= ltype
;
2029 bool trigger
, negate
;
2038 if (isempty(rvalue
)) {
2039 /* Empty assignment resets the list */
2040 *list
= condition_free_list(*list
);
2044 trigger
= rvalue
[0] == '|';
2048 negate
= rvalue
[0] == '!';
2052 r
= unit_full_printf(u
, rvalue
, &p
);
2054 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2058 if (!path_is_absolute(p
)) {
2059 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2063 c
= condition_new(t
, p
, trigger
, negate
);
2067 LIST_PREPEND(conditions
, *list
, c
);
2071 int config_parse_unit_condition_string(
2073 const char *filename
,
2075 const char *section
,
2076 unsigned section_line
,
2083 _cleanup_free_
char *s
= NULL
;
2084 Condition
**list
= data
, *c
;
2085 ConditionType t
= ltype
;
2086 bool trigger
, negate
;
2095 if (isempty(rvalue
)) {
2096 /* Empty assignment resets the list */
2097 *list
= condition_free_list(*list
);
2101 trigger
= rvalue
[0] == '|';
2105 negate
= rvalue
[0] == '!';
2109 r
= unit_full_printf(u
, rvalue
, &s
);
2111 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2115 c
= condition_new(t
, s
, trigger
, negate
);
2119 LIST_PREPEND(conditions
, *list
, c
);
2123 int config_parse_unit_condition_null(
2125 const char *filename
,
2127 const char *section
,
2128 unsigned section_line
,
2135 Condition
**list
= data
, *c
;
2136 bool trigger
, negate
;
2144 if (isempty(rvalue
)) {
2145 /* Empty assignment resets the list */
2146 *list
= condition_free_list(*list
);
2150 trigger
= rvalue
[0] == '|';
2154 negate
= rvalue
[0] == '!';
2158 b
= parse_boolean(rvalue
);
2160 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2167 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2171 LIST_PREPEND(conditions
, *list
, c
);
2175 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2176 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2178 int config_parse_unit_requires_mounts_for(
2180 const char *filename
,
2182 const char *section
,
2183 unsigned section_line
,
2191 const char *word
, *state
;
2199 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2201 _cleanup_free_
char *n
;
2203 n
= strndup(word
, l
);
2207 if (!utf8_is_valid(n
)) {
2208 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2212 r
= unit_require_mounts_for(u
, n
);
2214 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2215 "Failed to add required mount for, ignoring: %s", rvalue
);
2219 if (!isempty(state
))
2220 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2221 "Trailing garbage, ignoring.");
2226 int config_parse_documentation(const char *unit
,
2227 const char *filename
,
2229 const char *section
,
2230 unsigned section_line
,
2246 if (isempty(rvalue
)) {
2247 /* Empty assignment resets the list */
2248 u
->documentation
= strv_free(u
->documentation
);
2252 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2253 rvalue
, data
, userdata
);
2257 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2259 if (documentation_url_is_valid(*a
))
2262 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2263 "Invalid URL, ignoring: %s", *a
);
2274 int config_parse_syscall_filter(
2276 const char *filename
,
2278 const char *section
,
2279 unsigned section_line
,
2286 static const char default_syscalls
[] =
2293 ExecContext
*c
= data
;
2295 bool invert
= false;
2296 const char *word
, *state
;
2305 if (isempty(rvalue
)) {
2306 /* Empty assignment resets the list */
2307 c
->syscall_filter
= set_free(c
->syscall_filter
);
2308 c
->syscall_whitelist
= false;
2312 if (rvalue
[0] == '~') {
2317 if (!c
->syscall_filter
) {
2318 c
->syscall_filter
= set_new(NULL
);
2319 if (!c
->syscall_filter
)
2323 /* Allow everything but the ones listed */
2324 c
->syscall_whitelist
= false;
2328 /* Allow nothing but the ones listed */
2329 c
->syscall_whitelist
= true;
2331 /* Accept default syscalls if we are on a whitelist */
2332 NULSTR_FOREACH(i
, default_syscalls
) {
2335 id
= seccomp_syscall_resolve_name(i
);
2339 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2348 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2349 _cleanup_free_
char *t
= NULL
;
2352 t
= strndup(word
, l
);
2356 id
= seccomp_syscall_resolve_name(t
);
2358 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2359 "Failed to parse system call, ignoring: %s", t
);
2363 /* If we previously wanted to forbid a syscall and now
2364 * we want to allow it, then remove it from the list
2366 if (!invert
== c
->syscall_whitelist
) {
2367 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2373 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2375 if (!isempty(state
))
2376 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2377 "Trailing garbage, ignoring.");
2379 /* Turn on NNP, but only if it wasn't configured explicitly
2380 * before, and only if we are in user mode. */
2381 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2382 c
->no_new_privileges
= true;
2387 int config_parse_syscall_archs(
2389 const char *filename
,
2391 const char *section
,
2392 unsigned section_line
,
2400 const char *word
, *state
;
2404 if (isempty(rvalue
)) {
2405 *archs
= set_free(*archs
);
2409 r
= set_ensure_allocated(archs
, NULL
);
2413 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2414 _cleanup_free_
char *t
= NULL
;
2417 t
= strndup(word
, l
);
2421 r
= seccomp_arch_from_string(t
, &a
);
2423 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2424 "Failed to parse system call architecture, ignoring: %s", t
);
2428 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2434 if (!isempty(state
))
2435 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2436 "Trailing garbage, ignoring.");
2441 int config_parse_syscall_errno(
2443 const char *filename
,
2445 const char *section
,
2446 unsigned section_line
,
2453 ExecContext
*c
= data
;
2460 if (isempty(rvalue
)) {
2461 /* Empty assignment resets to KILL */
2462 c
->syscall_errno
= 0;
2466 e
= errno_from_name(rvalue
);
2468 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2469 "Failed to parse error number, ignoring: %s", rvalue
);
2473 c
->syscall_errno
= e
;
2477 int config_parse_address_families(
2479 const char *filename
,
2481 const char *section
,
2482 unsigned section_line
,
2489 ExecContext
*c
= data
;
2490 bool invert
= false;
2491 const char *word
, *state
;
2499 if (isempty(rvalue
)) {
2500 /* Empty assignment resets the list */
2501 c
->address_families
= set_free(c
->address_families
);
2502 c
->address_families_whitelist
= false;
2506 if (rvalue
[0] == '~') {
2511 if (!c
->address_families
) {
2512 c
->address_families
= set_new(NULL
);
2513 if (!c
->address_families
)
2516 c
->address_families_whitelist
= !invert
;
2519 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2520 _cleanup_free_
char *t
= NULL
;
2523 t
= strndup(word
, l
);
2527 af
= af_from_name(t
);
2529 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2530 "Failed to parse address family, ignoring: %s", t
);
2534 /* If we previously wanted to forbid an address family and now
2535 * we want to allow it, then remove it from the list
2537 if (!invert
== c
->address_families_whitelist
) {
2538 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2544 set_remove(c
->address_families
, INT_TO_PTR(af
));
2546 if (!isempty(state
))
2547 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2548 "Trailing garbage, ignoring.");
2554 int config_parse_unit_slice(
2556 const char *filename
,
2558 const char *section
,
2559 unsigned section_line
,
2566 _cleanup_free_
char *k
= NULL
;
2567 Unit
*u
= userdata
, *slice
= NULL
;
2575 r
= unit_name_printf(u
, rvalue
, &k
);
2577 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2581 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2583 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2587 r
= unit_set_slice(u
, slice
);
2589 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2596 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2598 int config_parse_cpu_shares(
2600 const char *filename
,
2602 const char *section
,
2603 unsigned section_line
,
2610 uint64_t *shares
= data
;
2617 r
= cg_cpu_shares_parse(rvalue
, shares
);
2619 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2626 int config_parse_cpu_quota(
2628 const char *filename
,
2630 const char *section
,
2631 unsigned section_line
,
2638 CGroupContext
*c
= data
;
2645 if (isempty(rvalue
)) {
2646 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2650 if (!endswith(rvalue
, "%")) {
2652 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2653 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2657 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2658 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2659 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2663 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2668 int config_parse_memory_limit(
2670 const char *filename
,
2672 const char *section
,
2673 unsigned section_line
,
2680 CGroupContext
*c
= data
;
2684 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2685 c
->memory_limit
= (uint64_t) -1;
2689 r
= parse_size(rvalue
, 1024, &bytes
);
2690 if (r
< 0 || bytes
< 1) {
2691 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2695 c
->memory_limit
= bytes
;
2699 int config_parse_tasks_max(
2701 const char *filename
,
2703 const char *section
,
2704 unsigned section_line
,
2711 CGroupContext
*c
= data
;
2715 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2716 c
->tasks_max
= (uint64_t) -1;
2720 r
= safe_atou64(rvalue
, &u
);
2721 if (r
< 0 || u
< 1) {
2722 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2729 int config_parse_device_allow(
2731 const char *filename
,
2733 const char *section
,
2734 unsigned section_line
,
2741 _cleanup_free_
char *path
= NULL
;
2742 CGroupContext
*c
= data
;
2743 CGroupDeviceAllow
*a
;
2747 if (isempty(rvalue
)) {
2748 while (c
->device_allow
)
2749 cgroup_context_free_device_allow(c
, c
->device_allow
);
2754 n
= strcspn(rvalue
, WHITESPACE
);
2755 path
= strndup(rvalue
, n
);
2759 if (!startswith(path
, "/dev/") &&
2760 !startswith(path
, "block-") &&
2761 !startswith(path
, "char-")) {
2762 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2763 "Invalid device node path '%s'. Ignoring.", path
);
2767 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2771 if (!in_charset(m
, "rwm")) {
2772 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2773 "Invalid device rights '%s'. Ignoring.", m
);
2777 a
= new0(CGroupDeviceAllow
, 1);
2783 a
->r
= !!strchr(m
, 'r');
2784 a
->w
= !!strchr(m
, 'w');
2785 a
->m
= !!strchr(m
, 'm');
2787 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2791 int config_parse_blockio_weight(
2793 const char *filename
,
2795 const char *section
,
2796 unsigned section_line
,
2803 uint64_t *weight
= data
;
2810 r
= cg_blkio_weight_parse(rvalue
, weight
);
2812 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2819 int config_parse_blockio_device_weight(
2821 const char *filename
,
2823 const char *section
,
2824 unsigned section_line
,
2831 _cleanup_free_
char *path
= NULL
;
2832 CGroupBlockIODeviceWeight
*w
;
2833 CGroupContext
*c
= data
;
2843 if (isempty(rvalue
)) {
2844 while (c
->blockio_device_weights
)
2845 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2850 n
= strcspn(rvalue
, WHITESPACE
);
2851 weight
= rvalue
+ n
;
2852 weight
+= strspn(weight
, WHITESPACE
);
2854 if (isempty(weight
)) {
2855 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Expected block device and device weight. Ignoring.");
2859 path
= strndup(rvalue
, n
);
2863 if (!path_startswith(path
, "/dev")) {
2864 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid device node path '%s'. Ignoring.", path
);
2868 r
= cg_blkio_weight_parse(weight
, &u
);
2870 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2874 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2876 w
= new0(CGroupBlockIODeviceWeight
, 1);
2885 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2889 int config_parse_blockio_bandwidth(
2891 const char *filename
,
2893 const char *section
,
2894 unsigned section_line
,
2901 _cleanup_free_
char *path
= NULL
;
2902 CGroupBlockIODeviceBandwidth
*b
;
2903 CGroupContext
*c
= data
;
2904 const char *bandwidth
;
2914 read
= streq("BlockIOReadBandwidth", lvalue
);
2916 if (isempty(rvalue
)) {
2917 CGroupBlockIODeviceBandwidth
*next
;
2919 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2920 if (b
->read
== read
)
2921 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2926 n
= strcspn(rvalue
, WHITESPACE
);
2927 bandwidth
= rvalue
+ n
;
2928 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2931 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2932 "Expected space separated pair of device node and bandwidth. Ignoring.");
2936 path
= strndup(rvalue
, n
);
2940 if (!path_startswith(path
, "/dev")) {
2941 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2942 "Invalid device node path '%s'. Ignoring.", path
);
2946 r
= parse_size(bandwidth
, 1000, &bytes
);
2947 if (r
< 0 || bytes
<= 0) {
2948 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2949 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2953 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2959 b
->bandwidth
= bytes
;
2962 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2967 int config_parse_netclass(
2969 const char *filename
,
2971 const char *section
,
2972 unsigned section_line
,
2979 CGroupContext
*c
= data
;
2987 if (streq(rvalue
, "auto")) {
2988 c
->netclass_type
= CGROUP_NETCLASS_TYPE_AUTO
;
2992 r
= safe_atou32(rvalue
, &v
);
2994 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2995 "Netclass '%s' invalid. Ignoring.", rvalue
);
2999 if (v
> CGROUP_NETCLASS_FIXED_MAX
)
3000 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3001 "Fixed netclass %" PRIu32
" out of allowed range (0-%d). Applying anyway.", v
, (uint32_t) CGROUP_NETCLASS_FIXED_MAX
);
3004 c
->netclass_type
= CGROUP_NETCLASS_TYPE_FIXED
;
3009 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3011 int config_parse_job_mode_isolate(
3013 const char *filename
,
3015 const char *section
,
3016 unsigned section_line
,
3030 r
= parse_boolean(rvalue
);
3032 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3033 "Failed to parse boolean, ignoring: %s", rvalue
);
3037 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3041 int config_parse_runtime_directory(
3043 const char *filename
,
3045 const char *section
,
3046 unsigned section_line
,
3055 const char *word
, *state
;
3064 if (isempty(rvalue
)) {
3065 /* Empty assignment resets the list */
3066 *rt
= strv_free(*rt
);
3070 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3071 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3073 t
= strndup(word
, l
);
3077 r
= unit_name_printf(u
, t
, &n
);
3079 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3080 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
3084 if (!filename_is_valid(n
)) {
3085 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3086 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3090 r
= strv_push(rt
, n
);
3096 if (!isempty(state
))
3097 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3098 "Trailing garbage, ignoring.");
3103 int config_parse_set_status(
3105 const char *filename
,
3107 const char *section
,
3108 unsigned section_line
,
3116 const char *word
, *state
;
3118 ExitStatusSet
*status_set
= data
;
3125 /* Empty assignment resets the list */
3126 if (isempty(rvalue
)) {
3127 exit_status_set_free(status_set
);
3131 FOREACH_WORD(word
, l
, rvalue
, state
) {
3132 _cleanup_free_
char *temp
;
3136 temp
= strndup(word
, l
);
3140 r
= safe_atoi(temp
, &val
);
3142 val
= signal_from_string_try_harder(temp
);
3145 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3146 "Failed to parse value, ignoring: %s", word
);
3149 set
= &status_set
->signal
;
3151 if (val
< 0 || val
> 255) {
3152 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3153 "Value %d is outside range 0-255, ignoring", val
);
3156 set
= &status_set
->status
;
3159 r
= set_ensure_allocated(set
, NULL
);
3163 r
= set_put(*set
, INT_TO_PTR(val
));
3165 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3166 "Unable to store: %s", word
);
3170 if (!isempty(state
))
3171 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3172 "Trailing garbage, ignoring.");
3177 int config_parse_namespace_path_strv(
3179 const char *filename
,
3181 const char *section
,
3182 unsigned section_line
,
3190 const char *word
, *state
;
3199 if (isempty(rvalue
)) {
3200 /* Empty assignment resets the list */
3201 *sv
= strv_free(*sv
);
3205 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3206 _cleanup_free_
char *n
;
3209 n
= strndup(word
, l
);
3213 if (!utf8_is_valid(n
)) {
3214 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3218 offset
= n
[0] == '-';
3219 if (!path_is_absolute(n
+ offset
)) {
3220 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3221 "Not an absolute path, ignoring: %s", rvalue
);
3225 path_kill_slashes(n
);
3227 r
= strv_push(sv
, n
);
3233 if (!isempty(state
))
3234 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3235 "Trailing garbage, ignoring.");
3240 int config_parse_no_new_privileges(
3242 const char *filename
,
3244 const char *section
,
3245 unsigned section_line
,
3252 ExecContext
*c
= data
;
3260 k
= parse_boolean(rvalue
);
3262 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3263 "Failed to parse boolean value, ignoring: %s", rvalue
);
3267 c
->no_new_privileges
= !!k
;
3268 c
->no_new_privileges_set
= true;
3273 int config_parse_protect_home(
3275 const char *filename
,
3277 const char *section
,
3278 unsigned section_line
,
3285 ExecContext
*c
= data
;
3293 /* Our enum shall be a superset of booleans, hence first try
3294 * to parse as as boolean, and then as enum */
3296 k
= parse_boolean(rvalue
);
3298 c
->protect_home
= PROTECT_HOME_YES
;
3300 c
->protect_home
= PROTECT_HOME_NO
;
3304 h
= protect_home_from_string(rvalue
);
3306 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3307 "Failed to parse protect home value, ignoring: %s", rvalue
);
3311 c
->protect_home
= h
;
3317 int config_parse_protect_system(
3319 const char *filename
,
3321 const char *section
,
3322 unsigned section_line
,
3329 ExecContext
*c
= data
;
3337 /* Our enum shall be a superset of booleans, hence first try
3338 * to parse as as boolean, and then as enum */
3340 k
= parse_boolean(rvalue
);
3342 c
->protect_system
= PROTECT_SYSTEM_YES
;
3344 c
->protect_system
= PROTECT_SYSTEM_NO
;
3348 s
= protect_system_from_string(rvalue
);
3350 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3351 "Failed to parse protect system value, ignoring: %s", rvalue
);
3355 c
->protect_system
= s
;
3361 #define FOLLOW_MAX 8
3363 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3374 /* This will update the filename pointer if the loaded file is
3375 * reached by a symlink. The old string will be freed. */
3378 char *target
, *name
;
3380 if (c
++ >= FOLLOW_MAX
)
3383 path_kill_slashes(*filename
);
3385 /* Add the file name we are currently looking at to
3386 * the names of this unit, but only if it is a valid
3388 name
= basename(*filename
);
3390 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3392 id
= set_get(names
, name
);
3398 r
= set_consume(names
, id
);
3404 /* Try to open the file name, but don't if its a symlink */
3405 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3412 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3413 r
= readlink_and_make_absolute(*filename
, &target
);
3421 f
= fdopen(fd
, "re");
3432 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3440 /* Let's try to add in all symlink names we found */
3441 while ((k
= set_steal_first(names
))) {
3443 /* First try to merge in the other name into our
3445 r
= unit_merge_by_name(*u
, k
);
3449 /* Hmm, we couldn't merge the other unit into
3450 * ours? Then let's try it the other way
3453 other
= manager_get_unit((*u
)->manager
, k
);
3457 r
= unit_merge(other
, *u
);
3460 return merge_by_names(u
, names
, NULL
);
3468 unit_choose_id(*u
, id
);
3476 static int load_from_path(Unit
*u
, const char *path
) {
3478 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3479 _cleanup_fclose_
FILE *f
= NULL
;
3480 _cleanup_free_
char *filename
= NULL
;
3488 symlink_names
= set_new(&string_hash_ops
);
3492 if (path_is_absolute(path
)) {
3494 filename
= strdup(path
);
3498 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3500 filename
= mfree(filename
);
3508 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3510 /* Instead of opening the path right away, we manually
3511 * follow all symlinks and add their name to our unit
3512 * name set while doing so */
3513 filename
= path_make_absolute(path
, *p
);
3517 if (u
->manager
->unit_path_cache
&&
3518 !set_get(u
->manager
->unit_path_cache
, filename
))
3521 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3524 filename
= mfree(filename
);
3528 /* Empty the symlink names for the next run */
3529 set_clear_free(symlink_names
);
3538 /* Hmm, no suitable file found? */
3542 r
= merge_by_names(&merged
, symlink_names
, id
);
3547 u
->load_state
= UNIT_MERGED
;
3551 if (fstat(fileno(f
), &st
) < 0)
3554 if (null_or_empty(&st
))
3555 u
->load_state
= UNIT_MASKED
;
3557 u
->load_state
= UNIT_LOADED
;
3559 /* Now, parse the file contents */
3560 r
= config_parse(u
->id
, filename
, f
,
3561 UNIT_VTABLE(u
)->sections
,
3562 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3563 false, true, false, u
);
3568 free(u
->fragment_path
);
3569 u
->fragment_path
= filename
;
3572 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3574 if (u
->source_path
) {
3575 if (stat(u
->source_path
, &st
) >= 0)
3576 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3578 u
->source_mtime
= 0;
3584 int unit_load_fragment(Unit
*u
) {
3590 assert(u
->load_state
== UNIT_STUB
);
3594 u
->load_state
= UNIT_LOADED
;
3598 /* First, try to find the unit under its id. We always look
3599 * for unit files in the default directories, to make it easy
3600 * to override things by placing things in /etc/systemd/system */
3601 r
= load_from_path(u
, u
->id
);
3605 /* Try to find an alias we can load this with */
3606 if (u
->load_state
== UNIT_STUB
) {
3607 SET_FOREACH(t
, u
->names
, i
) {
3612 r
= load_from_path(u
, t
);
3616 if (u
->load_state
!= UNIT_STUB
)
3621 /* And now, try looking for it under the suggested (originally linked) path */
3622 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3624 r
= load_from_path(u
, u
->fragment_path
);
3628 if (u
->load_state
== UNIT_STUB
)
3629 /* Hmm, this didn't work? Then let's get rid
3630 * of the fragment path stored for us, so that
3631 * we don't point to an invalid location. */
3632 u
->fragment_path
= mfree(u
->fragment_path
);
3635 /* Look for a template */
3636 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3637 _cleanup_free_
char *k
= NULL
;
3639 r
= unit_name_template(u
->id
, &k
);
3643 r
= load_from_path(u
, k
);
3647 if (u
->load_state
== UNIT_STUB
) {
3648 SET_FOREACH(t
, u
->names
, i
) {
3649 _cleanup_free_
char *z
= NULL
;
3654 r
= unit_name_template(t
, &z
);
3658 r
= load_from_path(u
, z
);
3662 if (u
->load_state
!= UNIT_STUB
)
3671 void unit_dump_config_items(FILE *f
) {
3672 static const struct {
3673 const ConfigParserCallback callback
;
3676 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3677 { config_parse_warn_compat
, "NOTSUPPORTED" },
3679 { config_parse_int
, "INTEGER" },
3680 { config_parse_unsigned
, "UNSIGNED" },
3681 { config_parse_iec_size
, "SIZE" },
3682 { config_parse_iec_uint64
, "SIZE" },
3683 { config_parse_si_size
, "SIZE" },
3684 { config_parse_bool
, "BOOLEAN" },
3685 { config_parse_string
, "STRING" },
3686 { config_parse_path
, "PATH" },
3687 { config_parse_unit_path_printf
, "PATH" },
3688 { config_parse_strv
, "STRING [...]" },
3689 { config_parse_exec_nice
, "NICE" },
3690 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3691 { config_parse_exec_io_class
, "IOCLASS" },
3692 { config_parse_exec_io_priority
, "IOPRIORITY" },
3693 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3694 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3695 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3696 { config_parse_mode
, "MODE" },
3697 { config_parse_unit_env_file
, "FILE" },
3698 { config_parse_output
, "OUTPUT" },
3699 { config_parse_input
, "INPUT" },
3700 { config_parse_log_facility
, "FACILITY" },
3701 { config_parse_log_level
, "LEVEL" },
3702 { config_parse_exec_capabilities
, "CAPABILITIES" },
3703 { config_parse_exec_secure_bits
, "SECUREBITS" },
3704 { config_parse_bounding_set
, "BOUNDINGSET" },
3705 { config_parse_limit
, "LIMIT" },
3706 { config_parse_unit_deps
, "UNIT [...]" },
3707 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3708 { config_parse_service_type
, "SERVICETYPE" },
3709 { config_parse_service_restart
, "SERVICERESTART" },
3710 #ifdef HAVE_SYSV_COMPAT
3711 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3713 { config_parse_kill_mode
, "KILLMODE" },
3714 { config_parse_signal
, "SIGNAL" },
3715 { config_parse_socket_listen
, "SOCKET [...]" },
3716 { config_parse_socket_bind
, "SOCKETBIND" },
3717 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3718 { config_parse_sec
, "SECONDS" },
3719 { config_parse_nsec
, "NANOSECONDS" },
3720 { config_parse_namespace_path_strv
, "PATH [...]" },
3721 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3722 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3723 { config_parse_unit_string_printf
, "STRING" },
3724 { config_parse_trigger_unit
, "UNIT" },
3725 { config_parse_timer
, "TIMER" },
3726 { config_parse_path_spec
, "PATH" },
3727 { config_parse_notify_access
, "ACCESS" },
3728 { config_parse_ip_tos
, "TOS" },
3729 { config_parse_unit_condition_path
, "CONDITION" },
3730 { config_parse_unit_condition_string
, "CONDITION" },
3731 { config_parse_unit_condition_null
, "CONDITION" },
3732 { config_parse_unit_slice
, "SLICE" },
3733 { config_parse_documentation
, "URL" },
3734 { config_parse_service_timeout
, "SECONDS" },
3735 { config_parse_failure_action
, "ACTION" },
3736 { config_parse_set_status
, "STATUS" },
3737 { config_parse_service_sockets
, "SOCKETS" },
3738 { config_parse_environ
, "ENVIRON" },
3740 { config_parse_syscall_filter
, "SYSCALLS" },
3741 { config_parse_syscall_archs
, "ARCHS" },
3742 { config_parse_syscall_errno
, "ERRNO" },
3743 { config_parse_address_families
, "FAMILIES" },
3745 { config_parse_cpu_shares
, "SHARES" },
3746 { config_parse_memory_limit
, "LIMIT" },
3747 { config_parse_device_allow
, "DEVICE" },
3748 { config_parse_device_policy
, "POLICY" },
3749 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3750 { config_parse_blockio_weight
, "WEIGHT" },
3751 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3752 { config_parse_long
, "LONG" },
3753 { config_parse_socket_service
, "SERVICE" },
3755 { config_parse_exec_selinux_context
, "LABEL" },
3757 { config_parse_job_mode
, "MODE" },
3758 { config_parse_job_mode_isolate
, "BOOLEAN" },
3759 { config_parse_personality
, "PERSONALITY" },
3762 const char *prev
= NULL
;
3767 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3768 const char *rvalue
= "OTHER", *lvalue
;
3772 const ConfigPerfItem
*p
;
3774 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3776 dot
= strchr(i
, '.');
3777 lvalue
= dot
? dot
+ 1 : i
;
3781 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3785 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3788 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3789 if (p
->parse
== table
[j
].callback
) {
3790 rvalue
= table
[j
].rvalue
;
3794 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);