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 free(c
->selinux_context
);
1219 c
->selinux_context
= NULL
;
1220 c
->selinux_context_ignore
= false;
1224 if (rvalue
[0] == '-') {
1230 r
= unit_name_printf(u
, rvalue
, &k
);
1232 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1233 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1237 free(c
->selinux_context
);
1238 c
->selinux_context
= k
;
1239 c
->selinux_context_ignore
= ignore
;
1244 int config_parse_exec_apparmor_profile(
1246 const char *filename
,
1248 const char *section
,
1249 unsigned section_line
,
1256 ExecContext
*c
= data
;
1267 if (isempty(rvalue
)) {
1268 free(c
->apparmor_profile
);
1269 c
->apparmor_profile
= NULL
;
1270 c
->apparmor_profile_ignore
= false;
1274 if (rvalue
[0] == '-') {
1280 r
= unit_name_printf(u
, rvalue
, &k
);
1282 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1283 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1287 free(c
->apparmor_profile
);
1288 c
->apparmor_profile
= k
;
1289 c
->apparmor_profile_ignore
= ignore
;
1294 int config_parse_exec_smack_process_label(
1296 const char *filename
,
1298 const char *section
,
1299 unsigned section_line
,
1306 ExecContext
*c
= data
;
1317 if (isempty(rvalue
)) {
1318 free(c
->smack_process_label
);
1319 c
->smack_process_label
= NULL
;
1320 c
->smack_process_label_ignore
= false;
1324 if (rvalue
[0] == '-') {
1330 r
= unit_name_printf(u
, rvalue
, &k
);
1332 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1333 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1337 free(c
->smack_process_label
);
1338 c
->smack_process_label
= k
;
1339 c
->smack_process_label_ignore
= ignore
;
1344 int config_parse_timer(const char *unit
,
1345 const char *filename
,
1347 const char *section
,
1348 unsigned section_line
,
1359 CalendarSpec
*c
= NULL
;
1366 if (isempty(rvalue
)) {
1367 /* Empty assignment resets list */
1368 timer_free_values(t
);
1372 b
= timer_base_from_string(lvalue
);
1374 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
,
1375 "Failed to parse timer base, ignoring: %s", lvalue
);
1379 if (b
== TIMER_CALENDAR
) {
1380 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1381 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1382 "Failed to parse calendar specification, ignoring: %s",
1387 if (parse_sec(rvalue
, &u
) < 0) {
1388 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1389 "Failed to parse timer value, ignoring: %s",
1395 v
= new0(TimerValue
, 1);
1397 calendar_spec_free(c
);
1403 v
->calendar_spec
= c
;
1405 LIST_PREPEND(value
, t
->values
, v
);
1410 int config_parse_trigger_unit(
1412 const char *filename
,
1414 const char *section
,
1415 unsigned section_line
,
1422 _cleanup_free_
char *p
= NULL
;
1432 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1433 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1434 "Multiple units to trigger specified, ignoring: %s", rvalue
);
1438 r
= unit_name_printf(u
, rvalue
, &p
);
1440 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1441 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1443 type
= unit_name_to_type(p
?: rvalue
);
1445 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1446 "Unit type not valid, ignoring: %s", rvalue
);
1450 if (type
== u
->type
) {
1451 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1452 "Trigger cannot be of same type, ignoring: %s", rvalue
);
1456 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
?: rvalue
, NULL
, true);
1458 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1459 "Failed to add trigger on %s, ignoring: %s", p
?: rvalue
, strerror(-r
));
1466 int config_parse_path_spec(const char *unit
,
1467 const char *filename
,
1469 const char *section
,
1470 unsigned section_line
,
1480 _cleanup_free_
char *k
= NULL
;
1488 if (isempty(rvalue
)) {
1489 /* Empty assignment clears list */
1494 b
= path_type_from_string(lvalue
);
1496 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1497 "Failed to parse path type, ignoring: %s", lvalue
);
1501 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1507 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1508 "Failed to resolve unit specifiers on %s. Ignoring.",
1512 if (!path_is_absolute(k
)) {
1513 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1514 "Path is not absolute, ignoring: %s", k
);
1518 s
= new0(PathSpec
, 1);
1523 s
->path
= path_kill_slashes(k
);
1528 LIST_PREPEND(spec
, p
->specs
, s
);
1533 int config_parse_socket_service(
1535 const char *filename
,
1537 const char *section
,
1538 unsigned section_line
,
1545 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1549 _cleanup_free_
char *p
= NULL
;
1556 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1558 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1562 if (!endswith(p
, ".service")) {
1563 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type service, ignoring: %s", rvalue
);
1567 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1569 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1573 unit_ref_set(&s
->service
, x
);
1578 int config_parse_service_sockets(
1580 const char *filename
,
1582 const char *section
,
1583 unsigned section_line
,
1591 const char *word
, *state
;
1600 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1601 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1603 t
= strndup(word
, l
);
1607 r
= unit_name_printf(UNIT(s
), t
, &k
);
1609 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1613 if (!endswith(k
, ".socket")) {
1614 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type socket, ignoring: %s", k
);
1618 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1620 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1622 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1624 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1626 if (!isempty(state
))
1627 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1632 int config_parse_bus_name(
1634 const char *filename
,
1636 const char *section
,
1637 unsigned section_line
,
1644 _cleanup_free_
char *k
= NULL
;
1653 r
= unit_full_printf(u
, rvalue
, &k
);
1655 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1659 if (!service_name_is_valid(k
)) {
1660 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bus name %s, ignoring.", k
);
1664 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1667 int config_parse_service_timeout(const char *unit
,
1668 const char *filename
,
1670 const char *section
,
1671 unsigned section_line
,
1678 Service
*s
= userdata
;
1686 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1687 rvalue
, data
, userdata
);
1691 if (streq(lvalue
, "TimeoutSec")) {
1692 s
->start_timeout_defined
= true;
1693 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1694 } else if (streq(lvalue
, "TimeoutStartSec"))
1695 s
->start_timeout_defined
= true;
1700 int config_parse_busname_service(
1702 const char *filename
,
1704 const char *section
,
1705 unsigned section_line
,
1712 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1716 _cleanup_free_
char *p
= NULL
;
1723 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1725 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1726 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1730 if (!endswith(p
, ".service")) {
1731 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1732 "Unit must be of type service, ignoring: %s", rvalue
);
1736 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1738 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1739 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1743 unit_ref_set(&n
->service
, x
);
1748 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1750 int config_parse_bus_policy(
1752 const char *filename
,
1754 const char *section
,
1755 unsigned section_line
,
1762 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1763 _cleanup_free_
char *id_str
= NULL
;
1764 BusName
*busname
= data
;
1772 p
= new0(BusNamePolicy
, 1);
1776 if (streq(lvalue
, "AllowUser"))
1777 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1778 else if (streq(lvalue
, "AllowGroup"))
1779 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1781 assert_not_reached("Unknown lvalue");
1783 id_str
= strdup(rvalue
);
1787 access_str
= strpbrk(id_str
, WHITESPACE
);
1789 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1790 "Invalid busname policy value '%s'", rvalue
);
1796 access_str
+= strspn(access_str
, WHITESPACE
);
1798 p
->access
= bus_policy_access_from_string(access_str
);
1799 if (p
->access
< 0) {
1800 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1801 "Invalid busname policy access type '%s'", access_str
);
1808 LIST_PREPEND(policy
, busname
->policy
, p
);
1814 int config_parse_bus_endpoint_policy(
1816 const char *filename
,
1818 const char *section
,
1819 unsigned section_line
,
1826 _cleanup_free_
char *name
= NULL
;
1827 BusPolicyAccess access
;
1828 ExecContext
*c
= data
;
1837 name
= strdup(rvalue
);
1841 access_str
= strpbrk(name
, WHITESPACE
);
1843 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1844 "Invalid endpoint policy value '%s'", rvalue
);
1850 access_str
+= strspn(access_str
, WHITESPACE
);
1852 access
= bus_policy_access_from_string(access_str
);
1853 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1854 access
>= _BUS_POLICY_ACCESS_MAX
) {
1855 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1856 "Invalid endpoint policy access type '%s'", access_str
);
1860 if (!c
->bus_endpoint
) {
1861 r
= bus_endpoint_new(&c
->bus_endpoint
);
1867 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1870 int config_parse_unit_env_file(const char *unit
,
1871 const char *filename
,
1873 const char *section
,
1874 unsigned section_line
,
1883 _cleanup_free_
char *n
= NULL
;
1892 if (isempty(rvalue
)) {
1893 /* Empty assignment frees the list */
1899 r
= unit_full_printf(u
, rvalue
, &n
);
1901 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1902 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1905 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1906 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1907 "Path '%s' is not absolute, ignoring.", s
);
1911 r
= strv_extend(env
, s
);
1918 int config_parse_environ(const char *unit
,
1919 const char *filename
,
1921 const char *section
,
1922 unsigned section_line
,
1931 const char *word
, *state
;
1933 _cleanup_free_
char *k
= NULL
;
1941 if (isempty(rvalue
)) {
1942 /* Empty assignment resets the list */
1949 r
= unit_full_printf(u
, rvalue
, &k
);
1951 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1959 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1960 _cleanup_free_
char *n
= NULL
;
1963 r
= cunescape_length(word
, l
, 0, &n
);
1965 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
1969 if (!env_assignment_is_valid(n
)) {
1970 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid environment assignment, ignoring: %s", rvalue
);
1974 x
= strv_env_set(*env
, n
);
1981 if (!isempty(state
))
1982 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1983 "Trailing garbage, ignoring.");
1988 int config_parse_ip_tos(const char *unit
,
1989 const char *filename
,
1991 const char *section
,
1992 unsigned section_line
,
1999 int *ip_tos
= data
, x
;
2006 x
= ip_tos_from_string(rvalue
);
2008 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2009 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2017 int config_parse_unit_condition_path(
2019 const char *filename
,
2021 const char *section
,
2022 unsigned section_line
,
2029 _cleanup_free_
char *p
= NULL
;
2030 Condition
**list
= data
, *c
;
2031 ConditionType t
= ltype
;
2032 bool trigger
, negate
;
2041 if (isempty(rvalue
)) {
2042 /* Empty assignment resets the list */
2043 *list
= condition_free_list(*list
);
2047 trigger
= rvalue
[0] == '|';
2051 negate
= rvalue
[0] == '!';
2055 r
= unit_full_printf(u
, rvalue
, &p
);
2057 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2061 if (!path_is_absolute(p
)) {
2062 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2066 c
= condition_new(t
, p
, trigger
, negate
);
2070 LIST_PREPEND(conditions
, *list
, c
);
2074 int config_parse_unit_condition_string(
2076 const char *filename
,
2078 const char *section
,
2079 unsigned section_line
,
2086 _cleanup_free_
char *s
= NULL
;
2087 Condition
**list
= data
, *c
;
2088 ConditionType t
= ltype
;
2089 bool trigger
, negate
;
2098 if (isempty(rvalue
)) {
2099 /* Empty assignment resets the list */
2100 *list
= condition_free_list(*list
);
2104 trigger
= rvalue
[0] == '|';
2108 negate
= rvalue
[0] == '!';
2112 r
= unit_full_printf(u
, rvalue
, &s
);
2114 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2118 c
= condition_new(t
, s
, trigger
, negate
);
2122 LIST_PREPEND(conditions
, *list
, c
);
2126 int config_parse_unit_condition_null(
2128 const char *filename
,
2130 const char *section
,
2131 unsigned section_line
,
2138 Condition
**list
= data
, *c
;
2139 bool trigger
, negate
;
2147 if (isempty(rvalue
)) {
2148 /* Empty assignment resets the list */
2149 *list
= condition_free_list(*list
);
2153 trigger
= rvalue
[0] == '|';
2157 negate
= rvalue
[0] == '!';
2161 b
= parse_boolean(rvalue
);
2163 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2170 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2174 LIST_PREPEND(conditions
, *list
, c
);
2178 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2179 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2181 int config_parse_unit_requires_mounts_for(
2183 const char *filename
,
2185 const char *section
,
2186 unsigned section_line
,
2194 const char *word
, *state
;
2202 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2204 _cleanup_free_
char *n
;
2206 n
= strndup(word
, l
);
2210 if (!utf8_is_valid(n
)) {
2211 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2215 r
= unit_require_mounts_for(u
, n
);
2217 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2218 "Failed to add required mount for, ignoring: %s", rvalue
);
2222 if (!isempty(state
))
2223 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2224 "Trailing garbage, ignoring.");
2229 int config_parse_documentation(const char *unit
,
2230 const char *filename
,
2232 const char *section
,
2233 unsigned section_line
,
2249 if (isempty(rvalue
)) {
2250 /* Empty assignment resets the list */
2251 strv_free(u
->documentation
);
2252 u
->documentation
= NULL
;
2256 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2257 rvalue
, data
, userdata
);
2261 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2263 if (documentation_url_is_valid(*a
))
2266 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2267 "Invalid URL, ignoring: %s", *a
);
2278 int config_parse_syscall_filter(
2280 const char *filename
,
2282 const char *section
,
2283 unsigned section_line
,
2290 static const char default_syscalls
[] =
2297 ExecContext
*c
= data
;
2299 bool invert
= false;
2300 const char *word
, *state
;
2309 if (isempty(rvalue
)) {
2310 /* Empty assignment resets the list */
2311 set_free(c
->syscall_filter
);
2312 c
->syscall_filter
= NULL
;
2313 c
->syscall_whitelist
= false;
2317 if (rvalue
[0] == '~') {
2322 if (!c
->syscall_filter
) {
2323 c
->syscall_filter
= set_new(NULL
);
2324 if (!c
->syscall_filter
)
2328 /* Allow everything but the ones listed */
2329 c
->syscall_whitelist
= false;
2333 /* Allow nothing but the ones listed */
2334 c
->syscall_whitelist
= true;
2336 /* Accept default syscalls if we are on a whitelist */
2337 NULSTR_FOREACH(i
, default_syscalls
) {
2340 id
= seccomp_syscall_resolve_name(i
);
2344 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2353 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2354 _cleanup_free_
char *t
= NULL
;
2357 t
= strndup(word
, l
);
2361 id
= seccomp_syscall_resolve_name(t
);
2363 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2364 "Failed to parse system call, ignoring: %s", t
);
2368 /* If we previously wanted to forbid a syscall and now
2369 * we want to allow it, then remove it from the list
2371 if (!invert
== c
->syscall_whitelist
) {
2372 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2378 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2380 if (!isempty(state
))
2381 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2382 "Trailing garbage, ignoring.");
2384 /* Turn on NNP, but only if it wasn't configured explicitly
2385 * before, and only if we are in user mode. */
2386 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2387 c
->no_new_privileges
= true;
2392 int config_parse_syscall_archs(
2394 const char *filename
,
2396 const char *section
,
2397 unsigned section_line
,
2405 const char *word
, *state
;
2409 if (isempty(rvalue
)) {
2415 r
= set_ensure_allocated(archs
, NULL
);
2419 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2420 _cleanup_free_
char *t
= NULL
;
2423 t
= strndup(word
, l
);
2427 r
= seccomp_arch_from_string(t
, &a
);
2429 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2430 "Failed to parse system call architecture, ignoring: %s", t
);
2434 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2440 if (!isempty(state
))
2441 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2442 "Trailing garbage, ignoring.");
2447 int config_parse_syscall_errno(
2449 const char *filename
,
2451 const char *section
,
2452 unsigned section_line
,
2459 ExecContext
*c
= data
;
2466 if (isempty(rvalue
)) {
2467 /* Empty assignment resets to KILL */
2468 c
->syscall_errno
= 0;
2472 e
= errno_from_name(rvalue
);
2474 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2475 "Failed to parse error number, ignoring: %s", rvalue
);
2479 c
->syscall_errno
= e
;
2483 int config_parse_address_families(
2485 const char *filename
,
2487 const char *section
,
2488 unsigned section_line
,
2495 ExecContext
*c
= data
;
2496 bool invert
= false;
2497 const char *word
, *state
;
2505 if (isempty(rvalue
)) {
2506 /* Empty assignment resets the list */
2507 set_free(c
->address_families
);
2508 c
->address_families
= NULL
;
2509 c
->address_families_whitelist
= false;
2513 if (rvalue
[0] == '~') {
2518 if (!c
->address_families
) {
2519 c
->address_families
= set_new(NULL
);
2520 if (!c
->address_families
)
2523 c
->address_families_whitelist
= !invert
;
2526 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2527 _cleanup_free_
char *t
= NULL
;
2530 t
= strndup(word
, l
);
2534 af
= af_from_name(t
);
2536 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2537 "Failed to parse address family, ignoring: %s", t
);
2541 /* If we previously wanted to forbid an address family and now
2542 * we want to allow it, then remove it from the list
2544 if (!invert
== c
->address_families_whitelist
) {
2545 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2551 set_remove(c
->address_families
, INT_TO_PTR(af
));
2553 if (!isempty(state
))
2554 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2555 "Trailing garbage, ignoring.");
2561 int config_parse_unit_slice(
2563 const char *filename
,
2565 const char *section
,
2566 unsigned section_line
,
2573 _cleanup_free_
char *k
= NULL
;
2574 Unit
*u
= userdata
, *slice
= NULL
;
2582 r
= unit_name_printf(u
, rvalue
, &k
);
2584 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2588 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2590 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2594 r
= unit_set_slice(u
, slice
);
2596 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2603 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2605 int config_parse_cpu_shares(
2607 const char *filename
,
2609 const char *section
,
2610 unsigned section_line
,
2617 unsigned long *shares
= data
, lu
;
2624 if (isempty(rvalue
)) {
2625 *shares
= (unsigned long) -1;
2629 r
= safe_atolu(rvalue
, &lu
);
2630 if (r
< 0 || lu
<= 0) {
2631 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2632 "CPU shares '%s' invalid. Ignoring.", rvalue
);
2640 int config_parse_cpu_quota(
2642 const char *filename
,
2644 const char *section
,
2645 unsigned section_line
,
2652 CGroupContext
*c
= data
;
2659 if (isempty(rvalue
)) {
2660 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2664 if (!endswith(rvalue
, "%")) {
2666 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2667 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2671 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2672 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2673 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2677 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2682 int config_parse_memory_limit(
2684 const char *filename
,
2686 const char *section
,
2687 unsigned section_line
,
2694 CGroupContext
*c
= data
;
2698 if (isempty(rvalue
)) {
2699 c
->memory_limit
= (uint64_t) -1;
2703 assert_cc(sizeof(uint64_t) == sizeof(off_t
));
2705 r
= parse_size(rvalue
, 1024, &bytes
);
2707 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2708 "Memory limit '%s' invalid. Ignoring.", rvalue
);
2712 c
->memory_limit
= (uint64_t) bytes
;
2716 int config_parse_device_allow(
2718 const char *filename
,
2720 const char *section
,
2721 unsigned section_line
,
2728 _cleanup_free_
char *path
= NULL
;
2729 CGroupContext
*c
= data
;
2730 CGroupDeviceAllow
*a
;
2734 if (isempty(rvalue
)) {
2735 while (c
->device_allow
)
2736 cgroup_context_free_device_allow(c
, c
->device_allow
);
2741 n
= strcspn(rvalue
, WHITESPACE
);
2742 path
= strndup(rvalue
, n
);
2746 if (!startswith(path
, "/dev/") &&
2747 !startswith(path
, "block-") &&
2748 !startswith(path
, "char-")) {
2749 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2750 "Invalid device node path '%s'. Ignoring.", path
);
2754 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2758 if (!in_charset(m
, "rwm")) {
2759 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2760 "Invalid device rights '%s'. Ignoring.", m
);
2764 a
= new0(CGroupDeviceAllow
, 1);
2770 a
->r
= !!strchr(m
, 'r');
2771 a
->w
= !!strchr(m
, 'w');
2772 a
->m
= !!strchr(m
, 'm');
2774 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2778 int config_parse_blockio_weight(
2780 const char *filename
,
2782 const char *section
,
2783 unsigned section_line
,
2790 unsigned long *weight
= data
, lu
;
2797 if (isempty(rvalue
)) {
2798 *weight
= (unsigned long) -1;
2802 r
= safe_atolu(rvalue
, &lu
);
2803 if (r
< 0 || lu
< 10 || lu
> 1000) {
2804 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2805 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2813 int config_parse_blockio_device_weight(
2815 const char *filename
,
2817 const char *section
,
2818 unsigned section_line
,
2825 _cleanup_free_
char *path
= NULL
;
2826 CGroupBlockIODeviceWeight
*w
;
2827 CGroupContext
*c
= data
;
2837 if (isempty(rvalue
)) {
2838 while (c
->blockio_device_weights
)
2839 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2844 n
= strcspn(rvalue
, WHITESPACE
);
2845 weight
= rvalue
+ n
;
2847 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2848 "Expected block device and device weight. Ignoring.");
2852 path
= strndup(rvalue
, n
);
2856 if (!path_startswith(path
, "/dev")) {
2857 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2858 "Invalid device node path '%s'. Ignoring.", path
);
2862 weight
+= strspn(weight
, WHITESPACE
);
2863 r
= safe_atolu(weight
, &lu
);
2864 if (r
< 0 || lu
< 10 || lu
> 1000) {
2865 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2866 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2870 w
= new0(CGroupBlockIODeviceWeight
, 1);
2879 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2883 int config_parse_blockio_bandwidth(
2885 const char *filename
,
2887 const char *section
,
2888 unsigned section_line
,
2895 _cleanup_free_
char *path
= NULL
;
2896 CGroupBlockIODeviceBandwidth
*b
;
2897 CGroupContext
*c
= data
;
2898 const char *bandwidth
;
2908 read
= streq("BlockIOReadBandwidth", lvalue
);
2910 if (isempty(rvalue
)) {
2911 CGroupBlockIODeviceBandwidth
*next
;
2913 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2914 if (b
->read
== read
)
2915 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2920 n
= strcspn(rvalue
, WHITESPACE
);
2921 bandwidth
= rvalue
+ n
;
2922 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2925 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2926 "Expected space separated pair of device node and bandwidth. Ignoring.");
2930 path
= strndup(rvalue
, n
);
2934 if (!path_startswith(path
, "/dev")) {
2935 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2936 "Invalid device node path '%s'. Ignoring.", path
);
2940 r
= parse_size(bandwidth
, 1000, &bytes
);
2941 if (r
< 0 || bytes
<= 0) {
2942 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2943 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2947 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2953 b
->bandwidth
= (uint64_t) bytes
;
2956 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2961 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
2963 int config_parse_job_mode_isolate(
2965 const char *filename
,
2967 const char *section
,
2968 unsigned section_line
,
2982 r
= parse_boolean(rvalue
);
2984 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2985 "Failed to parse boolean, ignoring: %s", rvalue
);
2989 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
2993 int config_parse_runtime_directory(
2995 const char *filename
,
2997 const char *section
,
2998 unsigned section_line
,
3006 const char *word
, *state
;
3015 if (isempty(rvalue
)) {
3016 /* Empty assignment resets the list */
3022 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3023 _cleanup_free_
char *n
;
3025 n
= strndup(word
, l
);
3029 if (!filename_is_valid(n
)) {
3030 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3031 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3035 r
= strv_push(rt
, n
);
3041 if (!isempty(state
))
3042 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3043 "Trailing garbage, ignoring.");
3048 int config_parse_set_status(
3050 const char *filename
,
3052 const char *section
,
3053 unsigned section_line
,
3061 const char *word
, *state
;
3063 ExitStatusSet
*status_set
= data
;
3070 /* Empty assignment resets the list */
3071 if (isempty(rvalue
)) {
3072 exit_status_set_free(status_set
);
3076 FOREACH_WORD(word
, l
, rvalue
, state
) {
3077 _cleanup_free_
char *temp
;
3081 temp
= strndup(word
, l
);
3085 r
= safe_atoi(temp
, &val
);
3087 val
= signal_from_string_try_harder(temp
);
3090 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3091 "Failed to parse value, ignoring: %s", word
);
3094 set
= &status_set
->signal
;
3096 if (val
< 0 || val
> 255) {
3097 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3098 "Value %d is outside range 0-255, ignoring", val
);
3101 set
= &status_set
->status
;
3104 r
= set_ensure_allocated(set
, NULL
);
3108 r
= set_put(*set
, INT_TO_PTR(val
));
3110 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3111 "Unable to store: %s", word
);
3115 if (!isempty(state
))
3116 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3117 "Trailing garbage, ignoring.");
3122 int config_parse_namespace_path_strv(
3124 const char *filename
,
3126 const char *section
,
3127 unsigned section_line
,
3135 const char *word
, *state
;
3144 if (isempty(rvalue
)) {
3145 /* Empty assignment resets the list */
3151 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3152 _cleanup_free_
char *n
;
3155 n
= strndup(word
, l
);
3159 if (!utf8_is_valid(n
)) {
3160 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3164 offset
= n
[0] == '-';
3165 if (!path_is_absolute(n
+ offset
)) {
3166 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3167 "Not an absolute path, ignoring: %s", rvalue
);
3171 path_kill_slashes(n
);
3173 r
= strv_push(sv
, n
);
3179 if (!isempty(state
))
3180 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3181 "Trailing garbage, ignoring.");
3186 int config_parse_no_new_privileges(
3188 const char *filename
,
3190 const char *section
,
3191 unsigned section_line
,
3198 ExecContext
*c
= data
;
3206 k
= parse_boolean(rvalue
);
3208 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3209 "Failed to parse boolean value, ignoring: %s", rvalue
);
3213 c
->no_new_privileges
= !!k
;
3214 c
->no_new_privileges_set
= true;
3219 int config_parse_protect_home(
3221 const char *filename
,
3223 const char *section
,
3224 unsigned section_line
,
3231 ExecContext
*c
= data
;
3239 /* Our enum shall be a superset of booleans, hence first try
3240 * to parse as as boolean, and then as enum */
3242 k
= parse_boolean(rvalue
);
3244 c
->protect_home
= PROTECT_HOME_YES
;
3246 c
->protect_home
= PROTECT_HOME_NO
;
3250 h
= protect_home_from_string(rvalue
);
3252 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3253 "Failed to parse protect home value, ignoring: %s", rvalue
);
3257 c
->protect_home
= h
;
3263 int config_parse_protect_system(
3265 const char *filename
,
3267 const char *section
,
3268 unsigned section_line
,
3275 ExecContext
*c
= data
;
3283 /* Our enum shall be a superset of booleans, hence first try
3284 * to parse as as boolean, and then as enum */
3286 k
= parse_boolean(rvalue
);
3288 c
->protect_system
= PROTECT_SYSTEM_YES
;
3290 c
->protect_system
= PROTECT_SYSTEM_NO
;
3294 s
= protect_system_from_string(rvalue
);
3296 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3297 "Failed to parse protect system value, ignoring: %s", rvalue
);
3301 c
->protect_system
= s
;
3307 #define FOLLOW_MAX 8
3309 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3320 /* This will update the filename pointer if the loaded file is
3321 * reached by a symlink. The old string will be freed. */
3324 char *target
, *name
;
3326 if (c
++ >= FOLLOW_MAX
)
3329 path_kill_slashes(*filename
);
3331 /* Add the file name we are currently looking at to
3332 * the names of this unit, but only if it is a valid
3334 name
= basename(*filename
);
3336 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3338 id
= set_get(names
, name
);
3344 r
= set_consume(names
, id
);
3350 /* Try to open the file name, but don't if its a symlink */
3351 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3358 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3359 r
= readlink_and_make_absolute(*filename
, &target
);
3367 f
= fdopen(fd
, "re");
3378 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3386 /* Let's try to add in all symlink names we found */
3387 while ((k
= set_steal_first(names
))) {
3389 /* First try to merge in the other name into our
3391 r
= unit_merge_by_name(*u
, k
);
3395 /* Hmm, we couldn't merge the other unit into
3396 * ours? Then let's try it the other way
3399 other
= manager_get_unit((*u
)->manager
, k
);
3403 r
= unit_merge(other
, *u
);
3406 return merge_by_names(u
, names
, NULL
);
3414 unit_choose_id(*u
, id
);
3422 static int load_from_path(Unit
*u
, const char *path
) {
3424 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3425 _cleanup_fclose_
FILE *f
= NULL
;
3426 _cleanup_free_
char *filename
= NULL
;
3434 symlink_names
= set_new(&string_hash_ops
);
3438 if (path_is_absolute(path
)) {
3440 filename
= strdup(path
);
3444 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3446 filename
= mfree(filename
);
3454 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3456 /* Instead of opening the path right away, we manually
3457 * follow all symlinks and add their name to our unit
3458 * name set while doing so */
3459 filename
= path_make_absolute(path
, *p
);
3463 if (u
->manager
->unit_path_cache
&&
3464 !set_get(u
->manager
->unit_path_cache
, filename
))
3467 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3470 filename
= mfree(filename
);
3474 /* Empty the symlink names for the next run */
3475 set_clear_free(symlink_names
);
3484 /* Hmm, no suitable file found? */
3488 r
= merge_by_names(&merged
, symlink_names
, id
);
3493 u
->load_state
= UNIT_MERGED
;
3497 if (fstat(fileno(f
), &st
) < 0)
3500 if (null_or_empty(&st
))
3501 u
->load_state
= UNIT_MASKED
;
3503 u
->load_state
= UNIT_LOADED
;
3505 /* Now, parse the file contents */
3506 r
= config_parse(u
->id
, filename
, f
,
3507 UNIT_VTABLE(u
)->sections
,
3508 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3509 false, true, false, u
);
3514 free(u
->fragment_path
);
3515 u
->fragment_path
= filename
;
3518 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3520 if (u
->source_path
) {
3521 if (stat(u
->source_path
, &st
) >= 0)
3522 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3524 u
->source_mtime
= 0;
3530 int unit_load_fragment(Unit
*u
) {
3536 assert(u
->load_state
== UNIT_STUB
);
3540 u
->load_state
= UNIT_LOADED
;
3544 /* First, try to find the unit under its id. We always look
3545 * for unit files in the default directories, to make it easy
3546 * to override things by placing things in /etc/systemd/system */
3547 r
= load_from_path(u
, u
->id
);
3551 /* Try to find an alias we can load this with */
3552 if (u
->load_state
== UNIT_STUB
) {
3553 SET_FOREACH(t
, u
->names
, i
) {
3558 r
= load_from_path(u
, t
);
3562 if (u
->load_state
!= UNIT_STUB
)
3567 /* And now, try looking for it under the suggested (originally linked) path */
3568 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3570 r
= load_from_path(u
, u
->fragment_path
);
3574 if (u
->load_state
== UNIT_STUB
) {
3575 /* Hmm, this didn't work? Then let's get rid
3576 * of the fragment path stored for us, so that
3577 * we don't point to an invalid location. */
3578 free(u
->fragment_path
);
3579 u
->fragment_path
= NULL
;
3583 /* Look for a template */
3584 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3585 _cleanup_free_
char *k
= NULL
;
3587 r
= unit_name_template(u
->id
, &k
);
3591 r
= load_from_path(u
, k
);
3595 if (u
->load_state
== UNIT_STUB
) {
3596 SET_FOREACH(t
, u
->names
, i
) {
3597 _cleanup_free_
char *z
= NULL
;
3602 r
= unit_name_template(t
, &z
);
3606 r
= load_from_path(u
, z
);
3610 if (u
->load_state
!= UNIT_STUB
)
3619 void unit_dump_config_items(FILE *f
) {
3620 static const struct {
3621 const ConfigParserCallback callback
;
3624 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3625 { config_parse_warn_compat
, "NOTSUPPORTED" },
3627 { config_parse_int
, "INTEGER" },
3628 { config_parse_unsigned
, "UNSIGNED" },
3629 { config_parse_iec_size
, "SIZE" },
3630 { config_parse_iec_off
, "SIZE" },
3631 { config_parse_si_size
, "SIZE" },
3632 { config_parse_bool
, "BOOLEAN" },
3633 { config_parse_string
, "STRING" },
3634 { config_parse_path
, "PATH" },
3635 { config_parse_unit_path_printf
, "PATH" },
3636 { config_parse_strv
, "STRING [...]" },
3637 { config_parse_exec_nice
, "NICE" },
3638 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3639 { config_parse_exec_io_class
, "IOCLASS" },
3640 { config_parse_exec_io_priority
, "IOPRIORITY" },
3641 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3642 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3643 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3644 { config_parse_mode
, "MODE" },
3645 { config_parse_unit_env_file
, "FILE" },
3646 { config_parse_output
, "OUTPUT" },
3647 { config_parse_input
, "INPUT" },
3648 { config_parse_log_facility
, "FACILITY" },
3649 { config_parse_log_level
, "LEVEL" },
3650 { config_parse_exec_capabilities
, "CAPABILITIES" },
3651 { config_parse_exec_secure_bits
, "SECUREBITS" },
3652 { config_parse_bounding_set
, "BOUNDINGSET" },
3653 { config_parse_limit
, "LIMIT" },
3654 { config_parse_unit_deps
, "UNIT [...]" },
3655 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3656 { config_parse_service_type
, "SERVICETYPE" },
3657 { config_parse_service_restart
, "SERVICERESTART" },
3658 #ifdef HAVE_SYSV_COMPAT
3659 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3661 { config_parse_kill_mode
, "KILLMODE" },
3662 { config_parse_signal
, "SIGNAL" },
3663 { config_parse_socket_listen
, "SOCKET [...]" },
3664 { config_parse_socket_bind
, "SOCKETBIND" },
3665 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3666 { config_parse_sec
, "SECONDS" },
3667 { config_parse_nsec
, "NANOSECONDS" },
3668 { config_parse_namespace_path_strv
, "PATH [...]" },
3669 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3670 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3671 { config_parse_unit_string_printf
, "STRING" },
3672 { config_parse_trigger_unit
, "UNIT" },
3673 { config_parse_timer
, "TIMER" },
3674 { config_parse_path_spec
, "PATH" },
3675 { config_parse_notify_access
, "ACCESS" },
3676 { config_parse_ip_tos
, "TOS" },
3677 { config_parse_unit_condition_path
, "CONDITION" },
3678 { config_parse_unit_condition_string
, "CONDITION" },
3679 { config_parse_unit_condition_null
, "CONDITION" },
3680 { config_parse_unit_slice
, "SLICE" },
3681 { config_parse_documentation
, "URL" },
3682 { config_parse_service_timeout
, "SECONDS" },
3683 { config_parse_failure_action
, "ACTION" },
3684 { config_parse_set_status
, "STATUS" },
3685 { config_parse_service_sockets
, "SOCKETS" },
3686 { config_parse_environ
, "ENVIRON" },
3688 { config_parse_syscall_filter
, "SYSCALLS" },
3689 { config_parse_syscall_archs
, "ARCHS" },
3690 { config_parse_syscall_errno
, "ERRNO" },
3691 { config_parse_address_families
, "FAMILIES" },
3693 { config_parse_cpu_shares
, "SHARES" },
3694 { config_parse_memory_limit
, "LIMIT" },
3695 { config_parse_device_allow
, "DEVICE" },
3696 { config_parse_device_policy
, "POLICY" },
3697 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3698 { config_parse_blockio_weight
, "WEIGHT" },
3699 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3700 { config_parse_long
, "LONG" },
3701 { config_parse_socket_service
, "SERVICE" },
3703 { config_parse_exec_selinux_context
, "LABEL" },
3705 { config_parse_job_mode
, "MODE" },
3706 { config_parse_job_mode_isolate
, "BOOLEAN" },
3707 { config_parse_personality
, "PERSONALITY" },
3710 const char *prev
= NULL
;
3715 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3716 const char *rvalue
= "OTHER", *lvalue
;
3720 const ConfigPerfItem
*p
;
3722 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3724 dot
= strchr(i
, '.');
3725 lvalue
= dot
? dot
+ 1 : i
;
3729 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3733 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3736 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3737 if (p
->parse
== table
[j
].callback
) {
3738 rvalue
= table
[j
].rvalue
;
3742 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);