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
, EINVAL
, "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_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1146 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1148 int config_parse_exec_mount_flags(const char *unit
,
1149 const char *filename
,
1151 const char *section
,
1152 unsigned section_line
,
1159 ExecContext
*c
= data
;
1160 const char *word
, *state
;
1162 unsigned long flags
= 0;
1169 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1170 _cleanup_free_
char *t
;
1172 t
= strndup(word
, l
);
1176 if (streq(t
, "shared"))
1178 else if (streq(t
, "slave"))
1180 else if (streq(t
, "private"))
1183 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1187 if (!isempty(state
))
1188 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1190 c
->mount_flags
= flags
;
1194 int config_parse_exec_selinux_context(
1196 const char *filename
,
1198 const char *section
,
1199 unsigned section_line
,
1206 ExecContext
*c
= data
;
1217 if (isempty(rvalue
)) {
1218 c
->selinux_context
= mfree(c
->selinux_context
);
1219 c
->selinux_context_ignore
= false;
1223 if (rvalue
[0] == '-') {
1229 r
= unit_name_printf(u
, rvalue
, &k
);
1231 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1232 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1236 free(c
->selinux_context
);
1237 c
->selinux_context
= k
;
1238 c
->selinux_context_ignore
= ignore
;
1243 int config_parse_exec_apparmor_profile(
1245 const char *filename
,
1247 const char *section
,
1248 unsigned section_line
,
1255 ExecContext
*c
= data
;
1266 if (isempty(rvalue
)) {
1267 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1268 c
->apparmor_profile_ignore
= false;
1272 if (rvalue
[0] == '-') {
1278 r
= unit_name_printf(u
, rvalue
, &k
);
1280 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1281 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1285 free(c
->apparmor_profile
);
1286 c
->apparmor_profile
= k
;
1287 c
->apparmor_profile_ignore
= ignore
;
1292 int config_parse_exec_smack_process_label(
1294 const char *filename
,
1296 const char *section
,
1297 unsigned section_line
,
1304 ExecContext
*c
= data
;
1315 if (isempty(rvalue
)) {
1316 c
->smack_process_label
= mfree(c
->smack_process_label
);
1317 c
->smack_process_label_ignore
= false;
1321 if (rvalue
[0] == '-') {
1327 r
= unit_name_printf(u
, rvalue
, &k
);
1329 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1330 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1334 free(c
->smack_process_label
);
1335 c
->smack_process_label
= k
;
1336 c
->smack_process_label_ignore
= ignore
;
1341 int config_parse_timer(const char *unit
,
1342 const char *filename
,
1344 const char *section
,
1345 unsigned section_line
,
1356 CalendarSpec
*c
= NULL
;
1363 if (isempty(rvalue
)) {
1364 /* Empty assignment resets list */
1365 timer_free_values(t
);
1369 b
= timer_base_from_string(lvalue
);
1371 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
,
1372 "Failed to parse timer base, ignoring: %s", lvalue
);
1376 if (b
== TIMER_CALENDAR
) {
1377 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1378 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1379 "Failed to parse calendar specification, ignoring: %s",
1384 if (parse_sec(rvalue
, &u
) < 0) {
1385 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1386 "Failed to parse timer value, ignoring: %s",
1392 v
= new0(TimerValue
, 1);
1394 calendar_spec_free(c
);
1400 v
->calendar_spec
= c
;
1402 LIST_PREPEND(value
, t
->values
, v
);
1407 int config_parse_trigger_unit(
1409 const char *filename
,
1411 const char *section
,
1412 unsigned section_line
,
1419 _cleanup_free_
char *p
= NULL
;
1429 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1430 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1431 "Multiple units to trigger specified, ignoring: %s", rvalue
);
1435 r
= unit_name_printf(u
, rvalue
, &p
);
1437 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1438 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1440 type
= unit_name_to_type(p
?: rvalue
);
1442 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1443 "Unit type not valid, ignoring: %s", rvalue
);
1447 if (type
== u
->type
) {
1448 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1449 "Trigger cannot be of same type, ignoring: %s", rvalue
);
1453 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
?: rvalue
, NULL
, true);
1455 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1456 "Failed to add trigger on %s, ignoring: %s", p
?: rvalue
, strerror(-r
));
1463 int config_parse_path_spec(const char *unit
,
1464 const char *filename
,
1466 const char *section
,
1467 unsigned section_line
,
1477 _cleanup_free_
char *k
= NULL
;
1485 if (isempty(rvalue
)) {
1486 /* Empty assignment clears list */
1491 b
= path_type_from_string(lvalue
);
1493 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1494 "Failed to parse path type, ignoring: %s", lvalue
);
1498 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1504 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1505 "Failed to resolve unit specifiers on %s. Ignoring.",
1509 if (!path_is_absolute(k
)) {
1510 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1511 "Path is not absolute, ignoring: %s", k
);
1515 s
= new0(PathSpec
, 1);
1520 s
->path
= path_kill_slashes(k
);
1525 LIST_PREPEND(spec
, p
->specs
, s
);
1530 int config_parse_socket_service(
1532 const char *filename
,
1534 const char *section
,
1535 unsigned section_line
,
1542 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1546 _cleanup_free_
char *p
= NULL
;
1553 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1555 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1559 if (!endswith(p
, ".service")) {
1560 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type service, ignoring: %s", rvalue
);
1564 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1566 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1570 unit_ref_set(&s
->service
, x
);
1575 int config_parse_service_sockets(
1577 const char *filename
,
1579 const char *section
,
1580 unsigned section_line
,
1588 const char *word
, *state
;
1597 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1598 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1600 t
= strndup(word
, l
);
1604 r
= unit_name_printf(UNIT(s
), t
, &k
);
1606 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1610 if (!endswith(k
, ".socket")) {
1611 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type socket, ignoring: %s", k
);
1615 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1617 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1619 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1621 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1623 if (!isempty(state
))
1624 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1629 int config_parse_bus_name(
1631 const char *filename
,
1633 const char *section
,
1634 unsigned section_line
,
1641 _cleanup_free_
char *k
= NULL
;
1650 r
= unit_full_printf(u
, rvalue
, &k
);
1652 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1656 if (!service_name_is_valid(k
)) {
1657 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bus name %s, ignoring.", k
);
1661 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1664 int config_parse_service_timeout(const char *unit
,
1665 const char *filename
,
1667 const char *section
,
1668 unsigned section_line
,
1675 Service
*s
= userdata
;
1683 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1684 rvalue
, data
, userdata
);
1688 if (streq(lvalue
, "TimeoutSec")) {
1689 s
->start_timeout_defined
= true;
1690 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1691 } else if (streq(lvalue
, "TimeoutStartSec"))
1692 s
->start_timeout_defined
= true;
1697 int config_parse_busname_service(
1699 const char *filename
,
1701 const char *section
,
1702 unsigned section_line
,
1709 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1713 _cleanup_free_
char *p
= NULL
;
1720 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1722 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1723 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1727 if (!endswith(p
, ".service")) {
1728 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1729 "Unit must be of type service, ignoring: %s", rvalue
);
1733 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1735 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1736 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1740 unit_ref_set(&n
->service
, x
);
1745 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1747 int config_parse_bus_policy(
1749 const char *filename
,
1751 const char *section
,
1752 unsigned section_line
,
1759 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1760 _cleanup_free_
char *id_str
= NULL
;
1761 BusName
*busname
= data
;
1769 p
= new0(BusNamePolicy
, 1);
1773 if (streq(lvalue
, "AllowUser"))
1774 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1775 else if (streq(lvalue
, "AllowGroup"))
1776 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1778 assert_not_reached("Unknown lvalue");
1780 id_str
= strdup(rvalue
);
1784 access_str
= strpbrk(id_str
, WHITESPACE
);
1786 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1787 "Invalid busname policy value '%s'", rvalue
);
1793 access_str
+= strspn(access_str
, WHITESPACE
);
1795 p
->access
= bus_policy_access_from_string(access_str
);
1796 if (p
->access
< 0) {
1797 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1798 "Invalid busname policy access type '%s'", access_str
);
1805 LIST_PREPEND(policy
, busname
->policy
, p
);
1811 int config_parse_bus_endpoint_policy(
1813 const char *filename
,
1815 const char *section
,
1816 unsigned section_line
,
1823 _cleanup_free_
char *name
= NULL
;
1824 BusPolicyAccess access
;
1825 ExecContext
*c
= data
;
1834 name
= strdup(rvalue
);
1838 access_str
= strpbrk(name
, WHITESPACE
);
1840 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1841 "Invalid endpoint policy value '%s'", rvalue
);
1847 access_str
+= strspn(access_str
, WHITESPACE
);
1849 access
= bus_policy_access_from_string(access_str
);
1850 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1851 access
>= _BUS_POLICY_ACCESS_MAX
) {
1852 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1853 "Invalid endpoint policy access type '%s'", access_str
);
1857 if (!c
->bus_endpoint
) {
1858 r
= bus_endpoint_new(&c
->bus_endpoint
);
1864 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1867 int config_parse_unit_env_file(const char *unit
,
1868 const char *filename
,
1870 const char *section
,
1871 unsigned section_line
,
1880 _cleanup_free_
char *n
= NULL
;
1889 if (isempty(rvalue
)) {
1890 /* Empty assignment frees the list */
1891 *env
= strv_free(*env
);
1895 r
= unit_full_printf(u
, rvalue
, &n
);
1897 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1898 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1901 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1902 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1903 "Path '%s' is not absolute, ignoring.", s
);
1907 r
= strv_extend(env
, s
);
1914 int config_parse_environ(const char *unit
,
1915 const char *filename
,
1917 const char *section
,
1918 unsigned section_line
,
1927 const char *word
, *state
;
1929 _cleanup_free_
char *k
= NULL
;
1937 if (isempty(rvalue
)) {
1938 /* Empty assignment resets the list */
1939 *env
= strv_free(*env
);
1944 r
= unit_full_printf(u
, rvalue
, &k
);
1946 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1954 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1955 _cleanup_free_
char *n
= NULL
;
1958 r
= cunescape_length(word
, l
, 0, &n
);
1960 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
1964 if (!env_assignment_is_valid(n
)) {
1965 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid environment assignment, ignoring: %s", rvalue
);
1969 x
= strv_env_set(*env
, n
);
1976 if (!isempty(state
))
1977 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1978 "Trailing garbage, ignoring.");
1983 int config_parse_ip_tos(const char *unit
,
1984 const char *filename
,
1986 const char *section
,
1987 unsigned section_line
,
1994 int *ip_tos
= data
, x
;
2001 x
= ip_tos_from_string(rvalue
);
2003 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2004 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2012 int config_parse_unit_condition_path(
2014 const char *filename
,
2016 const char *section
,
2017 unsigned section_line
,
2024 _cleanup_free_
char *p
= NULL
;
2025 Condition
**list
= data
, *c
;
2026 ConditionType t
= ltype
;
2027 bool trigger
, negate
;
2036 if (isempty(rvalue
)) {
2037 /* Empty assignment resets the list */
2038 *list
= condition_free_list(*list
);
2042 trigger
= rvalue
[0] == '|';
2046 negate
= rvalue
[0] == '!';
2050 r
= unit_full_printf(u
, rvalue
, &p
);
2052 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2056 if (!path_is_absolute(p
)) {
2057 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2061 c
= condition_new(t
, p
, trigger
, negate
);
2065 LIST_PREPEND(conditions
, *list
, c
);
2069 int config_parse_unit_condition_string(
2071 const char *filename
,
2073 const char *section
,
2074 unsigned section_line
,
2081 _cleanup_free_
char *s
= NULL
;
2082 Condition
**list
= data
, *c
;
2083 ConditionType t
= ltype
;
2084 bool trigger
, negate
;
2093 if (isempty(rvalue
)) {
2094 /* Empty assignment resets the list */
2095 *list
= condition_free_list(*list
);
2099 trigger
= rvalue
[0] == '|';
2103 negate
= rvalue
[0] == '!';
2107 r
= unit_full_printf(u
, rvalue
, &s
);
2109 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2113 c
= condition_new(t
, s
, trigger
, negate
);
2117 LIST_PREPEND(conditions
, *list
, c
);
2121 int config_parse_unit_condition_null(
2123 const char *filename
,
2125 const char *section
,
2126 unsigned section_line
,
2133 Condition
**list
= data
, *c
;
2134 bool trigger
, negate
;
2142 if (isempty(rvalue
)) {
2143 /* Empty assignment resets the list */
2144 *list
= condition_free_list(*list
);
2148 trigger
= rvalue
[0] == '|';
2152 negate
= rvalue
[0] == '!';
2156 b
= parse_boolean(rvalue
);
2158 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2165 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2169 LIST_PREPEND(conditions
, *list
, c
);
2173 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2174 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2176 int config_parse_unit_requires_mounts_for(
2178 const char *filename
,
2180 const char *section
,
2181 unsigned section_line
,
2189 const char *word
, *state
;
2197 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2199 _cleanup_free_
char *n
;
2201 n
= strndup(word
, l
);
2205 if (!utf8_is_valid(n
)) {
2206 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2210 r
= unit_require_mounts_for(u
, n
);
2212 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2213 "Failed to add required mount for, ignoring: %s", rvalue
);
2217 if (!isempty(state
))
2218 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2219 "Trailing garbage, ignoring.");
2224 int config_parse_documentation(const char *unit
,
2225 const char *filename
,
2227 const char *section
,
2228 unsigned section_line
,
2244 if (isempty(rvalue
)) {
2245 /* Empty assignment resets the list */
2246 u
->documentation
= strv_free(u
->documentation
);
2250 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2251 rvalue
, data
, userdata
);
2255 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2257 if (documentation_url_is_valid(*a
))
2260 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2261 "Invalid URL, ignoring: %s", *a
);
2272 int config_parse_syscall_filter(
2274 const char *filename
,
2276 const char *section
,
2277 unsigned section_line
,
2284 static const char default_syscalls
[] =
2291 ExecContext
*c
= data
;
2293 bool invert
= false;
2294 const char *word
, *state
;
2303 if (isempty(rvalue
)) {
2304 /* Empty assignment resets the list */
2305 c
->syscall_filter
= set_free(c
->syscall_filter
);
2306 c
->syscall_whitelist
= false;
2310 if (rvalue
[0] == '~') {
2315 if (!c
->syscall_filter
) {
2316 c
->syscall_filter
= set_new(NULL
);
2317 if (!c
->syscall_filter
)
2321 /* Allow everything but the ones listed */
2322 c
->syscall_whitelist
= false;
2326 /* Allow nothing but the ones listed */
2327 c
->syscall_whitelist
= true;
2329 /* Accept default syscalls if we are on a whitelist */
2330 NULSTR_FOREACH(i
, default_syscalls
) {
2333 id
= seccomp_syscall_resolve_name(i
);
2337 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2346 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2347 _cleanup_free_
char *t
= NULL
;
2350 t
= strndup(word
, l
);
2354 id
= seccomp_syscall_resolve_name(t
);
2356 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2357 "Failed to parse system call, ignoring: %s", t
);
2361 /* If we previously wanted to forbid a syscall and now
2362 * we want to allow it, then remove it from the list
2364 if (!invert
== c
->syscall_whitelist
) {
2365 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2371 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2373 if (!isempty(state
))
2374 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2375 "Trailing garbage, ignoring.");
2377 /* Turn on NNP, but only if it wasn't configured explicitly
2378 * before, and only if we are in user mode. */
2379 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2380 c
->no_new_privileges
= true;
2385 int config_parse_syscall_archs(
2387 const char *filename
,
2389 const char *section
,
2390 unsigned section_line
,
2398 const char *word
, *state
;
2402 if (isempty(rvalue
)) {
2403 *archs
= set_free(*archs
);
2407 r
= set_ensure_allocated(archs
, NULL
);
2411 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2412 _cleanup_free_
char *t
= NULL
;
2415 t
= strndup(word
, l
);
2419 r
= seccomp_arch_from_string(t
, &a
);
2421 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2422 "Failed to parse system call architecture, ignoring: %s", t
);
2426 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2432 if (!isempty(state
))
2433 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2434 "Trailing garbage, ignoring.");
2439 int config_parse_syscall_errno(
2441 const char *filename
,
2443 const char *section
,
2444 unsigned section_line
,
2451 ExecContext
*c
= data
;
2458 if (isempty(rvalue
)) {
2459 /* Empty assignment resets to KILL */
2460 c
->syscall_errno
= 0;
2464 e
= errno_from_name(rvalue
);
2466 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2467 "Failed to parse error number, ignoring: %s", rvalue
);
2471 c
->syscall_errno
= e
;
2475 int config_parse_address_families(
2477 const char *filename
,
2479 const char *section
,
2480 unsigned section_line
,
2487 ExecContext
*c
= data
;
2488 bool invert
= false;
2489 const char *word
, *state
;
2497 if (isempty(rvalue
)) {
2498 /* Empty assignment resets the list */
2499 c
->address_families
= set_free(c
->address_families
);
2500 c
->address_families_whitelist
= false;
2504 if (rvalue
[0] == '~') {
2509 if (!c
->address_families
) {
2510 c
->address_families
= set_new(NULL
);
2511 if (!c
->address_families
)
2514 c
->address_families_whitelist
= !invert
;
2517 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2518 _cleanup_free_
char *t
= NULL
;
2521 t
= strndup(word
, l
);
2525 af
= af_from_name(t
);
2527 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2528 "Failed to parse address family, ignoring: %s", t
);
2532 /* If we previously wanted to forbid an address family and now
2533 * we want to allow it, then remove it from the list
2535 if (!invert
== c
->address_families_whitelist
) {
2536 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2542 set_remove(c
->address_families
, INT_TO_PTR(af
));
2544 if (!isempty(state
))
2545 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2546 "Trailing garbage, ignoring.");
2552 int config_parse_unit_slice(
2554 const char *filename
,
2556 const char *section
,
2557 unsigned section_line
,
2564 _cleanup_free_
char *k
= NULL
;
2565 Unit
*u
= userdata
, *slice
= NULL
;
2573 r
= unit_name_printf(u
, rvalue
, &k
);
2575 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2579 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2581 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2585 r
= unit_set_slice(u
, slice
);
2587 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2594 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2596 int config_parse_cpu_shares(
2598 const char *filename
,
2600 const char *section
,
2601 unsigned section_line
,
2608 unsigned long *shares
= data
, lu
;
2615 if (isempty(rvalue
)) {
2616 *shares
= (unsigned long) -1;
2620 r
= safe_atolu(rvalue
, &lu
);
2621 if (r
< 0 || lu
<= 0) {
2622 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2623 "CPU shares '%s' invalid. Ignoring.", rvalue
);
2631 int config_parse_cpu_quota(
2633 const char *filename
,
2635 const char *section
,
2636 unsigned section_line
,
2643 CGroupContext
*c
= data
;
2650 if (isempty(rvalue
)) {
2651 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2655 if (!endswith(rvalue
, "%")) {
2657 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2658 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2662 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2663 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2664 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2668 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2673 int config_parse_memory_limit(
2675 const char *filename
,
2677 const char *section
,
2678 unsigned section_line
,
2685 CGroupContext
*c
= data
;
2689 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2690 c
->memory_limit
= (uint64_t) -1;
2694 r
= parse_size(rvalue
, 1024, &bytes
);
2695 if (r
< 0 || bytes
< 1) {
2696 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2700 c
->memory_limit
= bytes
;
2704 int config_parse_tasks_max(
2706 const char *filename
,
2708 const char *section
,
2709 unsigned section_line
,
2716 CGroupContext
*c
= data
;
2720 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2721 c
->tasks_max
= (uint64_t) -1;
2725 r
= safe_atou64(rvalue
, &u
);
2726 if (r
< 0 || u
< 1) {
2727 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2734 int config_parse_device_allow(
2736 const char *filename
,
2738 const char *section
,
2739 unsigned section_line
,
2746 _cleanup_free_
char *path
= NULL
;
2747 CGroupContext
*c
= data
;
2748 CGroupDeviceAllow
*a
;
2752 if (isempty(rvalue
)) {
2753 while (c
->device_allow
)
2754 cgroup_context_free_device_allow(c
, c
->device_allow
);
2759 n
= strcspn(rvalue
, WHITESPACE
);
2760 path
= strndup(rvalue
, n
);
2764 if (!startswith(path
, "/dev/") &&
2765 !startswith(path
, "block-") &&
2766 !startswith(path
, "char-")) {
2767 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2768 "Invalid device node path '%s'. Ignoring.", path
);
2772 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2776 if (!in_charset(m
, "rwm")) {
2777 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2778 "Invalid device rights '%s'. Ignoring.", m
);
2782 a
= new0(CGroupDeviceAllow
, 1);
2788 a
->r
= !!strchr(m
, 'r');
2789 a
->w
= !!strchr(m
, 'w');
2790 a
->m
= !!strchr(m
, 'm');
2792 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2796 int config_parse_blockio_weight(
2798 const char *filename
,
2800 const char *section
,
2801 unsigned section_line
,
2808 unsigned long *weight
= data
, lu
;
2815 if (isempty(rvalue
)) {
2816 *weight
= (unsigned long) -1;
2820 r
= safe_atolu(rvalue
, &lu
);
2821 if (r
< 0 || lu
< 10 || lu
> 1000) {
2822 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2823 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2831 int config_parse_blockio_device_weight(
2833 const char *filename
,
2835 const char *section
,
2836 unsigned section_line
,
2843 _cleanup_free_
char *path
= NULL
;
2844 CGroupBlockIODeviceWeight
*w
;
2845 CGroupContext
*c
= data
;
2855 if (isempty(rvalue
)) {
2856 while (c
->blockio_device_weights
)
2857 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2862 n
= strcspn(rvalue
, WHITESPACE
);
2863 weight
= rvalue
+ n
;
2865 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2866 "Expected block device and device weight. Ignoring.");
2870 path
= strndup(rvalue
, n
);
2874 if (!path_startswith(path
, "/dev")) {
2875 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2876 "Invalid device node path '%s'. Ignoring.", path
);
2880 weight
+= strspn(weight
, WHITESPACE
);
2881 r
= safe_atolu(weight
, &lu
);
2882 if (r
< 0 || lu
< 10 || lu
> 1000) {
2883 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2884 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2888 w
= new0(CGroupBlockIODeviceWeight
, 1);
2897 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2901 int config_parse_blockio_bandwidth(
2903 const char *filename
,
2905 const char *section
,
2906 unsigned section_line
,
2913 _cleanup_free_
char *path
= NULL
;
2914 CGroupBlockIODeviceBandwidth
*b
;
2915 CGroupContext
*c
= data
;
2916 const char *bandwidth
;
2926 read
= streq("BlockIOReadBandwidth", lvalue
);
2928 if (isempty(rvalue
)) {
2929 CGroupBlockIODeviceBandwidth
*next
;
2931 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2932 if (b
->read
== read
)
2933 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2938 n
= strcspn(rvalue
, WHITESPACE
);
2939 bandwidth
= rvalue
+ n
;
2940 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2943 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2944 "Expected space separated pair of device node and bandwidth. Ignoring.");
2948 path
= strndup(rvalue
, n
);
2952 if (!path_startswith(path
, "/dev")) {
2953 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2954 "Invalid device node path '%s'. Ignoring.", path
);
2958 r
= parse_size(bandwidth
, 1000, &bytes
);
2959 if (r
< 0 || bytes
<= 0) {
2960 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2961 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2965 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2971 b
->bandwidth
= bytes
;
2974 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2979 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
2981 int config_parse_job_mode_isolate(
2983 const char *filename
,
2985 const char *section
,
2986 unsigned section_line
,
3000 r
= parse_boolean(rvalue
);
3002 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3003 "Failed to parse boolean, ignoring: %s", rvalue
);
3007 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3011 int config_parse_runtime_directory(
3013 const char *filename
,
3015 const char *section
,
3016 unsigned section_line
,
3024 const char *word
, *state
;
3033 if (isempty(rvalue
)) {
3034 /* Empty assignment resets the list */
3035 *rt
= strv_free(*rt
);
3039 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3040 _cleanup_free_
char *n
;
3042 n
= strndup(word
, l
);
3046 if (!filename_is_valid(n
)) {
3047 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3048 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3052 r
= strv_push(rt
, n
);
3058 if (!isempty(state
))
3059 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3060 "Trailing garbage, ignoring.");
3065 int config_parse_set_status(
3067 const char *filename
,
3069 const char *section
,
3070 unsigned section_line
,
3078 const char *word
, *state
;
3080 ExitStatusSet
*status_set
= data
;
3087 /* Empty assignment resets the list */
3088 if (isempty(rvalue
)) {
3089 exit_status_set_free(status_set
);
3093 FOREACH_WORD(word
, l
, rvalue
, state
) {
3094 _cleanup_free_
char *temp
;
3098 temp
= strndup(word
, l
);
3102 r
= safe_atoi(temp
, &val
);
3104 val
= signal_from_string_try_harder(temp
);
3107 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3108 "Failed to parse value, ignoring: %s", word
);
3111 set
= &status_set
->signal
;
3113 if (val
< 0 || val
> 255) {
3114 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3115 "Value %d is outside range 0-255, ignoring", val
);
3118 set
= &status_set
->status
;
3121 r
= set_ensure_allocated(set
, NULL
);
3125 r
= set_put(*set
, INT_TO_PTR(val
));
3127 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3128 "Unable to store: %s", word
);
3132 if (!isempty(state
))
3133 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3134 "Trailing garbage, ignoring.");
3139 int config_parse_namespace_path_strv(
3141 const char *filename
,
3143 const char *section
,
3144 unsigned section_line
,
3152 const char *word
, *state
;
3161 if (isempty(rvalue
)) {
3162 /* Empty assignment resets the list */
3163 *sv
= strv_free(*sv
);
3167 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3168 _cleanup_free_
char *n
;
3171 n
= strndup(word
, l
);
3175 if (!utf8_is_valid(n
)) {
3176 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3180 offset
= n
[0] == '-';
3181 if (!path_is_absolute(n
+ offset
)) {
3182 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3183 "Not an absolute path, ignoring: %s", rvalue
);
3187 path_kill_slashes(n
);
3189 r
= strv_push(sv
, n
);
3195 if (!isempty(state
))
3196 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3197 "Trailing garbage, ignoring.");
3202 int config_parse_no_new_privileges(
3204 const char *filename
,
3206 const char *section
,
3207 unsigned section_line
,
3214 ExecContext
*c
= data
;
3222 k
= parse_boolean(rvalue
);
3224 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3225 "Failed to parse boolean value, ignoring: %s", rvalue
);
3229 c
->no_new_privileges
= !!k
;
3230 c
->no_new_privileges_set
= true;
3235 int config_parse_protect_home(
3237 const char *filename
,
3239 const char *section
,
3240 unsigned section_line
,
3247 ExecContext
*c
= data
;
3255 /* Our enum shall be a superset of booleans, hence first try
3256 * to parse as as boolean, and then as enum */
3258 k
= parse_boolean(rvalue
);
3260 c
->protect_home
= PROTECT_HOME_YES
;
3262 c
->protect_home
= PROTECT_HOME_NO
;
3266 h
= protect_home_from_string(rvalue
);
3268 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3269 "Failed to parse protect home value, ignoring: %s", rvalue
);
3273 c
->protect_home
= h
;
3279 int config_parse_protect_system(
3281 const char *filename
,
3283 const char *section
,
3284 unsigned section_line
,
3291 ExecContext
*c
= data
;
3299 /* Our enum shall be a superset of booleans, hence first try
3300 * to parse as as boolean, and then as enum */
3302 k
= parse_boolean(rvalue
);
3304 c
->protect_system
= PROTECT_SYSTEM_YES
;
3306 c
->protect_system
= PROTECT_SYSTEM_NO
;
3310 s
= protect_system_from_string(rvalue
);
3312 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3313 "Failed to parse protect system value, ignoring: %s", rvalue
);
3317 c
->protect_system
= s
;
3323 #define FOLLOW_MAX 8
3325 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3336 /* This will update the filename pointer if the loaded file is
3337 * reached by a symlink. The old string will be freed. */
3340 char *target
, *name
;
3342 if (c
++ >= FOLLOW_MAX
)
3345 path_kill_slashes(*filename
);
3347 /* Add the file name we are currently looking at to
3348 * the names of this unit, but only if it is a valid
3350 name
= basename(*filename
);
3352 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3354 id
= set_get(names
, name
);
3360 r
= set_consume(names
, id
);
3366 /* Try to open the file name, but don't if its a symlink */
3367 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3374 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3375 r
= readlink_and_make_absolute(*filename
, &target
);
3383 f
= fdopen(fd
, "re");
3394 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3402 /* Let's try to add in all symlink names we found */
3403 while ((k
= set_steal_first(names
))) {
3405 /* First try to merge in the other name into our
3407 r
= unit_merge_by_name(*u
, k
);
3411 /* Hmm, we couldn't merge the other unit into
3412 * ours? Then let's try it the other way
3415 other
= manager_get_unit((*u
)->manager
, k
);
3419 r
= unit_merge(other
, *u
);
3422 return merge_by_names(u
, names
, NULL
);
3430 unit_choose_id(*u
, id
);
3438 static int load_from_path(Unit
*u
, const char *path
) {
3440 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3441 _cleanup_fclose_
FILE *f
= NULL
;
3442 _cleanup_free_
char *filename
= NULL
;
3450 symlink_names
= set_new(&string_hash_ops
);
3454 if (path_is_absolute(path
)) {
3456 filename
= strdup(path
);
3460 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3462 filename
= mfree(filename
);
3470 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3472 /* Instead of opening the path right away, we manually
3473 * follow all symlinks and add their name to our unit
3474 * name set while doing so */
3475 filename
= path_make_absolute(path
, *p
);
3479 if (u
->manager
->unit_path_cache
&&
3480 !set_get(u
->manager
->unit_path_cache
, filename
))
3483 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3486 filename
= mfree(filename
);
3490 /* Empty the symlink names for the next run */
3491 set_clear_free(symlink_names
);
3500 /* Hmm, no suitable file found? */
3504 r
= merge_by_names(&merged
, symlink_names
, id
);
3509 u
->load_state
= UNIT_MERGED
;
3513 if (fstat(fileno(f
), &st
) < 0)
3516 if (null_or_empty(&st
))
3517 u
->load_state
= UNIT_MASKED
;
3519 u
->load_state
= UNIT_LOADED
;
3521 /* Now, parse the file contents */
3522 r
= config_parse(u
->id
, filename
, f
,
3523 UNIT_VTABLE(u
)->sections
,
3524 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3525 false, true, false, u
);
3530 free(u
->fragment_path
);
3531 u
->fragment_path
= filename
;
3534 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3536 if (u
->source_path
) {
3537 if (stat(u
->source_path
, &st
) >= 0)
3538 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3540 u
->source_mtime
= 0;
3546 int unit_load_fragment(Unit
*u
) {
3552 assert(u
->load_state
== UNIT_STUB
);
3556 u
->load_state
= UNIT_LOADED
;
3560 /* First, try to find the unit under its id. We always look
3561 * for unit files in the default directories, to make it easy
3562 * to override things by placing things in /etc/systemd/system */
3563 r
= load_from_path(u
, u
->id
);
3567 /* Try to find an alias we can load this with */
3568 if (u
->load_state
== UNIT_STUB
) {
3569 SET_FOREACH(t
, u
->names
, i
) {
3574 r
= load_from_path(u
, t
);
3578 if (u
->load_state
!= UNIT_STUB
)
3583 /* And now, try looking for it under the suggested (originally linked) path */
3584 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3586 r
= load_from_path(u
, u
->fragment_path
);
3590 if (u
->load_state
== UNIT_STUB
)
3591 /* Hmm, this didn't work? Then let's get rid
3592 * of the fragment path stored for us, so that
3593 * we don't point to an invalid location. */
3594 u
->fragment_path
= mfree(u
->fragment_path
);
3597 /* Look for a template */
3598 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3599 _cleanup_free_
char *k
= NULL
;
3601 r
= unit_name_template(u
->id
, &k
);
3605 r
= load_from_path(u
, k
);
3609 if (u
->load_state
== UNIT_STUB
) {
3610 SET_FOREACH(t
, u
->names
, i
) {
3611 _cleanup_free_
char *z
= NULL
;
3616 r
= unit_name_template(t
, &z
);
3620 r
= load_from_path(u
, z
);
3624 if (u
->load_state
!= UNIT_STUB
)
3633 void unit_dump_config_items(FILE *f
) {
3634 static const struct {
3635 const ConfigParserCallback callback
;
3638 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3639 { config_parse_warn_compat
, "NOTSUPPORTED" },
3641 { config_parse_int
, "INTEGER" },
3642 { config_parse_unsigned
, "UNSIGNED" },
3643 { config_parse_iec_size
, "SIZE" },
3644 { config_parse_iec_uint64
, "SIZE" },
3645 { config_parse_si_size
, "SIZE" },
3646 { config_parse_bool
, "BOOLEAN" },
3647 { config_parse_string
, "STRING" },
3648 { config_parse_path
, "PATH" },
3649 { config_parse_unit_path_printf
, "PATH" },
3650 { config_parse_strv
, "STRING [...]" },
3651 { config_parse_exec_nice
, "NICE" },
3652 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3653 { config_parse_exec_io_class
, "IOCLASS" },
3654 { config_parse_exec_io_priority
, "IOPRIORITY" },
3655 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3656 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3657 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3658 { config_parse_mode
, "MODE" },
3659 { config_parse_unit_env_file
, "FILE" },
3660 { config_parse_output
, "OUTPUT" },
3661 { config_parse_input
, "INPUT" },
3662 { config_parse_log_facility
, "FACILITY" },
3663 { config_parse_log_level
, "LEVEL" },
3664 { config_parse_exec_capabilities
, "CAPABILITIES" },
3665 { config_parse_exec_secure_bits
, "SECUREBITS" },
3666 { config_parse_bounding_set
, "BOUNDINGSET" },
3667 { config_parse_limit
, "LIMIT" },
3668 { config_parse_unit_deps
, "UNIT [...]" },
3669 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3670 { config_parse_service_type
, "SERVICETYPE" },
3671 { config_parse_service_restart
, "SERVICERESTART" },
3672 #ifdef HAVE_SYSV_COMPAT
3673 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3675 { config_parse_kill_mode
, "KILLMODE" },
3676 { config_parse_signal
, "SIGNAL" },
3677 { config_parse_socket_listen
, "SOCKET [...]" },
3678 { config_parse_socket_bind
, "SOCKETBIND" },
3679 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3680 { config_parse_sec
, "SECONDS" },
3681 { config_parse_nsec
, "NANOSECONDS" },
3682 { config_parse_namespace_path_strv
, "PATH [...]" },
3683 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3684 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3685 { config_parse_unit_string_printf
, "STRING" },
3686 { config_parse_trigger_unit
, "UNIT" },
3687 { config_parse_timer
, "TIMER" },
3688 { config_parse_path_spec
, "PATH" },
3689 { config_parse_notify_access
, "ACCESS" },
3690 { config_parse_ip_tos
, "TOS" },
3691 { config_parse_unit_condition_path
, "CONDITION" },
3692 { config_parse_unit_condition_string
, "CONDITION" },
3693 { config_parse_unit_condition_null
, "CONDITION" },
3694 { config_parse_unit_slice
, "SLICE" },
3695 { config_parse_documentation
, "URL" },
3696 { config_parse_service_timeout
, "SECONDS" },
3697 { config_parse_failure_action
, "ACTION" },
3698 { config_parse_set_status
, "STATUS" },
3699 { config_parse_service_sockets
, "SOCKETS" },
3700 { config_parse_environ
, "ENVIRON" },
3702 { config_parse_syscall_filter
, "SYSCALLS" },
3703 { config_parse_syscall_archs
, "ARCHS" },
3704 { config_parse_syscall_errno
, "ERRNO" },
3705 { config_parse_address_families
, "FAMILIES" },
3707 { config_parse_cpu_shares
, "SHARES" },
3708 { config_parse_memory_limit
, "LIMIT" },
3709 { config_parse_device_allow
, "DEVICE" },
3710 { config_parse_device_policy
, "POLICY" },
3711 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3712 { config_parse_blockio_weight
, "WEIGHT" },
3713 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3714 { config_parse_long
, "LONG" },
3715 { config_parse_socket_service
, "SERVICE" },
3717 { config_parse_exec_selinux_context
, "LABEL" },
3719 { config_parse_job_mode
, "MODE" },
3720 { config_parse_job_mode_isolate
, "BOOLEAN" },
3721 { config_parse_personality
, "PERSONALITY" },
3724 const char *prev
= NULL
;
3729 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3730 const char *rvalue
= "OTHER", *lvalue
;
3734 const ConfigPerfItem
*p
;
3736 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3738 dot
= strchr(i
, '.');
3739 lvalue
= dot
? dot
+ 1 : i
;
3743 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3747 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3750 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3751 if (p
->parse
== table
[j
].callback
) {
3752 rvalue
= table
[j
].rvalue
;
3756 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);