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 */
1896 r
= unit_full_printf(u
, rvalue
, &n
);
1898 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1899 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1902 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1903 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1904 "Path '%s' is not absolute, ignoring.", s
);
1908 r
= strv_extend(env
, s
);
1915 int config_parse_environ(const char *unit
,
1916 const char *filename
,
1918 const char *section
,
1919 unsigned section_line
,
1928 const char *word
, *state
;
1930 _cleanup_free_
char *k
= NULL
;
1938 if (isempty(rvalue
)) {
1939 /* Empty assignment resets the list */
1946 r
= unit_full_printf(u
, rvalue
, &k
);
1948 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1956 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1957 _cleanup_free_
char *n
= NULL
;
1960 r
= cunescape_length(word
, l
, 0, &n
);
1962 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
1966 if (!env_assignment_is_valid(n
)) {
1967 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid environment assignment, ignoring: %s", rvalue
);
1971 x
= strv_env_set(*env
, n
);
1978 if (!isempty(state
))
1979 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1980 "Trailing garbage, ignoring.");
1985 int config_parse_ip_tos(const char *unit
,
1986 const char *filename
,
1988 const char *section
,
1989 unsigned section_line
,
1996 int *ip_tos
= data
, x
;
2003 x
= ip_tos_from_string(rvalue
);
2005 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2006 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2014 int config_parse_unit_condition_path(
2016 const char *filename
,
2018 const char *section
,
2019 unsigned section_line
,
2026 _cleanup_free_
char *p
= NULL
;
2027 Condition
**list
= data
, *c
;
2028 ConditionType t
= ltype
;
2029 bool trigger
, negate
;
2038 if (isempty(rvalue
)) {
2039 /* Empty assignment resets the list */
2040 *list
= condition_free_list(*list
);
2044 trigger
= rvalue
[0] == '|';
2048 negate
= rvalue
[0] == '!';
2052 r
= unit_full_printf(u
, rvalue
, &p
);
2054 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2058 if (!path_is_absolute(p
)) {
2059 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2063 c
= condition_new(t
, p
, trigger
, negate
);
2067 LIST_PREPEND(conditions
, *list
, c
);
2071 int config_parse_unit_condition_string(
2073 const char *filename
,
2075 const char *section
,
2076 unsigned section_line
,
2083 _cleanup_free_
char *s
= NULL
;
2084 Condition
**list
= data
, *c
;
2085 ConditionType t
= ltype
;
2086 bool trigger
, negate
;
2095 if (isempty(rvalue
)) {
2096 /* Empty assignment resets the list */
2097 *list
= condition_free_list(*list
);
2101 trigger
= rvalue
[0] == '|';
2105 negate
= rvalue
[0] == '!';
2109 r
= unit_full_printf(u
, rvalue
, &s
);
2111 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2115 c
= condition_new(t
, s
, trigger
, negate
);
2119 LIST_PREPEND(conditions
, *list
, c
);
2123 int config_parse_unit_condition_null(
2125 const char *filename
,
2127 const char *section
,
2128 unsigned section_line
,
2135 Condition
**list
= data
, *c
;
2136 bool trigger
, negate
;
2144 if (isempty(rvalue
)) {
2145 /* Empty assignment resets the list */
2146 *list
= condition_free_list(*list
);
2150 trigger
= rvalue
[0] == '|';
2154 negate
= rvalue
[0] == '!';
2158 b
= parse_boolean(rvalue
);
2160 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2167 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2171 LIST_PREPEND(conditions
, *list
, c
);
2175 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2176 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2178 int config_parse_unit_requires_mounts_for(
2180 const char *filename
,
2182 const char *section
,
2183 unsigned section_line
,
2191 const char *word
, *state
;
2199 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2201 _cleanup_free_
char *n
;
2203 n
= strndup(word
, l
);
2207 if (!utf8_is_valid(n
)) {
2208 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2212 r
= unit_require_mounts_for(u
, n
);
2214 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2215 "Failed to add required mount for, ignoring: %s", rvalue
);
2219 if (!isempty(state
))
2220 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2221 "Trailing garbage, ignoring.");
2226 int config_parse_documentation(const char *unit
,
2227 const char *filename
,
2229 const char *section
,
2230 unsigned section_line
,
2246 if (isempty(rvalue
)) {
2247 /* Empty assignment resets the list */
2248 strv_free(u
->documentation
);
2249 u
->documentation
= NULL
;
2253 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2254 rvalue
, data
, userdata
);
2258 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2260 if (documentation_url_is_valid(*a
))
2263 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2264 "Invalid URL, ignoring: %s", *a
);
2275 int config_parse_syscall_filter(
2277 const char *filename
,
2279 const char *section
,
2280 unsigned section_line
,
2287 static const char default_syscalls
[] =
2294 ExecContext
*c
= data
;
2296 bool invert
= false;
2297 const char *word
, *state
;
2306 if (isempty(rvalue
)) {
2307 /* Empty assignment resets the list */
2308 set_free(c
->syscall_filter
);
2309 c
->syscall_filter
= NULL
;
2310 c
->syscall_whitelist
= false;
2314 if (rvalue
[0] == '~') {
2319 if (!c
->syscall_filter
) {
2320 c
->syscall_filter
= set_new(NULL
);
2321 if (!c
->syscall_filter
)
2325 /* Allow everything but the ones listed */
2326 c
->syscall_whitelist
= false;
2330 /* Allow nothing but the ones listed */
2331 c
->syscall_whitelist
= true;
2333 /* Accept default syscalls if we are on a whitelist */
2334 NULSTR_FOREACH(i
, default_syscalls
) {
2337 id
= seccomp_syscall_resolve_name(i
);
2341 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2350 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2351 _cleanup_free_
char *t
= NULL
;
2354 t
= strndup(word
, l
);
2358 id
= seccomp_syscall_resolve_name(t
);
2360 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2361 "Failed to parse system call, ignoring: %s", t
);
2365 /* If we previously wanted to forbid a syscall and now
2366 * we want to allow it, then remove it from the list
2368 if (!invert
== c
->syscall_whitelist
) {
2369 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2375 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2377 if (!isempty(state
))
2378 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2379 "Trailing garbage, ignoring.");
2381 /* Turn on NNP, but only if it wasn't configured explicitly
2382 * before, and only if we are in user mode. */
2383 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2384 c
->no_new_privileges
= true;
2389 int config_parse_syscall_archs(
2391 const char *filename
,
2393 const char *section
,
2394 unsigned section_line
,
2402 const char *word
, *state
;
2406 if (isempty(rvalue
)) {
2412 r
= set_ensure_allocated(archs
, NULL
);
2416 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2417 _cleanup_free_
char *t
= NULL
;
2420 t
= strndup(word
, l
);
2424 r
= seccomp_arch_from_string(t
, &a
);
2426 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2427 "Failed to parse system call architecture, ignoring: %s", t
);
2431 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2437 if (!isempty(state
))
2438 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2439 "Trailing garbage, ignoring.");
2444 int config_parse_syscall_errno(
2446 const char *filename
,
2448 const char *section
,
2449 unsigned section_line
,
2456 ExecContext
*c
= data
;
2463 if (isempty(rvalue
)) {
2464 /* Empty assignment resets to KILL */
2465 c
->syscall_errno
= 0;
2469 e
= errno_from_name(rvalue
);
2471 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2472 "Failed to parse error number, ignoring: %s", rvalue
);
2476 c
->syscall_errno
= e
;
2480 int config_parse_address_families(
2482 const char *filename
,
2484 const char *section
,
2485 unsigned section_line
,
2492 ExecContext
*c
= data
;
2493 bool invert
= false;
2494 const char *word
, *state
;
2502 if (isempty(rvalue
)) {
2503 /* Empty assignment resets the list */
2504 set_free(c
->address_families
);
2505 c
->address_families
= NULL
;
2506 c
->address_families_whitelist
= false;
2510 if (rvalue
[0] == '~') {
2515 if (!c
->address_families
) {
2516 c
->address_families
= set_new(NULL
);
2517 if (!c
->address_families
)
2520 c
->address_families_whitelist
= !invert
;
2523 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2524 _cleanup_free_
char *t
= NULL
;
2527 t
= strndup(word
, l
);
2531 af
= af_from_name(t
);
2533 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2534 "Failed to parse address family, ignoring: %s", t
);
2538 /* If we previously wanted to forbid an address family and now
2539 * we want to allow it, then remove it from the list
2541 if (!invert
== c
->address_families_whitelist
) {
2542 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2548 set_remove(c
->address_families
, INT_TO_PTR(af
));
2550 if (!isempty(state
))
2551 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2552 "Trailing garbage, ignoring.");
2558 int config_parse_unit_slice(
2560 const char *filename
,
2562 const char *section
,
2563 unsigned section_line
,
2570 _cleanup_free_
char *k
= NULL
;
2571 Unit
*u
= userdata
, *slice
= NULL
;
2579 r
= unit_name_printf(u
, rvalue
, &k
);
2581 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2585 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2587 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2591 r
= unit_set_slice(u
, slice
);
2593 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2600 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2602 int config_parse_cpu_shares(
2604 const char *filename
,
2606 const char *section
,
2607 unsigned section_line
,
2614 unsigned long *shares
= data
, lu
;
2621 if (isempty(rvalue
)) {
2622 *shares
= (unsigned long) -1;
2626 r
= safe_atolu(rvalue
, &lu
);
2627 if (r
< 0 || lu
<= 0) {
2628 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2629 "CPU shares '%s' invalid. Ignoring.", rvalue
);
2637 int config_parse_cpu_quota(
2639 const char *filename
,
2641 const char *section
,
2642 unsigned section_line
,
2649 CGroupContext
*c
= data
;
2656 if (isempty(rvalue
)) {
2657 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2661 if (!endswith(rvalue
, "%")) {
2663 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2664 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2668 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2669 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2670 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2674 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2679 int config_parse_memory_limit(
2681 const char *filename
,
2683 const char *section
,
2684 unsigned section_line
,
2691 CGroupContext
*c
= data
;
2695 if (isempty(rvalue
)) {
2696 c
->memory_limit
= (uint64_t) -1;
2700 assert_cc(sizeof(uint64_t) == sizeof(off_t
));
2702 r
= parse_size(rvalue
, 1024, &bytes
);
2704 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2705 "Memory limit '%s' invalid. Ignoring.", rvalue
);
2709 c
->memory_limit
= (uint64_t) bytes
;
2713 int config_parse_device_allow(
2715 const char *filename
,
2717 const char *section
,
2718 unsigned section_line
,
2725 _cleanup_free_
char *path
= NULL
;
2726 CGroupContext
*c
= data
;
2727 CGroupDeviceAllow
*a
;
2731 if (isempty(rvalue
)) {
2732 while (c
->device_allow
)
2733 cgroup_context_free_device_allow(c
, c
->device_allow
);
2738 n
= strcspn(rvalue
, WHITESPACE
);
2739 path
= strndup(rvalue
, n
);
2743 if (!startswith(path
, "/dev/") &&
2744 !startswith(path
, "block-") &&
2745 !startswith(path
, "char-")) {
2746 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2747 "Invalid device node path '%s'. Ignoring.", path
);
2751 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2755 if (!in_charset(m
, "rwm")) {
2756 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2757 "Invalid device rights '%s'. Ignoring.", m
);
2761 a
= new0(CGroupDeviceAllow
, 1);
2767 a
->r
= !!strchr(m
, 'r');
2768 a
->w
= !!strchr(m
, 'w');
2769 a
->m
= !!strchr(m
, 'm');
2771 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2775 int config_parse_blockio_weight(
2777 const char *filename
,
2779 const char *section
,
2780 unsigned section_line
,
2787 unsigned long *weight
= data
, lu
;
2794 if (isempty(rvalue
)) {
2795 *weight
= (unsigned long) -1;
2799 r
= safe_atolu(rvalue
, &lu
);
2800 if (r
< 0 || lu
< 10 || lu
> 1000) {
2801 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2802 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2810 int config_parse_blockio_device_weight(
2812 const char *filename
,
2814 const char *section
,
2815 unsigned section_line
,
2822 _cleanup_free_
char *path
= NULL
;
2823 CGroupBlockIODeviceWeight
*w
;
2824 CGroupContext
*c
= data
;
2834 if (isempty(rvalue
)) {
2835 while (c
->blockio_device_weights
)
2836 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2841 n
= strcspn(rvalue
, WHITESPACE
);
2842 weight
= rvalue
+ n
;
2844 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2845 "Expected block device and device weight. Ignoring.");
2849 path
= strndup(rvalue
, n
);
2853 if (!path_startswith(path
, "/dev")) {
2854 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2855 "Invalid device node path '%s'. Ignoring.", path
);
2859 weight
+= strspn(weight
, WHITESPACE
);
2860 r
= safe_atolu(weight
, &lu
);
2861 if (r
< 0 || lu
< 10 || lu
> 1000) {
2862 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2863 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2867 w
= new0(CGroupBlockIODeviceWeight
, 1);
2876 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2880 int config_parse_blockio_bandwidth(
2882 const char *filename
,
2884 const char *section
,
2885 unsigned section_line
,
2892 _cleanup_free_
char *path
= NULL
;
2893 CGroupBlockIODeviceBandwidth
*b
;
2894 CGroupContext
*c
= data
;
2895 const char *bandwidth
;
2905 read
= streq("BlockIOReadBandwidth", lvalue
);
2907 if (isempty(rvalue
)) {
2908 CGroupBlockIODeviceBandwidth
*next
;
2910 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2911 if (b
->read
== read
)
2912 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2917 n
= strcspn(rvalue
, WHITESPACE
);
2918 bandwidth
= rvalue
+ n
;
2919 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2922 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2923 "Expected space separated pair of device node and bandwidth. Ignoring.");
2927 path
= strndup(rvalue
, n
);
2931 if (!path_startswith(path
, "/dev")) {
2932 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2933 "Invalid device node path '%s'. Ignoring.", path
);
2937 r
= parse_size(bandwidth
, 1000, &bytes
);
2938 if (r
< 0 || bytes
<= 0) {
2939 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2940 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2944 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2950 b
->bandwidth
= (uint64_t) bytes
;
2953 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2958 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
2960 int config_parse_job_mode_isolate(
2962 const char *filename
,
2964 const char *section
,
2965 unsigned section_line
,
2979 r
= parse_boolean(rvalue
);
2981 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2982 "Failed to parse boolean, ignoring: %s", rvalue
);
2986 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
2990 int config_parse_runtime_directory(
2992 const char *filename
,
2994 const char *section
,
2995 unsigned section_line
,
3003 const char *word
, *state
;
3012 if (isempty(rvalue
)) {
3013 /* Empty assignment resets the list */
3019 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3020 _cleanup_free_
char *n
;
3022 n
= strndup(word
, l
);
3026 if (!filename_is_valid(n
)) {
3027 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3028 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3032 r
= strv_push(rt
, n
);
3038 if (!isempty(state
))
3039 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3040 "Trailing garbage, ignoring.");
3045 int config_parse_set_status(
3047 const char *filename
,
3049 const char *section
,
3050 unsigned section_line
,
3058 const char *word
, *state
;
3060 ExitStatusSet
*status_set
= data
;
3067 /* Empty assignment resets the list */
3068 if (isempty(rvalue
)) {
3069 exit_status_set_free(status_set
);
3073 FOREACH_WORD(word
, l
, rvalue
, state
) {
3074 _cleanup_free_
char *temp
;
3078 temp
= strndup(word
, l
);
3082 r
= safe_atoi(temp
, &val
);
3084 val
= signal_from_string_try_harder(temp
);
3087 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3088 "Failed to parse value, ignoring: %s", word
);
3091 set
= &status_set
->signal
;
3093 if (val
< 0 || val
> 255) {
3094 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3095 "Value %d is outside range 0-255, ignoring", val
);
3098 set
= &status_set
->status
;
3101 r
= set_ensure_allocated(set
, NULL
);
3105 r
= set_put(*set
, INT_TO_PTR(val
));
3107 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3108 "Unable to store: %s", word
);
3112 if (!isempty(state
))
3113 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3114 "Trailing garbage, ignoring.");
3119 int config_parse_namespace_path_strv(
3121 const char *filename
,
3123 const char *section
,
3124 unsigned section_line
,
3132 const char *word
, *state
;
3141 if (isempty(rvalue
)) {
3142 /* Empty assignment resets the list */
3148 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3149 _cleanup_free_
char *n
;
3152 n
= strndup(word
, l
);
3156 if (!utf8_is_valid(n
)) {
3157 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3161 offset
= n
[0] == '-';
3162 if (!path_is_absolute(n
+ offset
)) {
3163 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3164 "Not an absolute path, ignoring: %s", rvalue
);
3168 path_kill_slashes(n
);
3170 r
= strv_push(sv
, n
);
3176 if (!isempty(state
))
3177 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3178 "Trailing garbage, ignoring.");
3183 int config_parse_no_new_privileges(
3185 const char *filename
,
3187 const char *section
,
3188 unsigned section_line
,
3195 ExecContext
*c
= data
;
3203 k
= parse_boolean(rvalue
);
3205 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3206 "Failed to parse boolean value, ignoring: %s", rvalue
);
3210 c
->no_new_privileges
= !!k
;
3211 c
->no_new_privileges_set
= true;
3216 int config_parse_protect_home(
3218 const char *filename
,
3220 const char *section
,
3221 unsigned section_line
,
3228 ExecContext
*c
= data
;
3236 /* Our enum shall be a superset of booleans, hence first try
3237 * to parse as as boolean, and then as enum */
3239 k
= parse_boolean(rvalue
);
3241 c
->protect_home
= PROTECT_HOME_YES
;
3243 c
->protect_home
= PROTECT_HOME_NO
;
3247 h
= protect_home_from_string(rvalue
);
3249 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3250 "Failed to parse protect home value, ignoring: %s", rvalue
);
3254 c
->protect_home
= h
;
3260 int config_parse_protect_system(
3262 const char *filename
,
3264 const char *section
,
3265 unsigned section_line
,
3272 ExecContext
*c
= data
;
3280 /* Our enum shall be a superset of booleans, hence first try
3281 * to parse as as boolean, and then as enum */
3283 k
= parse_boolean(rvalue
);
3285 c
->protect_system
= PROTECT_SYSTEM_YES
;
3287 c
->protect_system
= PROTECT_SYSTEM_NO
;
3291 s
= protect_system_from_string(rvalue
);
3293 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3294 "Failed to parse protect system value, ignoring: %s", rvalue
);
3298 c
->protect_system
= s
;
3304 #define FOLLOW_MAX 8
3306 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3317 /* This will update the filename pointer if the loaded file is
3318 * reached by a symlink. The old string will be freed. */
3321 char *target
, *name
;
3323 if (c
++ >= FOLLOW_MAX
)
3326 path_kill_slashes(*filename
);
3328 /* Add the file name we are currently looking at to
3329 * the names of this unit, but only if it is a valid
3331 name
= basename(*filename
);
3333 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3335 id
= set_get(names
, name
);
3341 r
= set_consume(names
, id
);
3347 /* Try to open the file name, but don't if its a symlink */
3348 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3355 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3356 r
= readlink_and_make_absolute(*filename
, &target
);
3364 f
= fdopen(fd
, "re");
3375 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3383 /* Let's try to add in all symlink names we found */
3384 while ((k
= set_steal_first(names
))) {
3386 /* First try to merge in the other name into our
3388 r
= unit_merge_by_name(*u
, k
);
3392 /* Hmm, we couldn't merge the other unit into
3393 * ours? Then let's try it the other way
3396 other
= manager_get_unit((*u
)->manager
, k
);
3400 r
= unit_merge(other
, *u
);
3403 return merge_by_names(u
, names
, NULL
);
3411 unit_choose_id(*u
, id
);
3419 static int load_from_path(Unit
*u
, const char *path
) {
3421 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3422 _cleanup_fclose_
FILE *f
= NULL
;
3423 _cleanup_free_
char *filename
= NULL
;
3431 symlink_names
= set_new(&string_hash_ops
);
3435 if (path_is_absolute(path
)) {
3437 filename
= strdup(path
);
3441 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3443 filename
= mfree(filename
);
3451 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3453 /* Instead of opening the path right away, we manually
3454 * follow all symlinks and add their name to our unit
3455 * name set while doing so */
3456 filename
= path_make_absolute(path
, *p
);
3460 if (u
->manager
->unit_path_cache
&&
3461 !set_get(u
->manager
->unit_path_cache
, filename
))
3464 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3467 filename
= mfree(filename
);
3471 /* Empty the symlink names for the next run */
3472 set_clear_free(symlink_names
);
3481 /* Hmm, no suitable file found? */
3485 r
= merge_by_names(&merged
, symlink_names
, id
);
3490 u
->load_state
= UNIT_MERGED
;
3494 if (fstat(fileno(f
), &st
) < 0)
3497 if (null_or_empty(&st
))
3498 u
->load_state
= UNIT_MASKED
;
3500 u
->load_state
= UNIT_LOADED
;
3502 /* Now, parse the file contents */
3503 r
= config_parse(u
->id
, filename
, f
,
3504 UNIT_VTABLE(u
)->sections
,
3505 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3506 false, true, false, u
);
3511 free(u
->fragment_path
);
3512 u
->fragment_path
= filename
;
3515 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3517 if (u
->source_path
) {
3518 if (stat(u
->source_path
, &st
) >= 0)
3519 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3521 u
->source_mtime
= 0;
3527 int unit_load_fragment(Unit
*u
) {
3533 assert(u
->load_state
== UNIT_STUB
);
3537 u
->load_state
= UNIT_LOADED
;
3541 /* First, try to find the unit under its id. We always look
3542 * for unit files in the default directories, to make it easy
3543 * to override things by placing things in /etc/systemd/system */
3544 r
= load_from_path(u
, u
->id
);
3548 /* Try to find an alias we can load this with */
3549 if (u
->load_state
== UNIT_STUB
) {
3550 SET_FOREACH(t
, u
->names
, i
) {
3555 r
= load_from_path(u
, t
);
3559 if (u
->load_state
!= UNIT_STUB
)
3564 /* And now, try looking for it under the suggested (originally linked) path */
3565 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3567 r
= load_from_path(u
, u
->fragment_path
);
3571 if (u
->load_state
== UNIT_STUB
)
3572 /* Hmm, this didn't work? Then let's get rid
3573 * of the fragment path stored for us, so that
3574 * we don't point to an invalid location. */
3575 u
->fragment_path
= mfree(u
->fragment_path
);
3578 /* Look for a template */
3579 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3580 _cleanup_free_
char *k
= NULL
;
3582 r
= unit_name_template(u
->id
, &k
);
3586 r
= load_from_path(u
, k
);
3590 if (u
->load_state
== UNIT_STUB
) {
3591 SET_FOREACH(t
, u
->names
, i
) {
3592 _cleanup_free_
char *z
= NULL
;
3597 r
= unit_name_template(t
, &z
);
3601 r
= load_from_path(u
, z
);
3605 if (u
->load_state
!= UNIT_STUB
)
3614 void unit_dump_config_items(FILE *f
) {
3615 static const struct {
3616 const ConfigParserCallback callback
;
3619 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3620 { config_parse_warn_compat
, "NOTSUPPORTED" },
3622 { config_parse_int
, "INTEGER" },
3623 { config_parse_unsigned
, "UNSIGNED" },
3624 { config_parse_iec_size
, "SIZE" },
3625 { config_parse_iec_off
, "SIZE" },
3626 { config_parse_si_size
, "SIZE" },
3627 { config_parse_bool
, "BOOLEAN" },
3628 { config_parse_string
, "STRING" },
3629 { config_parse_path
, "PATH" },
3630 { config_parse_unit_path_printf
, "PATH" },
3631 { config_parse_strv
, "STRING [...]" },
3632 { config_parse_exec_nice
, "NICE" },
3633 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3634 { config_parse_exec_io_class
, "IOCLASS" },
3635 { config_parse_exec_io_priority
, "IOPRIORITY" },
3636 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3637 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3638 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3639 { config_parse_mode
, "MODE" },
3640 { config_parse_unit_env_file
, "FILE" },
3641 { config_parse_output
, "OUTPUT" },
3642 { config_parse_input
, "INPUT" },
3643 { config_parse_log_facility
, "FACILITY" },
3644 { config_parse_log_level
, "LEVEL" },
3645 { config_parse_exec_capabilities
, "CAPABILITIES" },
3646 { config_parse_exec_secure_bits
, "SECUREBITS" },
3647 { config_parse_bounding_set
, "BOUNDINGSET" },
3648 { config_parse_limit
, "LIMIT" },
3649 { config_parse_unit_deps
, "UNIT [...]" },
3650 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3651 { config_parse_service_type
, "SERVICETYPE" },
3652 { config_parse_service_restart
, "SERVICERESTART" },
3653 #ifdef HAVE_SYSV_COMPAT
3654 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3656 { config_parse_kill_mode
, "KILLMODE" },
3657 { config_parse_signal
, "SIGNAL" },
3658 { config_parse_socket_listen
, "SOCKET [...]" },
3659 { config_parse_socket_bind
, "SOCKETBIND" },
3660 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3661 { config_parse_sec
, "SECONDS" },
3662 { config_parse_nsec
, "NANOSECONDS" },
3663 { config_parse_namespace_path_strv
, "PATH [...]" },
3664 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3665 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3666 { config_parse_unit_string_printf
, "STRING" },
3667 { config_parse_trigger_unit
, "UNIT" },
3668 { config_parse_timer
, "TIMER" },
3669 { config_parse_path_spec
, "PATH" },
3670 { config_parse_notify_access
, "ACCESS" },
3671 { config_parse_ip_tos
, "TOS" },
3672 { config_parse_unit_condition_path
, "CONDITION" },
3673 { config_parse_unit_condition_string
, "CONDITION" },
3674 { config_parse_unit_condition_null
, "CONDITION" },
3675 { config_parse_unit_slice
, "SLICE" },
3676 { config_parse_documentation
, "URL" },
3677 { config_parse_service_timeout
, "SECONDS" },
3678 { config_parse_failure_action
, "ACTION" },
3679 { config_parse_set_status
, "STATUS" },
3680 { config_parse_service_sockets
, "SOCKETS" },
3681 { config_parse_environ
, "ENVIRON" },
3683 { config_parse_syscall_filter
, "SYSCALLS" },
3684 { config_parse_syscall_archs
, "ARCHS" },
3685 { config_parse_syscall_errno
, "ERRNO" },
3686 { config_parse_address_families
, "FAMILIES" },
3688 { config_parse_cpu_shares
, "SHARES" },
3689 { config_parse_memory_limit
, "LIMIT" },
3690 { config_parse_device_allow
, "DEVICE" },
3691 { config_parse_device_policy
, "POLICY" },
3692 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3693 { config_parse_blockio_weight
, "WEIGHT" },
3694 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3695 { config_parse_long
, "LONG" },
3696 { config_parse_socket_service
, "SERVICE" },
3698 { config_parse_exec_selinux_context
, "LABEL" },
3700 { config_parse_job_mode
, "MODE" },
3701 { config_parse_job_mode_isolate
, "BOOLEAN" },
3702 { config_parse_personality
, "PERSONALITY" },
3705 const char *prev
= NULL
;
3710 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3711 const char *rvalue
= "OTHER", *lvalue
;
3715 const ConfigPerfItem
*p
;
3717 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3719 dot
= strchr(i
, '.');
3720 lvalue
= dot
? dot
+ 1 : i
;
3724 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3728 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3731 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3732 if (p
->parse
== table
[j
].callback
) {
3733 rvalue
= table
[j
].rvalue
;
3737 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);