1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
7 Copyright 2012 Holger Hans Peter Freyther
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <linux/oom.h>
30 #include <sys/resource.h>
38 #include "conf-parser.h"
39 #include "load-fragment.h"
42 #include "securebits.h"
44 #include "unit-name.h"
45 #include "unit-printf.h"
47 #include "path-util.h"
51 #include "bus-error.h"
52 #include "errno-list.h"
55 #include "signal-util.h"
56 #include "bus-internal.h"
59 #include "seccomp-util.h"
62 int config_parse_warn_compat(
67 unsigned section_line
,
73 Disabled reason
= ltype
;
76 case DISABLED_CONFIGURATION
:
77 log_syntax(unit
, LOG_DEBUG
, filename
, line
, EINVAL
,
78 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
81 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
82 "Support for option %s= has been removed and it is ignored", lvalue
);
84 case DISABLED_EXPERIMENTAL
:
85 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
86 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
93 int config_parse_unit_deps(const char *unit
,
97 unsigned section_line
,
104 UnitDependency d
= ltype
;
106 const char *word
, *state
;
113 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
114 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
117 t
= strndup(word
, l
);
121 r
= unit_name_printf(u
, t
, &k
);
123 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
124 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
128 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
130 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
131 "Failed to add dependency on %s, ignoring: %s", k
, strerror(-r
));
134 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
139 int config_parse_unit_string_printf(
141 const char *filename
,
144 unsigned section_line
,
151 _cleanup_free_
char *k
= NULL
;
160 r
= unit_full_printf(u
, rvalue
, &k
);
162 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
166 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
169 int config_parse_unit_strv_printf(const char *unit
,
170 const char *filename
,
173 unsigned section_line
,
181 _cleanup_free_
char *k
= NULL
;
189 r
= unit_full_printf(u
, rvalue
, &k
);
191 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
192 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
194 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
195 k
? k
: rvalue
, data
, userdata
);
198 int config_parse_unit_path_printf(const char *unit
,
199 const char *filename
,
202 unsigned section_line
,
209 _cleanup_free_
char *k
= NULL
;
218 r
= unit_full_printf(u
, rvalue
, &k
);
220 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
224 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
227 int config_parse_unit_path_strv_printf(
229 const char *filename
,
232 unsigned section_line
,
240 const char *word
, *state
;
250 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
251 _cleanup_free_
char *k
= NULL
;
257 r
= unit_full_printf(u
, t
, &k
);
259 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", t
, strerror(-r
));
263 if (!utf8_is_valid(k
)) {
264 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
268 if (!path_is_absolute(k
)) {
269 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Symlink path %s is not absolute, ignoring: %s", k
, strerror(-r
));
273 path_kill_slashes(k
);
282 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
287 int config_parse_socket_listen(const char *unit
,
288 const char *filename
,
291 unsigned section_line
,
298 _cleanup_free_ SocketPort
*p
= NULL
;
310 if (isempty(rvalue
)) {
311 /* An empty assignment removes all ports */
312 socket_free_ports(s
);
316 p
= new0(SocketPort
, 1);
320 if (ltype
!= SOCKET_SOCKET
) {
323 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
325 p
->path
= strdup(rvalue
);
329 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
330 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
333 path_kill_slashes(p
->path
);
335 } else if (streq(lvalue
, "ListenNetlink")) {
336 _cleanup_free_
char *k
= NULL
;
338 p
->type
= SOCKET_SOCKET
;
339 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
341 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
342 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
344 r
= socket_address_parse_netlink(&p
->address
, k
?: rvalue
);
346 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
347 "Failed to parse address value, ignoring: %s", rvalue
);
352 _cleanup_free_
char *k
= NULL
;
354 p
->type
= SOCKET_SOCKET
;
355 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
357 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
358 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
360 r
= socket_address_parse_and_warn(&p
->address
, k
? k
: rvalue
);
362 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
363 "Failed to parse address value, ignoring: %s", rvalue
);
367 if (streq(lvalue
, "ListenStream"))
368 p
->address
.type
= SOCK_STREAM
;
369 else if (streq(lvalue
, "ListenDatagram"))
370 p
->address
.type
= SOCK_DGRAM
;
372 assert(streq(lvalue
, "ListenSequentialPacket"));
373 p
->address
.type
= SOCK_SEQPACKET
;
376 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
377 log_syntax(unit
, LOG_ERR
, filename
, line
, EOPNOTSUPP
,
378 "Address family not supported, ignoring: %s", rvalue
);
387 LIST_FIND_TAIL(port
, s
->ports
, tail
);
388 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
390 LIST_PREPEND(port
, s
->ports
, p
);
396 int config_parse_socket_bind(const char *unit
,
397 const char *filename
,
400 unsigned section_line
,
408 SocketAddressBindIPv6Only b
;
417 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
421 r
= parse_boolean(rvalue
);
423 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
424 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
428 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
430 s
->bind_ipv6_only
= b
;
435 int config_parse_exec_nice(const char *unit
,
436 const char *filename
,
439 unsigned section_line
,
446 ExecContext
*c
= data
;
454 r
= safe_atoi(rvalue
, &priority
);
456 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
457 "Failed to parse nice priority, ignoring: %s. ", rvalue
);
461 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
462 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
463 "Nice priority out of range, ignoring: %s", rvalue
);
473 int config_parse_exec_oom_score_adjust(const char* unit
,
474 const char *filename
,
477 unsigned section_line
,
484 ExecContext
*c
= data
;
492 r
= safe_atoi(rvalue
, &oa
);
494 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
495 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
499 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
500 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
501 "OOM score adjust value out of range, ignoring: %s", rvalue
);
505 c
->oom_score_adjust
= oa
;
506 c
->oom_score_adjust_set
= true;
511 int config_parse_exec(
513 const char *filename
,
516 unsigned section_line
,
523 ExecCommand
**e
= data
;
535 rvalue
+= strspn(rvalue
, WHITESPACE
);
538 if (isempty(rvalue
)) {
539 /* An empty assignment resets the list */
540 *e
= exec_command_free_list(*e
);
546 _cleanup_strv_free_
char **n
= NULL
;
547 size_t nlen
= 0, nbufsize
= 0;
548 _cleanup_free_ ExecCommand
*nce
= NULL
;
549 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
551 bool separate_argv0
= false, ignore
= false;
555 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
560 for (i
= 0; i
< 2; i
++) {
561 /* We accept an absolute path as first argument, or
562 * alternatively an absolute prefixed with @ to allow
563 * overriding of argv[0]. */
564 if (*f
== '-' && !ignore
)
566 else if (*f
== '@' && !separate_argv0
)
567 separate_argv0
= true;
574 /* First word is either "-" or "@" with no command. */
575 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
576 "Empty path in command line, ignoring: \"%s\"", rvalue
);
580 if (!string_is_safe(f
)) {
581 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
582 "Executable path contains special characters, ignoring: %s", rvalue
);
585 if (!path_is_absolute(f
)) {
586 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
587 "Executable path is not absolute, ignoring: %s", rvalue
);
590 if (endswith(f
, "/")) {
591 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
592 "Executable path specifies a directory, ignoring: %s", rvalue
);
596 if (f
== firstword
) {
605 if (!separate_argv0
) {
606 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
615 path_kill_slashes(path
);
617 while (!isempty(p
)) {
618 _cleanup_free_
char *word
= NULL
;
620 /* Check explicitly for an unquoted semicolon as
621 * command separator token. */
622 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
624 p
+= strspn(p
, WHITESPACE
);
629 /* Check for \; explicitly, to not confuse it with \\;
630 * or "\;" or "\\;" etc. extract_first_word would
631 * return the same for all of those. */
632 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
634 p
+= strspn(p
, WHITESPACE
);
635 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
645 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
651 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
659 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
660 "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
664 nce
= new0(ExecCommand
, 1);
670 nce
->ignore
= ignore
;
672 exec_command_append_list(e
, nce
);
674 /* Do not _cleanup_free_ these. */
685 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
686 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
688 int config_parse_socket_bindtodevice(const char* unit
,
689 const char *filename
,
692 unsigned section_line
,
707 if (rvalue
[0] && !streq(rvalue
, "*")) {
714 free(s
->bind_to_device
);
715 s
->bind_to_device
= n
;
720 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
721 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
723 int config_parse_exec_io_class(const char *unit
,
724 const char *filename
,
727 unsigned section_line
,
734 ExecContext
*c
= data
;
742 x
= ioprio_class_from_string(rvalue
);
744 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
745 "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
749 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
750 c
->ioprio_set
= true;
755 int config_parse_exec_io_priority(const char *unit
,
756 const char *filename
,
759 unsigned section_line
,
766 ExecContext
*c
= data
;
774 r
= safe_atoi(rvalue
, &i
);
775 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
776 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
777 "Failed to parse IO priority, ignoring: %s", rvalue
);
781 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
782 c
->ioprio_set
= true;
787 int config_parse_exec_cpu_sched_policy(const char *unit
,
788 const char *filename
,
791 unsigned section_line
,
799 ExecContext
*c
= data
;
807 x
= sched_policy_from_string(rvalue
);
809 log_syntax(unit
, LOG_ERR
, filename
, line
, -x
,
810 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
814 c
->cpu_sched_policy
= x
;
815 /* Moving to or from real-time policy? We need to adjust the priority */
816 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
817 c
->cpu_sched_set
= true;
822 int config_parse_exec_cpu_sched_prio(const char *unit
,
823 const char *filename
,
826 unsigned section_line
,
833 ExecContext
*c
= data
;
841 r
= safe_atoi(rvalue
, &i
);
843 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
844 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
848 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
849 min
= sched_get_priority_min(c
->cpu_sched_policy
);
850 max
= sched_get_priority_max(c
->cpu_sched_policy
);
852 if (i
< min
|| i
> max
) {
853 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
854 "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
858 c
->cpu_sched_priority
= i
;
859 c
->cpu_sched_set
= true;
864 int config_parse_exec_cpu_affinity(const char *unit
,
865 const char *filename
,
868 unsigned section_line
,
875 ExecContext
*c
= data
;
876 const char *word
, *state
;
884 if (isempty(rvalue
)) {
885 /* An empty assignment resets the CPU list */
892 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
893 _cleanup_free_
char *t
= NULL
;
897 t
= strndup(word
, l
);
901 r
= safe_atou(t
, &cpu
);
904 c
->cpuset
= cpu_set_malloc(&c
->cpuset_ncpus
);
909 if (r
< 0 || cpu
>= c
->cpuset_ncpus
) {
910 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
911 "Failed to parse CPU affinity '%s', ignoring: %s", t
, rvalue
);
915 CPU_SET_S(cpu
, CPU_ALLOC_SIZE(c
->cpuset_ncpus
), c
->cpuset
);
918 log_syntax(unit
, LOG_WARNING
, filename
, line
, EINVAL
,
919 "Trailing garbage, ignoring.");
924 int config_parse_exec_capabilities(const char *unit
,
925 const char *filename
,
928 unsigned section_line
,
935 ExecContext
*c
= data
;
943 cap
= cap_from_text(rvalue
);
945 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
,
946 "Failed to parse capabilities, ignoring: %s", rvalue
);
951 cap_free(c
->capabilities
);
952 c
->capabilities
= cap
;
957 int config_parse_exec_secure_bits(const char *unit
,
958 const char *filename
,
961 unsigned section_line
,
968 ExecContext
*c
= data
;
970 const char *word
, *state
;
977 if (isempty(rvalue
)) {
978 /* An empty assignment resets the field */
983 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
984 if (first_word(word
, "keep-caps"))
985 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
986 else if (first_word(word
, "keep-caps-locked"))
987 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
988 else if (first_word(word
, "no-setuid-fixup"))
989 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
990 else if (first_word(word
, "no-setuid-fixup-locked"))
991 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
992 else if (first_word(word
, "noroot"))
993 c
->secure_bits
|= 1<<SECURE_NOROOT
;
994 else if (first_word(word
, "noroot-locked"))
995 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
997 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
998 "Failed to parse secure bits, ignoring: %s", rvalue
);
1002 if (!isempty(state
))
1003 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1004 "Invalid syntax, garbage at the end, ignoring.");
1009 int config_parse_bounding_set(const char *unit
,
1010 const char *filename
,
1012 const char *section
,
1013 unsigned section_line
,
1020 uint64_t *capability_bounding_set_drop
= data
;
1021 const char *word
, *state
;
1023 bool invert
= false;
1031 if (rvalue
[0] == '~') {
1036 /* Note that we store this inverted internally, since the
1037 * kernel wants it like this. But we actually expose it
1038 * non-inverted everywhere to have a fully normalized
1041 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1042 _cleanup_free_
char *t
= NULL
;
1045 t
= strndup(word
, l
);
1049 cap
= capability_from_name(t
);
1051 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse capability in bounding set, ignoring: %s", t
);
1055 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1057 if (!isempty(state
))
1058 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1059 "Trailing garbage, ignoring.");
1062 *capability_bounding_set_drop
|= sum
;
1064 *capability_bounding_set_drop
|= ~sum
;
1069 int config_parse_limit(const char *unit
,
1070 const char *filename
,
1072 const char *section
,
1073 unsigned section_line
,
1080 struct rlimit
**rl
= data
;
1081 unsigned long long u
;
1090 if (streq(rvalue
, "infinity"))
1091 u
= (unsigned long long) RLIM_INFINITY
;
1095 r
= safe_atollu(rvalue
, &u
);
1097 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1098 "Failed to parse resource value, ignoring: %s", rvalue
);
1104 *rl
= new(struct rlimit
, 1);
1109 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1113 #ifdef HAVE_SYSV_COMPAT
1114 int config_parse_sysv_priority(const char *unit
,
1115 const char *filename
,
1117 const char *section
,
1118 unsigned section_line
,
1125 int *priority
= data
;
1133 r
= safe_atoi(rvalue
, &i
);
1134 if (r
< 0 || i
< 0) {
1135 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1136 "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1140 *priority
= (int) i
;
1145 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1146 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1148 int config_parse_exec_mount_flags(const char *unit
,
1149 const char *filename
,
1151 const char *section
,
1152 unsigned section_line
,
1159 ExecContext
*c
= data
;
1160 const char *word
, *state
;
1162 unsigned long flags
= 0;
1169 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1170 _cleanup_free_
char *t
;
1172 t
= strndup(word
, l
);
1176 if (streq(t
, "shared"))
1178 else if (streq(t
, "slave"))
1180 else if (streq(t
, "private"))
1183 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1187 if (!isempty(state
))
1188 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1190 c
->mount_flags
= flags
;
1194 int config_parse_exec_selinux_context(
1196 const char *filename
,
1198 const char *section
,
1199 unsigned section_line
,
1206 ExecContext
*c
= data
;
1217 if (isempty(rvalue
)) {
1218 c
->selinux_context
= mfree(c
->selinux_context
);
1219 c
->selinux_context_ignore
= false;
1223 if (rvalue
[0] == '-') {
1229 r
= unit_name_printf(u
, rvalue
, &k
);
1231 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1232 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1236 free(c
->selinux_context
);
1237 c
->selinux_context
= k
;
1238 c
->selinux_context_ignore
= ignore
;
1243 int config_parse_exec_apparmor_profile(
1245 const char *filename
,
1247 const char *section
,
1248 unsigned section_line
,
1255 ExecContext
*c
= data
;
1266 if (isempty(rvalue
)) {
1267 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1268 c
->apparmor_profile_ignore
= false;
1272 if (rvalue
[0] == '-') {
1278 r
= unit_name_printf(u
, rvalue
, &k
);
1280 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1281 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1285 free(c
->apparmor_profile
);
1286 c
->apparmor_profile
= k
;
1287 c
->apparmor_profile_ignore
= ignore
;
1292 int config_parse_exec_smack_process_label(
1294 const char *filename
,
1296 const char *section
,
1297 unsigned section_line
,
1304 ExecContext
*c
= data
;
1315 if (isempty(rvalue
)) {
1316 c
->smack_process_label
= mfree(c
->smack_process_label
);
1317 c
->smack_process_label_ignore
= false;
1321 if (rvalue
[0] == '-') {
1327 r
= unit_name_printf(u
, rvalue
, &k
);
1329 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1330 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1334 free(c
->smack_process_label
);
1335 c
->smack_process_label
= k
;
1336 c
->smack_process_label_ignore
= ignore
;
1341 int config_parse_timer(const char *unit
,
1342 const char *filename
,
1344 const char *section
,
1345 unsigned section_line
,
1356 CalendarSpec
*c
= NULL
;
1363 if (isempty(rvalue
)) {
1364 /* Empty assignment resets list */
1365 timer_free_values(t
);
1369 b
= timer_base_from_string(lvalue
);
1371 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
,
1372 "Failed to parse timer base, ignoring: %s", lvalue
);
1376 if (b
== TIMER_CALENDAR
) {
1377 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1378 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1379 "Failed to parse calendar specification, ignoring: %s",
1384 if (parse_sec(rvalue
, &u
) < 0) {
1385 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1386 "Failed to parse timer value, ignoring: %s",
1392 v
= new0(TimerValue
, 1);
1394 calendar_spec_free(c
);
1400 v
->calendar_spec
= c
;
1402 LIST_PREPEND(value
, t
->values
, v
);
1407 int config_parse_trigger_unit(
1409 const char *filename
,
1411 const char *section
,
1412 unsigned section_line
,
1419 _cleanup_free_
char *p
= NULL
;
1429 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1430 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1431 "Multiple units to trigger specified, ignoring: %s", rvalue
);
1435 r
= unit_name_printf(u
, rvalue
, &p
);
1437 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1438 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1440 type
= unit_name_to_type(p
?: rvalue
);
1442 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1443 "Unit type not valid, ignoring: %s", rvalue
);
1447 if (type
== u
->type
) {
1448 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1449 "Trigger cannot be of same type, ignoring: %s", rvalue
);
1453 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
?: rvalue
, NULL
, true);
1455 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1456 "Failed to add trigger on %s, ignoring: %s", p
?: rvalue
, strerror(-r
));
1463 int config_parse_path_spec(const char *unit
,
1464 const char *filename
,
1466 const char *section
,
1467 unsigned section_line
,
1477 _cleanup_free_
char *k
= NULL
;
1485 if (isempty(rvalue
)) {
1486 /* Empty assignment clears list */
1491 b
= path_type_from_string(lvalue
);
1493 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1494 "Failed to parse path type, ignoring: %s", lvalue
);
1498 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1504 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1505 "Failed to resolve unit specifiers on %s. Ignoring.",
1509 if (!path_is_absolute(k
)) {
1510 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1511 "Path is not absolute, ignoring: %s", k
);
1515 s
= new0(PathSpec
, 1);
1520 s
->path
= path_kill_slashes(k
);
1525 LIST_PREPEND(spec
, p
->specs
, s
);
1530 int config_parse_socket_service(
1532 const char *filename
,
1534 const char *section
,
1535 unsigned section_line
,
1542 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1546 _cleanup_free_
char *p
= NULL
;
1553 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1555 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1559 if (!endswith(p
, ".service")) {
1560 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type service, ignoring: %s", rvalue
);
1564 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1566 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1570 unit_ref_set(&s
->service
, x
);
1575 int config_parse_service_sockets(
1577 const char *filename
,
1579 const char *section
,
1580 unsigned section_line
,
1588 const char *word
, *state
;
1597 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1598 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1600 t
= strndup(word
, l
);
1604 r
= unit_name_printf(UNIT(s
), t
, &k
);
1606 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1610 if (!endswith(k
, ".socket")) {
1611 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type socket, ignoring: %s", k
);
1615 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1617 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1619 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1621 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1623 if (!isempty(state
))
1624 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1629 int config_parse_bus_name(
1631 const char *filename
,
1633 const char *section
,
1634 unsigned section_line
,
1641 _cleanup_free_
char *k
= NULL
;
1650 r
= unit_full_printf(u
, rvalue
, &k
);
1652 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1656 if (!service_name_is_valid(k
)) {
1657 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bus name %s, ignoring.", k
);
1661 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1664 int config_parse_service_timeout(const char *unit
,
1665 const char *filename
,
1667 const char *section
,
1668 unsigned section_line
,
1675 Service
*s
= userdata
;
1683 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1684 rvalue
, data
, userdata
);
1688 if (streq(lvalue
, "TimeoutSec")) {
1689 s
->start_timeout_defined
= true;
1690 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1691 } else if (streq(lvalue
, "TimeoutStartSec"))
1692 s
->start_timeout_defined
= true;
1697 int config_parse_busname_service(
1699 const char *filename
,
1701 const char *section
,
1702 unsigned section_line
,
1709 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1713 _cleanup_free_
char *p
= NULL
;
1720 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1722 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1723 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1727 if (!endswith(p
, ".service")) {
1728 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1729 "Unit must be of type service, ignoring: %s", rvalue
);
1733 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1735 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1736 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1740 unit_ref_set(&n
->service
, x
);
1745 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1747 int config_parse_bus_policy(
1749 const char *filename
,
1751 const char *section
,
1752 unsigned section_line
,
1759 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1760 _cleanup_free_
char *id_str
= NULL
;
1761 BusName
*busname
= data
;
1769 p
= new0(BusNamePolicy
, 1);
1773 if (streq(lvalue
, "AllowUser"))
1774 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1775 else if (streq(lvalue
, "AllowGroup"))
1776 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1778 assert_not_reached("Unknown lvalue");
1780 id_str
= strdup(rvalue
);
1784 access_str
= strpbrk(id_str
, WHITESPACE
);
1786 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1787 "Invalid busname policy value '%s'", rvalue
);
1793 access_str
+= strspn(access_str
, WHITESPACE
);
1795 p
->access
= bus_policy_access_from_string(access_str
);
1796 if (p
->access
< 0) {
1797 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1798 "Invalid busname policy access type '%s'", access_str
);
1805 LIST_PREPEND(policy
, busname
->policy
, p
);
1811 int config_parse_bus_endpoint_policy(
1813 const char *filename
,
1815 const char *section
,
1816 unsigned section_line
,
1823 _cleanup_free_
char *name
= NULL
;
1824 BusPolicyAccess access
;
1825 ExecContext
*c
= data
;
1834 name
= strdup(rvalue
);
1838 access_str
= strpbrk(name
, WHITESPACE
);
1840 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1841 "Invalid endpoint policy value '%s'", rvalue
);
1847 access_str
+= strspn(access_str
, WHITESPACE
);
1849 access
= bus_policy_access_from_string(access_str
);
1850 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1851 access
>= _BUS_POLICY_ACCESS_MAX
) {
1852 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1853 "Invalid endpoint policy access type '%s'", access_str
);
1857 if (!c
->bus_endpoint
) {
1858 r
= bus_endpoint_new(&c
->bus_endpoint
);
1864 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1867 int config_parse_unit_env_file(const char *unit
,
1868 const char *filename
,
1870 const char *section
,
1871 unsigned section_line
,
1880 _cleanup_free_
char *n
= NULL
;
1889 if (isempty(rvalue
)) {
1890 /* Empty assignment frees the list */
1891 *env
= strv_free(*env
);
1895 r
= unit_full_printf(u
, rvalue
, &n
);
1897 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1898 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1901 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1902 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1903 "Path '%s' is not absolute, ignoring.", s
);
1907 r
= strv_extend(env
, s
);
1914 int config_parse_environ(const char *unit
,
1915 const char *filename
,
1917 const char *section
,
1918 unsigned section_line
,
1927 const char *word
, *state
;
1929 _cleanup_free_
char *k
= NULL
;
1937 if (isempty(rvalue
)) {
1938 /* Empty assignment resets the list */
1939 *env
= strv_free(*env
);
1944 r
= unit_full_printf(u
, rvalue
, &k
);
1946 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1954 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1955 _cleanup_free_
char *n
= NULL
;
1958 r
= cunescape_length(word
, l
, 0, &n
);
1960 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
1964 if (!env_assignment_is_valid(n
)) {
1965 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid environment assignment, ignoring: %s", rvalue
);
1969 x
= strv_env_set(*env
, n
);
1976 if (!isempty(state
))
1977 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1978 "Trailing garbage, ignoring.");
1983 int config_parse_ip_tos(const char *unit
,
1984 const char *filename
,
1986 const char *section
,
1987 unsigned section_line
,
1994 int *ip_tos
= data
, x
;
2001 x
= ip_tos_from_string(rvalue
);
2003 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2004 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2012 int config_parse_unit_condition_path(
2014 const char *filename
,
2016 const char *section
,
2017 unsigned section_line
,
2024 _cleanup_free_
char *p
= NULL
;
2025 Condition
**list
= data
, *c
;
2026 ConditionType t
= ltype
;
2027 bool trigger
, negate
;
2036 if (isempty(rvalue
)) {
2037 /* Empty assignment resets the list */
2038 *list
= condition_free_list(*list
);
2042 trigger
= rvalue
[0] == '|';
2046 negate
= rvalue
[0] == '!';
2050 r
= unit_full_printf(u
, rvalue
, &p
);
2052 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2056 if (!path_is_absolute(p
)) {
2057 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2061 c
= condition_new(t
, p
, trigger
, negate
);
2065 LIST_PREPEND(conditions
, *list
, c
);
2069 int config_parse_unit_condition_string(
2071 const char *filename
,
2073 const char *section
,
2074 unsigned section_line
,
2081 _cleanup_free_
char *s
= NULL
;
2082 Condition
**list
= data
, *c
;
2083 ConditionType t
= ltype
;
2084 bool trigger
, negate
;
2093 if (isempty(rvalue
)) {
2094 /* Empty assignment resets the list */
2095 *list
= condition_free_list(*list
);
2099 trigger
= rvalue
[0] == '|';
2103 negate
= rvalue
[0] == '!';
2107 r
= unit_full_printf(u
, rvalue
, &s
);
2109 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2113 c
= condition_new(t
, s
, trigger
, negate
);
2117 LIST_PREPEND(conditions
, *list
, c
);
2121 int config_parse_unit_condition_null(
2123 const char *filename
,
2125 const char *section
,
2126 unsigned section_line
,
2133 Condition
**list
= data
, *c
;
2134 bool trigger
, negate
;
2142 if (isempty(rvalue
)) {
2143 /* Empty assignment resets the list */
2144 *list
= condition_free_list(*list
);
2148 trigger
= rvalue
[0] == '|';
2152 negate
= rvalue
[0] == '!';
2156 b
= parse_boolean(rvalue
);
2158 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2165 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2169 LIST_PREPEND(conditions
, *list
, c
);
2173 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2174 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2176 int config_parse_unit_requires_mounts_for(
2178 const char *filename
,
2180 const char *section
,
2181 unsigned section_line
,
2189 const char *word
, *state
;
2197 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2199 _cleanup_free_
char *n
;
2201 n
= strndup(word
, l
);
2205 if (!utf8_is_valid(n
)) {
2206 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2210 r
= unit_require_mounts_for(u
, n
);
2212 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2213 "Failed to add required mount for, ignoring: %s", rvalue
);
2217 if (!isempty(state
))
2218 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2219 "Trailing garbage, ignoring.");
2224 int config_parse_documentation(const char *unit
,
2225 const char *filename
,
2227 const char *section
,
2228 unsigned section_line
,
2244 if (isempty(rvalue
)) {
2245 /* Empty assignment resets the list */
2246 u
->documentation
= strv_free(u
->documentation
);
2250 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2251 rvalue
, data
, userdata
);
2255 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2257 if (documentation_url_is_valid(*a
))
2260 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2261 "Invalid URL, ignoring: %s", *a
);
2272 int config_parse_syscall_filter(
2274 const char *filename
,
2276 const char *section
,
2277 unsigned section_line
,
2284 static const char default_syscalls
[] =
2291 ExecContext
*c
= data
;
2293 bool invert
= false;
2294 const char *word
, *state
;
2303 if (isempty(rvalue
)) {
2304 /* Empty assignment resets the list */
2305 c
->syscall_filter
= set_free(c
->syscall_filter
);
2306 c
->syscall_whitelist
= false;
2310 if (rvalue
[0] == '~') {
2315 if (!c
->syscall_filter
) {
2316 c
->syscall_filter
= set_new(NULL
);
2317 if (!c
->syscall_filter
)
2321 /* Allow everything but the ones listed */
2322 c
->syscall_whitelist
= false;
2326 /* Allow nothing but the ones listed */
2327 c
->syscall_whitelist
= true;
2329 /* Accept default syscalls if we are on a whitelist */
2330 NULSTR_FOREACH(i
, default_syscalls
) {
2333 id
= seccomp_syscall_resolve_name(i
);
2337 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2346 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2347 _cleanup_free_
char *t
= NULL
;
2350 t
= strndup(word
, l
);
2354 id
= seccomp_syscall_resolve_name(t
);
2356 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2357 "Failed to parse system call, ignoring: %s", t
);
2361 /* If we previously wanted to forbid a syscall and now
2362 * we want to allow it, then remove it from the list
2364 if (!invert
== c
->syscall_whitelist
) {
2365 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2371 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2373 if (!isempty(state
))
2374 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2375 "Trailing garbage, ignoring.");
2377 /* Turn on NNP, but only if it wasn't configured explicitly
2378 * before, and only if we are in user mode. */
2379 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2380 c
->no_new_privileges
= true;
2385 int config_parse_syscall_archs(
2387 const char *filename
,
2389 const char *section
,
2390 unsigned section_line
,
2398 const char *word
, *state
;
2402 if (isempty(rvalue
)) {
2403 *archs
= set_free(*archs
);
2407 r
= set_ensure_allocated(archs
, NULL
);
2411 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2412 _cleanup_free_
char *t
= NULL
;
2415 t
= strndup(word
, l
);
2419 r
= seccomp_arch_from_string(t
, &a
);
2421 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2422 "Failed to parse system call architecture, ignoring: %s", t
);
2426 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2432 if (!isempty(state
))
2433 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2434 "Trailing garbage, ignoring.");
2439 int config_parse_syscall_errno(
2441 const char *filename
,
2443 const char *section
,
2444 unsigned section_line
,
2451 ExecContext
*c
= data
;
2458 if (isempty(rvalue
)) {
2459 /* Empty assignment resets to KILL */
2460 c
->syscall_errno
= 0;
2464 e
= errno_from_name(rvalue
);
2466 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2467 "Failed to parse error number, ignoring: %s", rvalue
);
2471 c
->syscall_errno
= e
;
2475 int config_parse_address_families(
2477 const char *filename
,
2479 const char *section
,
2480 unsigned section_line
,
2487 ExecContext
*c
= data
;
2488 bool invert
= false;
2489 const char *word
, *state
;
2497 if (isempty(rvalue
)) {
2498 /* Empty assignment resets the list */
2499 c
->address_families
= set_free(c
->address_families
);
2500 c
->address_families_whitelist
= false;
2504 if (rvalue
[0] == '~') {
2509 if (!c
->address_families
) {
2510 c
->address_families
= set_new(NULL
);
2511 if (!c
->address_families
)
2514 c
->address_families_whitelist
= !invert
;
2517 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2518 _cleanup_free_
char *t
= NULL
;
2521 t
= strndup(word
, l
);
2525 af
= af_from_name(t
);
2527 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2528 "Failed to parse address family, ignoring: %s", t
);
2532 /* If we previously wanted to forbid an address family and now
2533 * we want to allow it, then remove it from the list
2535 if (!invert
== c
->address_families_whitelist
) {
2536 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2542 set_remove(c
->address_families
, INT_TO_PTR(af
));
2544 if (!isempty(state
))
2545 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2546 "Trailing garbage, ignoring.");
2552 int config_parse_unit_slice(
2554 const char *filename
,
2556 const char *section
,
2557 unsigned section_line
,
2564 _cleanup_free_
char *k
= NULL
;
2565 Unit
*u
= userdata
, *slice
= NULL
;
2573 r
= unit_name_printf(u
, rvalue
, &k
);
2575 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2579 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2581 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2585 r
= unit_set_slice(u
, slice
);
2587 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2594 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2596 int config_parse_cpu_shares(
2598 const char *filename
,
2600 const char *section
,
2601 unsigned section_line
,
2608 uint64_t *shares
= data
;
2615 r
= cg_cpu_shares_parse(rvalue
, shares
);
2617 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2624 int config_parse_cpu_quota(
2626 const char *filename
,
2628 const char *section
,
2629 unsigned section_line
,
2636 CGroupContext
*c
= data
;
2643 if (isempty(rvalue
)) {
2644 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2648 if (!endswith(rvalue
, "%")) {
2650 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2651 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2655 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2656 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2657 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2661 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2666 int config_parse_memory_limit(
2668 const char *filename
,
2670 const char *section
,
2671 unsigned section_line
,
2678 CGroupContext
*c
= data
;
2682 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2683 c
->memory_limit
= (uint64_t) -1;
2687 r
= parse_size(rvalue
, 1024, &bytes
);
2688 if (r
< 0 || bytes
< 1) {
2689 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2693 c
->memory_limit
= bytes
;
2697 int config_parse_tasks_max(
2699 const char *filename
,
2701 const char *section
,
2702 unsigned section_line
,
2709 CGroupContext
*c
= data
;
2713 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2714 c
->tasks_max
= (uint64_t) -1;
2718 r
= safe_atou64(rvalue
, &u
);
2719 if (r
< 0 || u
< 1) {
2720 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2727 int config_parse_device_allow(
2729 const char *filename
,
2731 const char *section
,
2732 unsigned section_line
,
2739 _cleanup_free_
char *path
= NULL
;
2740 CGroupContext
*c
= data
;
2741 CGroupDeviceAllow
*a
;
2745 if (isempty(rvalue
)) {
2746 while (c
->device_allow
)
2747 cgroup_context_free_device_allow(c
, c
->device_allow
);
2752 n
= strcspn(rvalue
, WHITESPACE
);
2753 path
= strndup(rvalue
, n
);
2757 if (!startswith(path
, "/dev/") &&
2758 !startswith(path
, "block-") &&
2759 !startswith(path
, "char-")) {
2760 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2761 "Invalid device node path '%s'. Ignoring.", path
);
2765 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2769 if (!in_charset(m
, "rwm")) {
2770 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2771 "Invalid device rights '%s'. Ignoring.", m
);
2775 a
= new0(CGroupDeviceAllow
, 1);
2781 a
->r
= !!strchr(m
, 'r');
2782 a
->w
= !!strchr(m
, 'w');
2783 a
->m
= !!strchr(m
, 'm');
2785 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2789 int config_parse_blockio_weight(
2791 const char *filename
,
2793 const char *section
,
2794 unsigned section_line
,
2801 uint64_t *weight
= data
;
2808 r
= cg_blkio_weight_parse(rvalue
, weight
);
2810 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2817 int config_parse_blockio_device_weight(
2819 const char *filename
,
2821 const char *section
,
2822 unsigned section_line
,
2829 _cleanup_free_
char *path
= NULL
;
2830 CGroupBlockIODeviceWeight
*w
;
2831 CGroupContext
*c
= data
;
2841 if (isempty(rvalue
)) {
2842 while (c
->blockio_device_weights
)
2843 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2848 n
= strcspn(rvalue
, WHITESPACE
);
2849 weight
= rvalue
+ n
;
2850 weight
+= strspn(weight
, WHITESPACE
);
2852 if (isempty(weight
)) {
2853 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Expected block device and device weight. Ignoring.");
2857 path
= strndup(rvalue
, n
);
2861 if (!path_startswith(path
, "/dev")) {
2862 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid device node path '%s'. Ignoring.", path
);
2866 r
= cg_blkio_weight_parse(weight
, &u
);
2868 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2872 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2874 w
= new0(CGroupBlockIODeviceWeight
, 1);
2883 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2887 int config_parse_blockio_bandwidth(
2889 const char *filename
,
2891 const char *section
,
2892 unsigned section_line
,
2899 _cleanup_free_
char *path
= NULL
;
2900 CGroupBlockIODeviceBandwidth
*b
;
2901 CGroupContext
*c
= data
;
2902 const char *bandwidth
;
2912 read
= streq("BlockIOReadBandwidth", lvalue
);
2914 if (isempty(rvalue
)) {
2915 CGroupBlockIODeviceBandwidth
*next
;
2917 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2918 if (b
->read
== read
)
2919 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2924 n
= strcspn(rvalue
, WHITESPACE
);
2925 bandwidth
= rvalue
+ n
;
2926 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2929 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2930 "Expected space separated pair of device node and bandwidth. Ignoring.");
2934 path
= strndup(rvalue
, n
);
2938 if (!path_startswith(path
, "/dev")) {
2939 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2940 "Invalid device node path '%s'. Ignoring.", path
);
2944 r
= parse_size(bandwidth
, 1000, &bytes
);
2945 if (r
< 0 || bytes
<= 0) {
2946 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2947 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2951 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2957 b
->bandwidth
= bytes
;
2960 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2965 int config_parse_netclass(
2967 const char *filename
,
2969 const char *section
,
2970 unsigned section_line
,
2977 CGroupContext
*c
= data
;
2985 if (streq(rvalue
, "auto")) {
2986 c
->netclass_type
= CGROUP_NETCLASS_TYPE_AUTO
;
2990 r
= safe_atou32(rvalue
, &v
);
2992 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2993 "Netclass '%s' invalid. Ignoring.", rvalue
);
2997 if (v
> CGROUP_NETCLASS_FIXED_MAX
)
2998 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2999 "Fixed netclass %" PRIu32
" out of allowed range (0-%d). Applying anyway.", v
, (uint32_t) CGROUP_NETCLASS_FIXED_MAX
);
3002 c
->netclass_type
= CGROUP_NETCLASS_TYPE_FIXED
;
3007 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3009 int config_parse_job_mode_isolate(
3011 const char *filename
,
3013 const char *section
,
3014 unsigned section_line
,
3028 r
= parse_boolean(rvalue
);
3030 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3031 "Failed to parse boolean, ignoring: %s", rvalue
);
3035 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3039 int config_parse_runtime_directory(
3041 const char *filename
,
3043 const char *section
,
3044 unsigned section_line
,
3052 const char *word
, *state
;
3061 if (isempty(rvalue
)) {
3062 /* Empty assignment resets the list */
3063 *rt
= strv_free(*rt
);
3067 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3068 _cleanup_free_
char *n
;
3070 n
= strndup(word
, l
);
3074 if (!filename_is_valid(n
)) {
3075 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3076 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3080 r
= strv_push(rt
, n
);
3086 if (!isempty(state
))
3087 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3088 "Trailing garbage, ignoring.");
3093 int config_parse_set_status(
3095 const char *filename
,
3097 const char *section
,
3098 unsigned section_line
,
3106 const char *word
, *state
;
3108 ExitStatusSet
*status_set
= data
;
3115 /* Empty assignment resets the list */
3116 if (isempty(rvalue
)) {
3117 exit_status_set_free(status_set
);
3121 FOREACH_WORD(word
, l
, rvalue
, state
) {
3122 _cleanup_free_
char *temp
;
3126 temp
= strndup(word
, l
);
3130 r
= safe_atoi(temp
, &val
);
3132 val
= signal_from_string_try_harder(temp
);
3135 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3136 "Failed to parse value, ignoring: %s", word
);
3139 set
= &status_set
->signal
;
3141 if (val
< 0 || val
> 255) {
3142 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3143 "Value %d is outside range 0-255, ignoring", val
);
3146 set
= &status_set
->status
;
3149 r
= set_ensure_allocated(set
, NULL
);
3153 r
= set_put(*set
, INT_TO_PTR(val
));
3155 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3156 "Unable to store: %s", word
);
3160 if (!isempty(state
))
3161 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3162 "Trailing garbage, ignoring.");
3167 int config_parse_namespace_path_strv(
3169 const char *filename
,
3171 const char *section
,
3172 unsigned section_line
,
3180 const char *word
, *state
;
3189 if (isempty(rvalue
)) {
3190 /* Empty assignment resets the list */
3191 *sv
= strv_free(*sv
);
3195 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3196 _cleanup_free_
char *n
;
3199 n
= strndup(word
, l
);
3203 if (!utf8_is_valid(n
)) {
3204 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3208 offset
= n
[0] == '-';
3209 if (!path_is_absolute(n
+ offset
)) {
3210 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3211 "Not an absolute path, ignoring: %s", rvalue
);
3215 path_kill_slashes(n
);
3217 r
= strv_push(sv
, n
);
3223 if (!isempty(state
))
3224 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3225 "Trailing garbage, ignoring.");
3230 int config_parse_no_new_privileges(
3232 const char *filename
,
3234 const char *section
,
3235 unsigned section_line
,
3242 ExecContext
*c
= data
;
3250 k
= parse_boolean(rvalue
);
3252 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3253 "Failed to parse boolean value, ignoring: %s", rvalue
);
3257 c
->no_new_privileges
= !!k
;
3258 c
->no_new_privileges_set
= true;
3263 int config_parse_protect_home(
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_home
= PROTECT_HOME_YES
;
3290 c
->protect_home
= PROTECT_HOME_NO
;
3294 h
= protect_home_from_string(rvalue
);
3296 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3297 "Failed to parse protect home value, ignoring: %s", rvalue
);
3301 c
->protect_home
= h
;
3307 int config_parse_protect_system(
3309 const char *filename
,
3311 const char *section
,
3312 unsigned section_line
,
3319 ExecContext
*c
= data
;
3327 /* Our enum shall be a superset of booleans, hence first try
3328 * to parse as as boolean, and then as enum */
3330 k
= parse_boolean(rvalue
);
3332 c
->protect_system
= PROTECT_SYSTEM_YES
;
3334 c
->protect_system
= PROTECT_SYSTEM_NO
;
3338 s
= protect_system_from_string(rvalue
);
3340 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3341 "Failed to parse protect system value, ignoring: %s", rvalue
);
3345 c
->protect_system
= s
;
3351 #define FOLLOW_MAX 8
3353 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3364 /* This will update the filename pointer if the loaded file is
3365 * reached by a symlink. The old string will be freed. */
3368 char *target
, *name
;
3370 if (c
++ >= FOLLOW_MAX
)
3373 path_kill_slashes(*filename
);
3375 /* Add the file name we are currently looking at to
3376 * the names of this unit, but only if it is a valid
3378 name
= basename(*filename
);
3380 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3382 id
= set_get(names
, name
);
3388 r
= set_consume(names
, id
);
3394 /* Try to open the file name, but don't if its a symlink */
3395 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3402 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3403 r
= readlink_and_make_absolute(*filename
, &target
);
3411 f
= fdopen(fd
, "re");
3422 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3430 /* Let's try to add in all symlink names we found */
3431 while ((k
= set_steal_first(names
))) {
3433 /* First try to merge in the other name into our
3435 r
= unit_merge_by_name(*u
, k
);
3439 /* Hmm, we couldn't merge the other unit into
3440 * ours? Then let's try it the other way
3443 other
= manager_get_unit((*u
)->manager
, k
);
3447 r
= unit_merge(other
, *u
);
3450 return merge_by_names(u
, names
, NULL
);
3458 unit_choose_id(*u
, id
);
3466 static int load_from_path(Unit
*u
, const char *path
) {
3468 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3469 _cleanup_fclose_
FILE *f
= NULL
;
3470 _cleanup_free_
char *filename
= NULL
;
3478 symlink_names
= set_new(&string_hash_ops
);
3482 if (path_is_absolute(path
)) {
3484 filename
= strdup(path
);
3488 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3490 filename
= mfree(filename
);
3498 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3500 /* Instead of opening the path right away, we manually
3501 * follow all symlinks and add their name to our unit
3502 * name set while doing so */
3503 filename
= path_make_absolute(path
, *p
);
3507 if (u
->manager
->unit_path_cache
&&
3508 !set_get(u
->manager
->unit_path_cache
, filename
))
3511 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3514 filename
= mfree(filename
);
3518 /* Empty the symlink names for the next run */
3519 set_clear_free(symlink_names
);
3528 /* Hmm, no suitable file found? */
3532 r
= merge_by_names(&merged
, symlink_names
, id
);
3537 u
->load_state
= UNIT_MERGED
;
3541 if (fstat(fileno(f
), &st
) < 0)
3544 if (null_or_empty(&st
))
3545 u
->load_state
= UNIT_MASKED
;
3547 u
->load_state
= UNIT_LOADED
;
3549 /* Now, parse the file contents */
3550 r
= config_parse(u
->id
, filename
, f
,
3551 UNIT_VTABLE(u
)->sections
,
3552 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3553 false, true, false, u
);
3558 free(u
->fragment_path
);
3559 u
->fragment_path
= filename
;
3562 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3564 if (u
->source_path
) {
3565 if (stat(u
->source_path
, &st
) >= 0)
3566 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3568 u
->source_mtime
= 0;
3574 int unit_load_fragment(Unit
*u
) {
3580 assert(u
->load_state
== UNIT_STUB
);
3584 u
->load_state
= UNIT_LOADED
;
3588 /* First, try to find the unit under its id. We always look
3589 * for unit files in the default directories, to make it easy
3590 * to override things by placing things in /etc/systemd/system */
3591 r
= load_from_path(u
, u
->id
);
3595 /* Try to find an alias we can load this with */
3596 if (u
->load_state
== UNIT_STUB
) {
3597 SET_FOREACH(t
, u
->names
, i
) {
3602 r
= load_from_path(u
, t
);
3606 if (u
->load_state
!= UNIT_STUB
)
3611 /* And now, try looking for it under the suggested (originally linked) path */
3612 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3614 r
= load_from_path(u
, u
->fragment_path
);
3618 if (u
->load_state
== UNIT_STUB
)
3619 /* Hmm, this didn't work? Then let's get rid
3620 * of the fragment path stored for us, so that
3621 * we don't point to an invalid location. */
3622 u
->fragment_path
= mfree(u
->fragment_path
);
3625 /* Look for a template */
3626 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3627 _cleanup_free_
char *k
= NULL
;
3629 r
= unit_name_template(u
->id
, &k
);
3633 r
= load_from_path(u
, k
);
3637 if (u
->load_state
== UNIT_STUB
) {
3638 SET_FOREACH(t
, u
->names
, i
) {
3639 _cleanup_free_
char *z
= NULL
;
3644 r
= unit_name_template(t
, &z
);
3648 r
= load_from_path(u
, z
);
3652 if (u
->load_state
!= UNIT_STUB
)
3661 void unit_dump_config_items(FILE *f
) {
3662 static const struct {
3663 const ConfigParserCallback callback
;
3666 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3667 { config_parse_warn_compat
, "NOTSUPPORTED" },
3669 { config_parse_int
, "INTEGER" },
3670 { config_parse_unsigned
, "UNSIGNED" },
3671 { config_parse_iec_size
, "SIZE" },
3672 { config_parse_iec_uint64
, "SIZE" },
3673 { config_parse_si_size
, "SIZE" },
3674 { config_parse_bool
, "BOOLEAN" },
3675 { config_parse_string
, "STRING" },
3676 { config_parse_path
, "PATH" },
3677 { config_parse_unit_path_printf
, "PATH" },
3678 { config_parse_strv
, "STRING [...]" },
3679 { config_parse_exec_nice
, "NICE" },
3680 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3681 { config_parse_exec_io_class
, "IOCLASS" },
3682 { config_parse_exec_io_priority
, "IOPRIORITY" },
3683 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3684 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3685 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3686 { config_parse_mode
, "MODE" },
3687 { config_parse_unit_env_file
, "FILE" },
3688 { config_parse_output
, "OUTPUT" },
3689 { config_parse_input
, "INPUT" },
3690 { config_parse_log_facility
, "FACILITY" },
3691 { config_parse_log_level
, "LEVEL" },
3692 { config_parse_exec_capabilities
, "CAPABILITIES" },
3693 { config_parse_exec_secure_bits
, "SECUREBITS" },
3694 { config_parse_bounding_set
, "BOUNDINGSET" },
3695 { config_parse_limit
, "LIMIT" },
3696 { config_parse_unit_deps
, "UNIT [...]" },
3697 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3698 { config_parse_service_type
, "SERVICETYPE" },
3699 { config_parse_service_restart
, "SERVICERESTART" },
3700 #ifdef HAVE_SYSV_COMPAT
3701 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3703 { config_parse_kill_mode
, "KILLMODE" },
3704 { config_parse_signal
, "SIGNAL" },
3705 { config_parse_socket_listen
, "SOCKET [...]" },
3706 { config_parse_socket_bind
, "SOCKETBIND" },
3707 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3708 { config_parse_sec
, "SECONDS" },
3709 { config_parse_nsec
, "NANOSECONDS" },
3710 { config_parse_namespace_path_strv
, "PATH [...]" },
3711 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3712 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3713 { config_parse_unit_string_printf
, "STRING" },
3714 { config_parse_trigger_unit
, "UNIT" },
3715 { config_parse_timer
, "TIMER" },
3716 { config_parse_path_spec
, "PATH" },
3717 { config_parse_notify_access
, "ACCESS" },
3718 { config_parse_ip_tos
, "TOS" },
3719 { config_parse_unit_condition_path
, "CONDITION" },
3720 { config_parse_unit_condition_string
, "CONDITION" },
3721 { config_parse_unit_condition_null
, "CONDITION" },
3722 { config_parse_unit_slice
, "SLICE" },
3723 { config_parse_documentation
, "URL" },
3724 { config_parse_service_timeout
, "SECONDS" },
3725 { config_parse_failure_action
, "ACTION" },
3726 { config_parse_set_status
, "STATUS" },
3727 { config_parse_service_sockets
, "SOCKETS" },
3728 { config_parse_environ
, "ENVIRON" },
3730 { config_parse_syscall_filter
, "SYSCALLS" },
3731 { config_parse_syscall_archs
, "ARCHS" },
3732 { config_parse_syscall_errno
, "ERRNO" },
3733 { config_parse_address_families
, "FAMILIES" },
3735 { config_parse_cpu_shares
, "SHARES" },
3736 { config_parse_memory_limit
, "LIMIT" },
3737 { config_parse_device_allow
, "DEVICE" },
3738 { config_parse_device_policy
, "POLICY" },
3739 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3740 { config_parse_blockio_weight
, "WEIGHT" },
3741 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3742 { config_parse_long
, "LONG" },
3743 { config_parse_socket_service
, "SERVICE" },
3745 { config_parse_exec_selinux_context
, "LABEL" },
3747 { config_parse_job_mode
, "MODE" },
3748 { config_parse_job_mode_isolate
, "BOOLEAN" },
3749 { config_parse_personality
, "PERSONALITY" },
3752 const char *prev
= NULL
;
3757 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3758 const char *rvalue
= "OTHER", *lvalue
;
3762 const ConfigPerfItem
*p
;
3764 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3766 dot
= strchr(i
, '.');
3767 lvalue
= dot
? dot
+ 1 : i
;
3771 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3775 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3778 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3779 if (p
->parse
== table
[j
].callback
) {
3780 rvalue
= table
[j
].rvalue
;
3784 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);