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
);
387 LIST_FIND_TAIL(port
, s
->ports
, tail
);
388 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
390 LIST_PREPEND(port
, s
->ports
, p
);
396 int config_parse_socket_bind(const char *unit
,
397 const char *filename
,
400 unsigned section_line
,
408 SocketAddressBindIPv6Only b
;
417 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
421 r
= parse_boolean(rvalue
);
423 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
424 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
428 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
430 s
->bind_ipv6_only
= b
;
435 int config_parse_exec_nice(const char *unit
,
436 const char *filename
,
439 unsigned section_line
,
446 ExecContext
*c
= data
;
454 r
= safe_atoi(rvalue
, &priority
);
456 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
457 "Failed to parse nice priority, ignoring: %s. ", rvalue
);
461 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
462 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
463 "Nice priority out of range, ignoring: %s", rvalue
);
473 int config_parse_exec_oom_score_adjust(const char* unit
,
474 const char *filename
,
477 unsigned section_line
,
484 ExecContext
*c
= data
;
492 r
= safe_atoi(rvalue
, &oa
);
494 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
495 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
499 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
500 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
501 "OOM score adjust value out of range, ignoring: %s", rvalue
);
505 c
->oom_score_adjust
= oa
;
506 c
->oom_score_adjust_set
= true;
511 int config_parse_exec(
513 const char *filename
,
516 unsigned section_line
,
523 ExecCommand
**e
= data
;
535 rvalue
+= strspn(rvalue
, WHITESPACE
);
538 if (isempty(rvalue
)) {
539 /* An empty assignment resets the list */
540 *e
= exec_command_free_list(*e
);
546 _cleanup_strv_free_
char **n
= NULL
;
547 size_t nlen
= 0, nbufsize
= 0;
548 _cleanup_free_ ExecCommand
*nce
= NULL
;
549 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
551 bool separate_argv0
= false, ignore
= false;
555 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
560 for (i
= 0; i
< 2; i
++) {
561 /* We accept an absolute path as first argument, or
562 * alternatively an absolute prefixed with @ to allow
563 * overriding of argv[0]. */
564 if (*f
== '-' && !ignore
)
566 else if (*f
== '@' && !separate_argv0
)
567 separate_argv0
= true;
574 /* First word is either "-" or "@" with no command. */
575 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
576 "Empty path in command line, ignoring: \"%s\"", rvalue
);
580 if (!string_is_safe(f
)) {
581 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
582 "Executable path contains special characters, ignoring: %s", rvalue
);
585 if (!path_is_absolute(f
)) {
586 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
587 "Executable path is not absolute, ignoring: %s", rvalue
);
590 if (endswith(f
, "/")) {
591 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
592 "Executable path specifies a directory, ignoring: %s", rvalue
);
596 if (f
== firstword
) {
605 if (!separate_argv0
) {
606 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
615 path_kill_slashes(path
);
617 while (!isempty(p
)) {
618 _cleanup_free_
char *word
= NULL
;
620 /* Check explicitly for an unquoted semicolon as
621 * command separator token. */
622 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
624 p
+= strspn(p
, WHITESPACE
);
629 /* Check for \; explicitly, to not confuse it with \\;
630 * or "\;" or "\\;" etc. extract_first_word would
631 * return the same for all of those. */
632 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
634 p
+= strspn(p
, WHITESPACE
);
635 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
645 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
651 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
659 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
660 "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
664 nce
= new0(ExecCommand
, 1);
670 nce
->ignore
= ignore
;
672 exec_command_append_list(e
, nce
);
674 /* Do not _cleanup_free_ these. */
685 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
686 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
688 int config_parse_socket_bindtodevice(const char* unit
,
689 const char *filename
,
692 unsigned section_line
,
707 if (rvalue
[0] && !streq(rvalue
, "*")) {
714 free(s
->bind_to_device
);
715 s
->bind_to_device
= n
;
720 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
721 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
723 int config_parse_exec_io_class(const char *unit
,
724 const char *filename
,
727 unsigned section_line
,
734 ExecContext
*c
= data
;
742 x
= ioprio_class_from_string(rvalue
);
744 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
745 "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
749 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
750 c
->ioprio_set
= true;
755 int config_parse_exec_io_priority(const char *unit
,
756 const char *filename
,
759 unsigned section_line
,
766 ExecContext
*c
= data
;
774 r
= safe_atoi(rvalue
, &i
);
775 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
776 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
777 "Failed to parse IO priority, ignoring: %s", rvalue
);
781 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
782 c
->ioprio_set
= true;
787 int config_parse_exec_cpu_sched_policy(const char *unit
,
788 const char *filename
,
791 unsigned section_line
,
799 ExecContext
*c
= data
;
807 x
= sched_policy_from_string(rvalue
);
809 log_syntax(unit
, LOG_ERR
, filename
, line
, -x
,
810 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
814 c
->cpu_sched_policy
= x
;
815 /* Moving to or from real-time policy? We need to adjust the priority */
816 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
817 c
->cpu_sched_set
= true;
822 int config_parse_exec_cpu_sched_prio(const char *unit
,
823 const char *filename
,
826 unsigned section_line
,
833 ExecContext
*c
= data
;
841 r
= safe_atoi(rvalue
, &i
);
843 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
844 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
848 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
849 min
= sched_get_priority_min(c
->cpu_sched_policy
);
850 max
= sched_get_priority_max(c
->cpu_sched_policy
);
852 if (i
< min
|| i
> max
) {
853 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
854 "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
858 c
->cpu_sched_priority
= i
;
859 c
->cpu_sched_set
= true;
864 int config_parse_exec_cpu_affinity(const char *unit
,
865 const char *filename
,
868 unsigned section_line
,
875 ExecContext
*c
= data
;
876 const char *word
, *state
;
884 if (isempty(rvalue
)) {
885 /* An empty assignment resets the CPU list */
892 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
893 _cleanup_free_
char *t
= NULL
;
897 t
= strndup(word
, l
);
901 r
= safe_atou(t
, &cpu
);
904 c
->cpuset
= cpu_set_malloc(&c
->cpuset_ncpus
);
909 if (r
< 0 || cpu
>= c
->cpuset_ncpus
) {
910 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
911 "Failed to parse CPU affinity '%s', ignoring: %s", t
, rvalue
);
915 CPU_SET_S(cpu
, CPU_ALLOC_SIZE(c
->cpuset_ncpus
), c
->cpuset
);
918 log_syntax(unit
, LOG_WARNING
, filename
, line
, EINVAL
,
919 "Trailing garbage, ignoring.");
924 int config_parse_exec_capabilities(const char *unit
,
925 const char *filename
,
928 unsigned section_line
,
935 ExecContext
*c
= data
;
943 cap
= cap_from_text(rvalue
);
945 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
,
946 "Failed to parse capabilities, ignoring: %s", rvalue
);
951 cap_free(c
->capabilities
);
952 c
->capabilities
= cap
;
957 int config_parse_exec_secure_bits(const char *unit
,
958 const char *filename
,
961 unsigned section_line
,
968 ExecContext
*c
= data
;
970 const char *word
, *state
;
977 if (isempty(rvalue
)) {
978 /* An empty assignment resets the field */
983 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
984 if (first_word(word
, "keep-caps"))
985 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
986 else if (first_word(word
, "keep-caps-locked"))
987 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
988 else if (first_word(word
, "no-setuid-fixup"))
989 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
990 else if (first_word(word
, "no-setuid-fixup-locked"))
991 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
992 else if (first_word(word
, "noroot"))
993 c
->secure_bits
|= 1<<SECURE_NOROOT
;
994 else if (first_word(word
, "noroot-locked"))
995 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
997 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
998 "Failed to parse secure bits, ignoring: %s", rvalue
);
1002 if (!isempty(state
))
1003 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1004 "Invalid syntax, garbage at the end, ignoring.");
1009 int config_parse_bounding_set(const char *unit
,
1010 const char *filename
,
1012 const char *section
,
1013 unsigned section_line
,
1020 uint64_t *capability_bounding_set_drop
= data
;
1021 const char *word
, *state
;
1023 bool invert
= false;
1031 if (rvalue
[0] == '~') {
1036 /* Note that we store this inverted internally, since the
1037 * kernel wants it like this. But we actually expose it
1038 * non-inverted everywhere to have a fully normalized
1041 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1042 _cleanup_free_
char *t
= NULL
;
1045 t
= strndup(word
, l
);
1049 cap
= capability_from_name(t
);
1051 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
, "Failed to parse capability in bounding set, ignoring: %s", t
);
1055 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1057 if (!isempty(state
))
1058 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1059 "Trailing garbage, ignoring.");
1062 *capability_bounding_set_drop
|= sum
;
1064 *capability_bounding_set_drop
|= ~sum
;
1069 int config_parse_limit(const char *unit
,
1070 const char *filename
,
1072 const char *section
,
1073 unsigned section_line
,
1080 struct rlimit
**rl
= data
;
1081 unsigned long long u
;
1090 if (streq(rvalue
, "infinity"))
1091 u
= (unsigned long long) RLIM_INFINITY
;
1095 r
= safe_atollu(rvalue
, &u
);
1097 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1098 "Failed to parse resource value, ignoring: %s", rvalue
);
1104 *rl
= new(struct rlimit
, 1);
1109 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1113 #ifdef HAVE_SYSV_COMPAT
1114 int config_parse_sysv_priority(const char *unit
,
1115 const char *filename
,
1117 const char *section
,
1118 unsigned section_line
,
1125 int *priority
= data
;
1133 r
= safe_atoi(rvalue
, &i
);
1134 if (r
< 0 || i
< 0) {
1135 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1136 "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1140 *priority
= (int) i
;
1145 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1147 int config_parse_kill_signal(const char *unit
,
1148 const char *filename
,
1150 const char *section
,
1151 unsigned section_line
,
1166 r
= signal_from_string_try_harder(rvalue
);
1168 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1169 "Failed to parse kill signal, ignoring: %s", rvalue
);
1177 int config_parse_exec_mount_flags(const char *unit
,
1178 const char *filename
,
1180 const char *section
,
1181 unsigned section_line
,
1188 ExecContext
*c
= data
;
1189 const char *word
, *state
;
1191 unsigned long flags
= 0;
1198 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1199 _cleanup_free_
char *t
;
1201 t
= strndup(word
, l
);
1205 if (streq(t
, "shared"))
1207 else if (streq(t
, "slave"))
1209 else if (streq(t
, "private"))
1212 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1216 if (!isempty(state
))
1217 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1219 c
->mount_flags
= flags
;
1223 int config_parse_exec_selinux_context(
1225 const char *filename
,
1227 const char *section
,
1228 unsigned section_line
,
1235 ExecContext
*c
= data
;
1246 if (isempty(rvalue
)) {
1247 free(c
->selinux_context
);
1248 c
->selinux_context
= NULL
;
1249 c
->selinux_context_ignore
= false;
1253 if (rvalue
[0] == '-') {
1259 r
= unit_name_printf(u
, rvalue
, &k
);
1261 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1262 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1266 free(c
->selinux_context
);
1267 c
->selinux_context
= k
;
1268 c
->selinux_context_ignore
= ignore
;
1273 int config_parse_exec_apparmor_profile(
1275 const char *filename
,
1277 const char *section
,
1278 unsigned section_line
,
1285 ExecContext
*c
= data
;
1296 if (isempty(rvalue
)) {
1297 free(c
->apparmor_profile
);
1298 c
->apparmor_profile
= NULL
;
1299 c
->apparmor_profile_ignore
= false;
1303 if (rvalue
[0] == '-') {
1309 r
= unit_name_printf(u
, rvalue
, &k
);
1311 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1312 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1316 free(c
->apparmor_profile
);
1317 c
->apparmor_profile
= k
;
1318 c
->apparmor_profile_ignore
= ignore
;
1323 int config_parse_exec_smack_process_label(
1325 const char *filename
,
1327 const char *section
,
1328 unsigned section_line
,
1335 ExecContext
*c
= data
;
1346 if (isempty(rvalue
)) {
1347 free(c
->smack_process_label
);
1348 c
->smack_process_label
= NULL
;
1349 c
->smack_process_label_ignore
= false;
1353 if (rvalue
[0] == '-') {
1359 r
= unit_name_printf(u
, rvalue
, &k
);
1361 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1362 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1366 free(c
->smack_process_label
);
1367 c
->smack_process_label
= k
;
1368 c
->smack_process_label_ignore
= ignore
;
1373 int config_parse_timer(const char *unit
,
1374 const char *filename
,
1376 const char *section
,
1377 unsigned section_line
,
1388 CalendarSpec
*c
= NULL
;
1395 if (isempty(rvalue
)) {
1396 /* Empty assignment resets list */
1397 timer_free_values(t
);
1401 b
= timer_base_from_string(lvalue
);
1403 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
,
1404 "Failed to parse timer base, ignoring: %s", lvalue
);
1408 if (b
== TIMER_CALENDAR
) {
1409 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1410 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1411 "Failed to parse calendar specification, ignoring: %s",
1416 if (parse_sec(rvalue
, &u
) < 0) {
1417 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1418 "Failed to parse timer value, ignoring: %s",
1424 v
= new0(TimerValue
, 1);
1426 calendar_spec_free(c
);
1432 v
->calendar_spec
= c
;
1434 LIST_PREPEND(value
, t
->values
, v
);
1439 int config_parse_trigger_unit(
1441 const char *filename
,
1443 const char *section
,
1444 unsigned section_line
,
1451 _cleanup_free_
char *p
= NULL
;
1461 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1462 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1463 "Multiple units to trigger specified, ignoring: %s", rvalue
);
1467 r
= unit_name_printf(u
, rvalue
, &p
);
1469 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1470 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1472 type
= unit_name_to_type(p
?: rvalue
);
1474 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1475 "Unit type not valid, ignoring: %s", rvalue
);
1479 if (type
== u
->type
) {
1480 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1481 "Trigger cannot be of same type, ignoring: %s", rvalue
);
1485 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
?: rvalue
, NULL
, true);
1487 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1488 "Failed to add trigger on %s, ignoring: %s", p
?: rvalue
, strerror(-r
));
1495 int config_parse_path_spec(const char *unit
,
1496 const char *filename
,
1498 const char *section
,
1499 unsigned section_line
,
1509 _cleanup_free_
char *k
= NULL
;
1517 if (isempty(rvalue
)) {
1518 /* Empty assignment clears list */
1523 b
= path_type_from_string(lvalue
);
1525 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1526 "Failed to parse path type, ignoring: %s", lvalue
);
1530 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1536 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1537 "Failed to resolve unit specifiers on %s. Ignoring.",
1541 if (!path_is_absolute(k
)) {
1542 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1543 "Path is not absolute, ignoring: %s", k
);
1547 s
= new0(PathSpec
, 1);
1552 s
->path
= path_kill_slashes(k
);
1557 LIST_PREPEND(spec
, p
->specs
, s
);
1562 int config_parse_socket_service(
1564 const char *filename
,
1566 const char *section
,
1567 unsigned section_line
,
1574 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1578 _cleanup_free_
char *p
= NULL
;
1585 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1587 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1591 if (!endswith(p
, ".service")) {
1592 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type service, ignoring: %s", rvalue
);
1596 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1598 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1602 unit_ref_set(&s
->service
, x
);
1607 int config_parse_service_sockets(
1609 const char *filename
,
1611 const char *section
,
1612 unsigned section_line
,
1620 const char *word
, *state
;
1629 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1630 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1632 t
= strndup(word
, l
);
1636 r
= unit_name_printf(UNIT(s
), t
, &k
);
1638 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1642 if (!endswith(k
, ".socket")) {
1643 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type socket, ignoring: %s", k
);
1647 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1649 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1651 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1653 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1655 if (!isempty(state
))
1656 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1661 int config_parse_bus_name(
1663 const char *filename
,
1665 const char *section
,
1666 unsigned section_line
,
1673 _cleanup_free_
char *k
= NULL
;
1682 r
= unit_full_printf(u
, rvalue
, &k
);
1684 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1688 if (!service_name_is_valid(k
)) {
1689 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bus name %s, ignoring.", k
);
1693 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1696 int config_parse_service_timeout(const char *unit
,
1697 const char *filename
,
1699 const char *section
,
1700 unsigned section_line
,
1707 Service
*s
= userdata
;
1715 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1716 rvalue
, data
, userdata
);
1720 if (streq(lvalue
, "TimeoutSec")) {
1721 s
->start_timeout_defined
= true;
1722 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1723 } else if (streq(lvalue
, "TimeoutStartSec"))
1724 s
->start_timeout_defined
= true;
1729 int config_parse_busname_service(
1731 const char *filename
,
1733 const char *section
,
1734 unsigned section_line
,
1741 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1745 _cleanup_free_
char *p
= NULL
;
1752 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1754 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1755 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1759 if (!endswith(p
, ".service")) {
1760 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1761 "Unit must be of type service, ignoring: %s", rvalue
);
1765 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1767 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1768 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1772 unit_ref_set(&n
->service
, x
);
1777 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1779 int config_parse_bus_policy(
1781 const char *filename
,
1783 const char *section
,
1784 unsigned section_line
,
1791 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1792 _cleanup_free_
char *id_str
= NULL
;
1793 BusName
*busname
= data
;
1801 p
= new0(BusNamePolicy
, 1);
1805 if (streq(lvalue
, "AllowUser"))
1806 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1807 else if (streq(lvalue
, "AllowGroup"))
1808 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1810 assert_not_reached("Unknown lvalue");
1812 id_str
= strdup(rvalue
);
1816 access_str
= strpbrk(id_str
, WHITESPACE
);
1818 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1819 "Invalid busname policy value '%s'", rvalue
);
1825 access_str
+= strspn(access_str
, WHITESPACE
);
1827 p
->access
= bus_policy_access_from_string(access_str
);
1828 if (p
->access
< 0) {
1829 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1830 "Invalid busname policy access type '%s'", access_str
);
1837 LIST_PREPEND(policy
, busname
->policy
, p
);
1843 int config_parse_bus_endpoint_policy(
1845 const char *filename
,
1847 const char *section
,
1848 unsigned section_line
,
1855 _cleanup_free_
char *name
= NULL
;
1856 BusPolicyAccess access
;
1857 ExecContext
*c
= data
;
1866 name
= strdup(rvalue
);
1870 access_str
= strpbrk(name
, WHITESPACE
);
1872 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1873 "Invalid endpoint policy value '%s'", rvalue
);
1879 access_str
+= strspn(access_str
, WHITESPACE
);
1881 access
= bus_policy_access_from_string(access_str
);
1882 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1883 access
>= _BUS_POLICY_ACCESS_MAX
) {
1884 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1885 "Invalid endpoint policy access type '%s'", access_str
);
1889 if (!c
->bus_endpoint
) {
1890 r
= bus_endpoint_new(&c
->bus_endpoint
);
1896 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1899 int config_parse_unit_env_file(const char *unit
,
1900 const char *filename
,
1902 const char *section
,
1903 unsigned section_line
,
1912 _cleanup_free_
char *n
= NULL
;
1921 if (isempty(rvalue
)) {
1922 /* Empty assignment frees the list */
1928 r
= unit_full_printf(u
, rvalue
, &n
);
1930 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1931 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1934 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1935 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1936 "Path '%s' is not absolute, ignoring.", s
);
1940 r
= strv_extend(env
, s
);
1947 int config_parse_environ(const char *unit
,
1948 const char *filename
,
1950 const char *section
,
1951 unsigned section_line
,
1960 const char *word
, *state
;
1962 _cleanup_free_
char *k
= NULL
;
1970 if (isempty(rvalue
)) {
1971 /* Empty assignment resets the list */
1978 r
= unit_full_printf(u
, rvalue
, &k
);
1980 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1988 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1989 _cleanup_free_
char *n
= NULL
;
1992 r
= cunescape_length(word
, l
, 0, &n
);
1994 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
1998 if (!env_assignment_is_valid(n
)) {
1999 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid environment assignment, ignoring: %s", rvalue
);
2003 x
= strv_env_set(*env
, n
);
2010 if (!isempty(state
))
2011 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2012 "Trailing garbage, ignoring.");
2017 int config_parse_ip_tos(const char *unit
,
2018 const char *filename
,
2020 const char *section
,
2021 unsigned section_line
,
2028 int *ip_tos
= data
, x
;
2035 x
= ip_tos_from_string(rvalue
);
2037 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2038 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2046 int config_parse_unit_condition_path(
2048 const char *filename
,
2050 const char *section
,
2051 unsigned section_line
,
2058 _cleanup_free_
char *p
= NULL
;
2059 Condition
**list
= data
, *c
;
2060 ConditionType t
= ltype
;
2061 bool trigger
, negate
;
2070 if (isempty(rvalue
)) {
2071 /* Empty assignment resets the list */
2072 *list
= condition_free_list(*list
);
2076 trigger
= rvalue
[0] == '|';
2080 negate
= rvalue
[0] == '!';
2084 r
= unit_full_printf(u
, rvalue
, &p
);
2086 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2090 if (!path_is_absolute(p
)) {
2091 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2095 c
= condition_new(t
, p
, trigger
, negate
);
2099 LIST_PREPEND(conditions
, *list
, c
);
2103 int config_parse_unit_condition_string(
2105 const char *filename
,
2107 const char *section
,
2108 unsigned section_line
,
2115 _cleanup_free_
char *s
= NULL
;
2116 Condition
**list
= data
, *c
;
2117 ConditionType t
= ltype
;
2118 bool trigger
, negate
;
2127 if (isempty(rvalue
)) {
2128 /* Empty assignment resets the list */
2129 *list
= condition_free_list(*list
);
2133 trigger
= rvalue
[0] == '|';
2137 negate
= rvalue
[0] == '!';
2141 r
= unit_full_printf(u
, rvalue
, &s
);
2143 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2147 c
= condition_new(t
, s
, trigger
, negate
);
2151 LIST_PREPEND(conditions
, *list
, c
);
2155 int config_parse_unit_condition_null(
2157 const char *filename
,
2159 const char *section
,
2160 unsigned section_line
,
2167 Condition
**list
= data
, *c
;
2168 bool trigger
, negate
;
2176 if (isempty(rvalue
)) {
2177 /* Empty assignment resets the list */
2178 *list
= condition_free_list(*list
);
2182 trigger
= rvalue
[0] == '|';
2186 negate
= rvalue
[0] == '!';
2190 b
= parse_boolean(rvalue
);
2192 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2199 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2203 LIST_PREPEND(conditions
, *list
, c
);
2207 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2208 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2210 int config_parse_unit_requires_mounts_for(
2212 const char *filename
,
2214 const char *section
,
2215 unsigned section_line
,
2223 const char *word
, *state
;
2231 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2233 _cleanup_free_
char *n
;
2235 n
= strndup(word
, l
);
2239 if (!utf8_is_valid(n
)) {
2240 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2244 r
= unit_require_mounts_for(u
, n
);
2246 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2247 "Failed to add required mount for, ignoring: %s", rvalue
);
2251 if (!isempty(state
))
2252 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2253 "Trailing garbage, ignoring.");
2258 int config_parse_documentation(const char *unit
,
2259 const char *filename
,
2261 const char *section
,
2262 unsigned section_line
,
2278 if (isempty(rvalue
)) {
2279 /* Empty assignment resets the list */
2280 strv_free(u
->documentation
);
2281 u
->documentation
= NULL
;
2285 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2286 rvalue
, data
, userdata
);
2290 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2292 if (documentation_url_is_valid(*a
))
2295 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2296 "Invalid URL, ignoring: %s", *a
);
2307 int config_parse_syscall_filter(
2309 const char *filename
,
2311 const char *section
,
2312 unsigned section_line
,
2319 static const char default_syscalls
[] =
2326 ExecContext
*c
= data
;
2328 bool invert
= false;
2329 const char *word
, *state
;
2338 if (isempty(rvalue
)) {
2339 /* Empty assignment resets the list */
2340 set_free(c
->syscall_filter
);
2341 c
->syscall_filter
= NULL
;
2342 c
->syscall_whitelist
= false;
2346 if (rvalue
[0] == '~') {
2351 if (!c
->syscall_filter
) {
2352 c
->syscall_filter
= set_new(NULL
);
2353 if (!c
->syscall_filter
)
2357 /* Allow everything but the ones listed */
2358 c
->syscall_whitelist
= false;
2362 /* Allow nothing but the ones listed */
2363 c
->syscall_whitelist
= true;
2365 /* Accept default syscalls if we are on a whitelist */
2366 NULSTR_FOREACH(i
, default_syscalls
) {
2369 id
= seccomp_syscall_resolve_name(i
);
2373 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2382 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2383 _cleanup_free_
char *t
= NULL
;
2386 t
= strndup(word
, l
);
2390 id
= seccomp_syscall_resolve_name(t
);
2392 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2393 "Failed to parse system call, ignoring: %s", t
);
2397 /* If we previously wanted to forbid a syscall and now
2398 * we want to allow it, then remove it from the list
2400 if (!invert
== c
->syscall_whitelist
) {
2401 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2407 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2409 if (!isempty(state
))
2410 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2411 "Trailing garbage, ignoring.");
2413 /* Turn on NNP, but only if it wasn't configured explicitly
2414 * before, and only if we are in user mode. */
2415 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2416 c
->no_new_privileges
= true;
2421 int config_parse_syscall_archs(
2423 const char *filename
,
2425 const char *section
,
2426 unsigned section_line
,
2434 const char *word
, *state
;
2438 if (isempty(rvalue
)) {
2444 r
= set_ensure_allocated(archs
, NULL
);
2448 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2449 _cleanup_free_
char *t
= NULL
;
2452 t
= strndup(word
, l
);
2456 r
= seccomp_arch_from_string(t
, &a
);
2458 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2459 "Failed to parse system call architecture, ignoring: %s", t
);
2463 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2469 if (!isempty(state
))
2470 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2471 "Trailing garbage, ignoring.");
2476 int config_parse_syscall_errno(
2478 const char *filename
,
2480 const char *section
,
2481 unsigned section_line
,
2488 ExecContext
*c
= data
;
2495 if (isempty(rvalue
)) {
2496 /* Empty assignment resets to KILL */
2497 c
->syscall_errno
= 0;
2501 e
= errno_from_name(rvalue
);
2503 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2504 "Failed to parse error number, ignoring: %s", rvalue
);
2508 c
->syscall_errno
= e
;
2512 int config_parse_address_families(
2514 const char *filename
,
2516 const char *section
,
2517 unsigned section_line
,
2524 ExecContext
*c
= data
;
2525 bool invert
= false;
2526 const char *word
, *state
;
2534 if (isempty(rvalue
)) {
2535 /* Empty assignment resets the list */
2536 set_free(c
->address_families
);
2537 c
->address_families
= NULL
;
2538 c
->address_families_whitelist
= false;
2542 if (rvalue
[0] == '~') {
2547 if (!c
->address_families
) {
2548 c
->address_families
= set_new(NULL
);
2549 if (!c
->address_families
)
2552 c
->address_families_whitelist
= !invert
;
2555 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2556 _cleanup_free_
char *t
= NULL
;
2559 t
= strndup(word
, l
);
2563 af
= af_from_name(t
);
2565 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2566 "Failed to parse address family, ignoring: %s", t
);
2570 /* If we previously wanted to forbid an address family and now
2571 * we want to allow it, then remove it from the list
2573 if (!invert
== c
->address_families_whitelist
) {
2574 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2580 set_remove(c
->address_families
, INT_TO_PTR(af
));
2582 if (!isempty(state
))
2583 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2584 "Trailing garbage, ignoring.");
2590 int config_parse_unit_slice(
2592 const char *filename
,
2594 const char *section
,
2595 unsigned section_line
,
2602 _cleanup_free_
char *k
= NULL
;
2603 Unit
*u
= userdata
, *slice
;
2611 r
= unit_name_printf(u
, rvalue
, &k
);
2613 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2614 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2621 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2623 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2624 "Failed to load slice unit %s. Ignoring.", k
);
2628 if (slice
->type
!= UNIT_SLICE
) {
2629 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2630 "Slice unit %s is not a slice. Ignoring.", k
);
2634 unit_ref_set(&u
->slice
, slice
);
2638 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2640 int config_parse_cpu_shares(
2642 const char *filename
,
2644 const char *section
,
2645 unsigned section_line
,
2652 unsigned long *shares
= data
, lu
;
2659 if (isempty(rvalue
)) {
2660 *shares
= (unsigned long) -1;
2664 r
= safe_atolu(rvalue
, &lu
);
2665 if (r
< 0 || lu
<= 0) {
2666 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2667 "CPU shares '%s' invalid. Ignoring.", rvalue
);
2675 int config_parse_cpu_quota(
2677 const char *filename
,
2679 const char *section
,
2680 unsigned section_line
,
2687 CGroupContext
*c
= data
;
2694 if (isempty(rvalue
)) {
2695 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2699 if (!endswith(rvalue
, "%")) {
2701 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2702 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2706 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2707 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2708 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2712 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2717 int config_parse_memory_limit(
2719 const char *filename
,
2721 const char *section
,
2722 unsigned section_line
,
2729 CGroupContext
*c
= data
;
2733 if (isempty(rvalue
)) {
2734 c
->memory_limit
= (uint64_t) -1;
2738 assert_cc(sizeof(uint64_t) == sizeof(off_t
));
2740 r
= parse_size(rvalue
, 1024, &bytes
);
2742 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2743 "Memory limit '%s' invalid. Ignoring.", rvalue
);
2747 c
->memory_limit
= (uint64_t) bytes
;
2751 int config_parse_device_allow(
2753 const char *filename
,
2755 const char *section
,
2756 unsigned section_line
,
2763 _cleanup_free_
char *path
= NULL
;
2764 CGroupContext
*c
= data
;
2765 CGroupDeviceAllow
*a
;
2769 if (isempty(rvalue
)) {
2770 while (c
->device_allow
)
2771 cgroup_context_free_device_allow(c
, c
->device_allow
);
2776 n
= strcspn(rvalue
, WHITESPACE
);
2777 path
= strndup(rvalue
, n
);
2781 if (!startswith(path
, "/dev/") &&
2782 !startswith(path
, "block-") &&
2783 !startswith(path
, "char-")) {
2784 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2785 "Invalid device node path '%s'. Ignoring.", path
);
2789 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2793 if (!in_charset(m
, "rwm")) {
2794 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2795 "Invalid device rights '%s'. Ignoring.", m
);
2799 a
= new0(CGroupDeviceAllow
, 1);
2805 a
->r
= !!strchr(m
, 'r');
2806 a
->w
= !!strchr(m
, 'w');
2807 a
->m
= !!strchr(m
, 'm');
2809 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2813 int config_parse_blockio_weight(
2815 const char *filename
,
2817 const char *section
,
2818 unsigned section_line
,
2825 unsigned long *weight
= data
, lu
;
2832 if (isempty(rvalue
)) {
2833 *weight
= (unsigned long) -1;
2837 r
= safe_atolu(rvalue
, &lu
);
2838 if (r
< 0 || lu
< 10 || lu
> 1000) {
2839 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2840 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2848 int config_parse_blockio_device_weight(
2850 const char *filename
,
2852 const char *section
,
2853 unsigned section_line
,
2860 _cleanup_free_
char *path
= NULL
;
2861 CGroupBlockIODeviceWeight
*w
;
2862 CGroupContext
*c
= data
;
2872 if (isempty(rvalue
)) {
2873 while (c
->blockio_device_weights
)
2874 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2879 n
= strcspn(rvalue
, WHITESPACE
);
2880 weight
= rvalue
+ n
;
2882 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2883 "Expected block device and device weight. Ignoring.");
2887 path
= strndup(rvalue
, n
);
2891 if (!path_startswith(path
, "/dev")) {
2892 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2893 "Invalid device node path '%s'. Ignoring.", path
);
2897 weight
+= strspn(weight
, WHITESPACE
);
2898 r
= safe_atolu(weight
, &lu
);
2899 if (r
< 0 || lu
< 10 || lu
> 1000) {
2900 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2901 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2905 w
= new0(CGroupBlockIODeviceWeight
, 1);
2914 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2918 int config_parse_blockio_bandwidth(
2920 const char *filename
,
2922 const char *section
,
2923 unsigned section_line
,
2930 _cleanup_free_
char *path
= NULL
;
2931 CGroupBlockIODeviceBandwidth
*b
;
2932 CGroupContext
*c
= data
;
2933 const char *bandwidth
;
2943 read
= streq("BlockIOReadBandwidth", lvalue
);
2945 if (isempty(rvalue
)) {
2946 CGroupBlockIODeviceBandwidth
*next
;
2948 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2949 if (b
->read
== read
)
2950 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2955 n
= strcspn(rvalue
, WHITESPACE
);
2956 bandwidth
= rvalue
+ n
;
2957 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2960 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2961 "Expected space separated pair of device node and bandwidth. Ignoring.");
2965 path
= strndup(rvalue
, n
);
2969 if (!path_startswith(path
, "/dev")) {
2970 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2971 "Invalid device node path '%s'. Ignoring.", path
);
2975 r
= parse_size(bandwidth
, 1000, &bytes
);
2976 if (r
< 0 || bytes
<= 0) {
2977 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2978 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2982 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2988 b
->bandwidth
= (uint64_t) bytes
;
2991 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2996 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
2998 int config_parse_job_mode_isolate(
3000 const char *filename
,
3002 const char *section
,
3003 unsigned section_line
,
3017 r
= parse_boolean(rvalue
);
3019 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3020 "Failed to parse boolean, ignoring: %s", rvalue
);
3024 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3028 int config_parse_personality(
3030 const char *filename
,
3032 const char *section
,
3033 unsigned section_line
,
3040 unsigned long *personality
= data
, p
;
3045 assert(personality
);
3047 p
= personality_from_string(rvalue
);
3048 if (p
== PERSONALITY_INVALID
) {
3049 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3050 "Failed to parse personality, ignoring: %s", rvalue
);
3058 int config_parse_runtime_directory(
3060 const char *filename
,
3062 const char *section
,
3063 unsigned section_line
,
3071 const char *word
, *state
;
3080 if (isempty(rvalue
)) {
3081 /* Empty assignment resets the list */
3087 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3088 _cleanup_free_
char *n
;
3090 n
= strndup(word
, l
);
3094 if (!filename_is_valid(n
)) {
3095 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3096 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3100 r
= strv_push(rt
, n
);
3106 if (!isempty(state
))
3107 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3108 "Trailing garbage, ignoring.");
3113 int config_parse_set_status(
3115 const char *filename
,
3117 const char *section
,
3118 unsigned section_line
,
3126 const char *word
, *state
;
3128 ExitStatusSet
*status_set
= data
;
3135 /* Empty assignment resets the list */
3136 if (isempty(rvalue
)) {
3137 exit_status_set_free(status_set
);
3141 FOREACH_WORD(word
, l
, rvalue
, state
) {
3142 _cleanup_free_
char *temp
;
3146 temp
= strndup(word
, l
);
3150 r
= safe_atoi(temp
, &val
);
3152 val
= signal_from_string_try_harder(temp
);
3155 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3156 "Failed to parse value, ignoring: %s", word
);
3159 set
= &status_set
->signal
;
3161 if (val
< 0 || val
> 255) {
3162 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3163 "Value %d is outside range 0-255, ignoring", val
);
3166 set
= &status_set
->status
;
3169 r
= set_ensure_allocated(set
, NULL
);
3173 r
= set_put(*set
, INT_TO_PTR(val
));
3175 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3176 "Unable to store: %s", word
);
3180 if (!isempty(state
))
3181 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3182 "Trailing garbage, ignoring.");
3187 int config_parse_namespace_path_strv(
3189 const char *filename
,
3191 const char *section
,
3192 unsigned section_line
,
3200 const char *word
, *state
;
3209 if (isempty(rvalue
)) {
3210 /* Empty assignment resets the list */
3216 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3217 _cleanup_free_
char *n
;
3220 n
= strndup(word
, l
);
3224 if (!utf8_is_valid(n
)) {
3225 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3229 offset
= n
[0] == '-';
3230 if (!path_is_absolute(n
+ offset
)) {
3231 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3232 "Not an absolute path, ignoring: %s", rvalue
);
3236 path_kill_slashes(n
);
3238 r
= strv_push(sv
, n
);
3244 if (!isempty(state
))
3245 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3246 "Trailing garbage, ignoring.");
3251 int config_parse_no_new_privileges(
3253 const char *filename
,
3255 const char *section
,
3256 unsigned section_line
,
3263 ExecContext
*c
= data
;
3271 k
= parse_boolean(rvalue
);
3273 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3274 "Failed to parse boolean value, ignoring: %s", rvalue
);
3278 c
->no_new_privileges
= !!k
;
3279 c
->no_new_privileges_set
= true;
3284 int config_parse_protect_home(
3286 const char *filename
,
3288 const char *section
,
3289 unsigned section_line
,
3296 ExecContext
*c
= data
;
3304 /* Our enum shall be a superset of booleans, hence first try
3305 * to parse as as boolean, and then as enum */
3307 k
= parse_boolean(rvalue
);
3309 c
->protect_home
= PROTECT_HOME_YES
;
3311 c
->protect_home
= PROTECT_HOME_NO
;
3315 h
= protect_home_from_string(rvalue
);
3317 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3318 "Failed to parse protect home value, ignoring: %s", rvalue
);
3322 c
->protect_home
= h
;
3328 int config_parse_protect_system(
3330 const char *filename
,
3332 const char *section
,
3333 unsigned section_line
,
3340 ExecContext
*c
= data
;
3348 /* Our enum shall be a superset of booleans, hence first try
3349 * to parse as as boolean, and then as enum */
3351 k
= parse_boolean(rvalue
);
3353 c
->protect_system
= PROTECT_SYSTEM_YES
;
3355 c
->protect_system
= PROTECT_SYSTEM_NO
;
3359 s
= protect_system_from_string(rvalue
);
3361 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3362 "Failed to parse protect system value, ignoring: %s", rvalue
);
3366 c
->protect_system
= s
;
3372 #define FOLLOW_MAX 8
3374 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3385 /* This will update the filename pointer if the loaded file is
3386 * reached by a symlink. The old string will be freed. */
3389 char *target
, *name
;
3391 if (c
++ >= FOLLOW_MAX
)
3394 path_kill_slashes(*filename
);
3396 /* Add the file name we are currently looking at to
3397 * the names of this unit, but only if it is a valid
3399 name
= basename(*filename
);
3401 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3403 id
= set_get(names
, name
);
3409 r
= set_consume(names
, id
);
3415 /* Try to open the file name, but don't if its a symlink */
3416 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3423 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3424 r
= readlink_and_make_absolute(*filename
, &target
);
3432 f
= fdopen(fd
, "re");
3443 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3451 /* Let's try to add in all symlink names we found */
3452 while ((k
= set_steal_first(names
))) {
3454 /* First try to merge in the other name into our
3456 r
= unit_merge_by_name(*u
, k
);
3460 /* Hmm, we couldn't merge the other unit into
3461 * ours? Then let's try it the other way
3464 other
= manager_get_unit((*u
)->manager
, k
);
3468 r
= unit_merge(other
, *u
);
3471 return merge_by_names(u
, names
, NULL
);
3479 unit_choose_id(*u
, id
);
3487 static int load_from_path(Unit
*u
, const char *path
) {
3489 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3490 _cleanup_fclose_
FILE *f
= NULL
;
3491 _cleanup_free_
char *filename
= NULL
;
3499 symlink_names
= set_new(&string_hash_ops
);
3503 if (path_is_absolute(path
)) {
3505 filename
= strdup(path
);
3509 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3511 filename
= mfree(filename
);
3519 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3521 /* Instead of opening the path right away, we manually
3522 * follow all symlinks and add their name to our unit
3523 * name set while doing so */
3524 filename
= path_make_absolute(path
, *p
);
3528 if (u
->manager
->unit_path_cache
&&
3529 !set_get(u
->manager
->unit_path_cache
, filename
))
3532 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3535 filename
= mfree(filename
);
3539 /* Empty the symlink names for the next run */
3540 set_clear_free(symlink_names
);
3549 /* Hmm, no suitable file found? */
3553 r
= merge_by_names(&merged
, symlink_names
, id
);
3558 u
->load_state
= UNIT_MERGED
;
3562 if (fstat(fileno(f
), &st
) < 0)
3565 if (null_or_empty(&st
))
3566 u
->load_state
= UNIT_MASKED
;
3568 u
->load_state
= UNIT_LOADED
;
3570 /* Now, parse the file contents */
3571 r
= config_parse(u
->id
, filename
, f
,
3572 UNIT_VTABLE(u
)->sections
,
3573 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3574 false, true, false, u
);
3579 free(u
->fragment_path
);
3580 u
->fragment_path
= filename
;
3583 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3585 if (u
->source_path
) {
3586 if (stat(u
->source_path
, &st
) >= 0)
3587 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3589 u
->source_mtime
= 0;
3595 int unit_load_fragment(Unit
*u
) {
3601 assert(u
->load_state
== UNIT_STUB
);
3604 /* First, try to find the unit under its id. We always look
3605 * for unit files in the default directories, to make it easy
3606 * to override things by placing things in /etc/systemd/system */
3607 r
= load_from_path(u
, u
->id
);
3611 /* Try to find an alias we can load this with */
3612 if (u
->load_state
== UNIT_STUB
) {
3613 SET_FOREACH(t
, u
->names
, i
) {
3618 r
= load_from_path(u
, t
);
3622 if (u
->load_state
!= UNIT_STUB
)
3627 /* And now, try looking for it under the suggested (originally linked) path */
3628 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3630 r
= load_from_path(u
, u
->fragment_path
);
3634 if (u
->load_state
== UNIT_STUB
) {
3635 /* Hmm, this didn't work? Then let's get rid
3636 * of the fragment path stored for us, so that
3637 * we don't point to an invalid location. */
3638 free(u
->fragment_path
);
3639 u
->fragment_path
= NULL
;
3643 /* Look for a template */
3644 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3645 _cleanup_free_
char *k
= NULL
;
3647 r
= unit_name_template(u
->id
, &k
);
3651 r
= load_from_path(u
, k
);
3655 if (u
->load_state
== UNIT_STUB
) {
3656 SET_FOREACH(t
, u
->names
, i
) {
3657 _cleanup_free_
char *z
= NULL
;
3662 r
= unit_name_template(t
, &z
);
3666 r
= load_from_path(u
, z
);
3670 if (u
->load_state
!= UNIT_STUB
)
3679 void unit_dump_config_items(FILE *f
) {
3680 static const struct {
3681 const ConfigParserCallback callback
;
3684 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3685 { config_parse_warn_compat
, "NOTSUPPORTED" },
3687 { config_parse_int
, "INTEGER" },
3688 { config_parse_unsigned
, "UNSIGNED" },
3689 { config_parse_iec_size
, "SIZE" },
3690 { config_parse_iec_off
, "SIZE" },
3691 { config_parse_si_size
, "SIZE" },
3692 { config_parse_bool
, "BOOLEAN" },
3693 { config_parse_string
, "STRING" },
3694 { config_parse_path
, "PATH" },
3695 { config_parse_unit_path_printf
, "PATH" },
3696 { config_parse_strv
, "STRING [...]" },
3697 { config_parse_exec_nice
, "NICE" },
3698 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3699 { config_parse_exec_io_class
, "IOCLASS" },
3700 { config_parse_exec_io_priority
, "IOPRIORITY" },
3701 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3702 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3703 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3704 { config_parse_mode
, "MODE" },
3705 { config_parse_unit_env_file
, "FILE" },
3706 { config_parse_output
, "OUTPUT" },
3707 { config_parse_input
, "INPUT" },
3708 { config_parse_log_facility
, "FACILITY" },
3709 { config_parse_log_level
, "LEVEL" },
3710 { config_parse_exec_capabilities
, "CAPABILITIES" },
3711 { config_parse_exec_secure_bits
, "SECUREBITS" },
3712 { config_parse_bounding_set
, "BOUNDINGSET" },
3713 { config_parse_limit
, "LIMIT" },
3714 { config_parse_unit_deps
, "UNIT [...]" },
3715 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3716 { config_parse_service_type
, "SERVICETYPE" },
3717 { config_parse_service_restart
, "SERVICERESTART" },
3718 #ifdef HAVE_SYSV_COMPAT
3719 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3721 { config_parse_kill_mode
, "KILLMODE" },
3722 { config_parse_kill_signal
, "SIGNAL" },
3723 { config_parse_socket_listen
, "SOCKET [...]" },
3724 { config_parse_socket_bind
, "SOCKETBIND" },
3725 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3726 { config_parse_sec
, "SECONDS" },
3727 { config_parse_nsec
, "NANOSECONDS" },
3728 { config_parse_namespace_path_strv
, "PATH [...]" },
3729 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3730 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3731 { config_parse_unit_string_printf
, "STRING" },
3732 { config_parse_trigger_unit
, "UNIT" },
3733 { config_parse_timer
, "TIMER" },
3734 { config_parse_path_spec
, "PATH" },
3735 { config_parse_notify_access
, "ACCESS" },
3736 { config_parse_ip_tos
, "TOS" },
3737 { config_parse_unit_condition_path
, "CONDITION" },
3738 { config_parse_unit_condition_string
, "CONDITION" },
3739 { config_parse_unit_condition_null
, "CONDITION" },
3740 { config_parse_unit_slice
, "SLICE" },
3741 { config_parse_documentation
, "URL" },
3742 { config_parse_service_timeout
, "SECONDS" },
3743 { config_parse_failure_action
, "ACTION" },
3744 { config_parse_set_status
, "STATUS" },
3745 { config_parse_service_sockets
, "SOCKETS" },
3746 { config_parse_environ
, "ENVIRON" },
3748 { config_parse_syscall_filter
, "SYSCALLS" },
3749 { config_parse_syscall_archs
, "ARCHS" },
3750 { config_parse_syscall_errno
, "ERRNO" },
3751 { config_parse_address_families
, "FAMILIES" },
3753 { config_parse_cpu_shares
, "SHARES" },
3754 { config_parse_memory_limit
, "LIMIT" },
3755 { config_parse_device_allow
, "DEVICE" },
3756 { config_parse_device_policy
, "POLICY" },
3757 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3758 { config_parse_blockio_weight
, "WEIGHT" },
3759 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3760 { config_parse_long
, "LONG" },
3761 { config_parse_socket_service
, "SERVICE" },
3763 { config_parse_exec_selinux_context
, "LABEL" },
3765 { config_parse_job_mode
, "MODE" },
3766 { config_parse_job_mode_isolate
, "BOOLEAN" },
3767 { config_parse_personality
, "PERSONALITY" },
3770 const char *prev
= NULL
;
3775 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3776 const char *rvalue
= "OTHER", *lvalue
;
3780 const ConfigPerfItem
*p
;
3782 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3784 dot
= strchr(i
, '.');
3785 lvalue
= dot
? dot
+ 1 : i
;
3789 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3793 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3796 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3797 if (p
->parse
== table
[j
].callback
) {
3798 rvalue
= table
[j
].rvalue
;
3802 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);