1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
7 Copyright 2012 Holger Hans Peter Freyther
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <linux/oom.h>
30 #include <sys/resource.h>
38 #include "conf-parser.h"
39 #include "load-fragment.h"
42 #include "securebits.h"
44 #include "unit-name.h"
45 #include "unit-printf.h"
47 #include "path-util.h"
51 #include "bus-error.h"
52 #include "errno-list.h"
55 #include "signal-util.h"
56 #include "bus-internal.h"
59 #include "seccomp-util.h"
62 int config_parse_warn_compat(
67 unsigned section_line
,
73 Disabled reason
= ltype
;
76 case DISABLED_CONFIGURATION
:
77 log_syntax(unit
, LOG_DEBUG
, filename
, line
, EINVAL
,
78 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
81 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
82 "Support for option %s= has been removed and it is ignored", lvalue
);
84 case DISABLED_EXPERIMENTAL
:
85 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
86 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
93 int config_parse_unit_deps(const char *unit
,
97 unsigned section_line
,
104 UnitDependency d
= ltype
;
106 const char *word
, *state
;
113 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
114 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
117 t
= strndup(word
, l
);
121 r
= unit_name_printf(u
, t
, &k
);
123 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
124 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
128 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
130 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
131 "Failed to add dependency on %s, ignoring: %s", k
, strerror(-r
));
134 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
139 int config_parse_unit_string_printf(
141 const char *filename
,
144 unsigned section_line
,
151 _cleanup_free_
char *k
= NULL
;
160 r
= unit_full_printf(u
, rvalue
, &k
);
162 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
166 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
169 int config_parse_unit_strv_printf(const char *unit
,
170 const char *filename
,
173 unsigned section_line
,
181 _cleanup_free_
char *k
= NULL
;
189 r
= unit_full_printf(u
, rvalue
, &k
);
191 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
192 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
194 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
195 k
? k
: rvalue
, data
, userdata
);
198 int config_parse_unit_path_printf(const char *unit
,
199 const char *filename
,
202 unsigned section_line
,
209 _cleanup_free_
char *k
= NULL
;
218 r
= unit_full_printf(u
, rvalue
, &k
);
220 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
224 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
227 int config_parse_unit_path_strv_printf(
229 const char *filename
,
232 unsigned section_line
,
240 const char *word
, *state
;
250 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
251 _cleanup_free_
char *k
= NULL
;
257 r
= unit_full_printf(u
, t
, &k
);
259 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", t
, strerror(-r
));
263 if (!utf8_is_valid(k
)) {
264 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
268 if (!path_is_absolute(k
)) {
269 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Symlink path %s is not absolute, ignoring: %s", k
, strerror(-r
));
273 path_kill_slashes(k
);
282 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
287 int config_parse_socket_listen(const char *unit
,
288 const char *filename
,
291 unsigned section_line
,
298 _cleanup_free_ SocketPort
*p
= NULL
;
310 if (isempty(rvalue
)) {
311 /* An empty assignment removes all ports */
312 socket_free_ports(s
);
316 p
= new0(SocketPort
, 1);
320 if (ltype
!= SOCKET_SOCKET
) {
323 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
325 p
->path
= strdup(rvalue
);
329 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
330 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
333 path_kill_slashes(p
->path
);
335 } else if (streq(lvalue
, "ListenNetlink")) {
336 _cleanup_free_
char *k
= NULL
;
338 p
->type
= SOCKET_SOCKET
;
339 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
341 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
342 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
344 r
= socket_address_parse_netlink(&p
->address
, k
?: rvalue
);
346 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
347 "Failed to parse address value, ignoring: %s", rvalue
);
352 _cleanup_free_
char *k
= NULL
;
354 p
->type
= SOCKET_SOCKET
;
355 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
357 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
358 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
360 r
= socket_address_parse_and_warn(&p
->address
, k
? k
: rvalue
);
362 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
363 "Failed to parse address value, ignoring: %s", rvalue
);
367 if (streq(lvalue
, "ListenStream"))
368 p
->address
.type
= SOCK_STREAM
;
369 else if (streq(lvalue
, "ListenDatagram"))
370 p
->address
.type
= SOCK_DGRAM
;
372 assert(streq(lvalue
, "ListenSequentialPacket"));
373 p
->address
.type
= SOCK_SEQPACKET
;
376 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
377 log_syntax(unit
, LOG_ERR
, filename
, line
, EOPNOTSUPP
,
378 "Address family not supported, ignoring: %s", rvalue
);
387 LIST_FIND_TAIL(port
, s
->ports
, tail
);
388 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
390 LIST_PREPEND(port
, s
->ports
, p
);
396 int config_parse_socket_bind(const char *unit
,
397 const char *filename
,
400 unsigned section_line
,
408 SocketAddressBindIPv6Only b
;
417 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
421 r
= parse_boolean(rvalue
);
423 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
424 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
428 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
430 s
->bind_ipv6_only
= b
;
435 int config_parse_exec_nice(const char *unit
,
436 const char *filename
,
439 unsigned section_line
,
446 ExecContext
*c
= data
;
454 r
= safe_atoi(rvalue
, &priority
);
456 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
457 "Failed to parse nice priority, ignoring: %s. ", rvalue
);
461 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
462 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
463 "Nice priority out of range, ignoring: %s", rvalue
);
473 int config_parse_exec_oom_score_adjust(const char* unit
,
474 const char *filename
,
477 unsigned section_line
,
484 ExecContext
*c
= data
;
492 r
= safe_atoi(rvalue
, &oa
);
494 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
495 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
499 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
500 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
501 "OOM score adjust value out of range, ignoring: %s", rvalue
);
505 c
->oom_score_adjust
= oa
;
506 c
->oom_score_adjust_set
= true;
511 int config_parse_exec(
513 const char *filename
,
516 unsigned section_line
,
523 ExecCommand
**e
= data
;
535 rvalue
+= strspn(rvalue
, WHITESPACE
);
538 if (isempty(rvalue
)) {
539 /* An empty assignment resets the list */
540 *e
= exec_command_free_list(*e
);
546 _cleanup_strv_free_
char **n
= NULL
;
547 size_t nlen
= 0, nbufsize
= 0;
548 _cleanup_free_ ExecCommand
*nce
= NULL
;
549 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
551 bool separate_argv0
= false, ignore
= false;
555 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
560 for (i
= 0; i
< 2; i
++) {
561 /* We accept an absolute path as first argument, or
562 * alternatively an absolute prefixed with @ to allow
563 * overriding of argv[0]. */
564 if (*f
== '-' && !ignore
)
566 else if (*f
== '@' && !separate_argv0
)
567 separate_argv0
= true;
574 /* First word is either "-" or "@" with no command. */
575 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
576 "Empty path in command line, ignoring: \"%s\"", rvalue
);
580 if (!string_is_safe(f
)) {
581 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
582 "Executable path contains special characters, ignoring: %s", rvalue
);
585 if (!path_is_absolute(f
)) {
586 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
587 "Executable path is not absolute, ignoring: %s", rvalue
);
590 if (endswith(f
, "/")) {
591 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
592 "Executable path specifies a directory, ignoring: %s", rvalue
);
596 if (f
== firstword
) {
605 if (!separate_argv0
) {
606 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
615 path_kill_slashes(path
);
617 while (!isempty(p
)) {
618 _cleanup_free_
char *word
= NULL
;
620 /* Check explicitly for an unquoted semicolon as
621 * command separator token. */
622 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
624 p
+= strspn(p
, WHITESPACE
);
629 /* Check for \; explicitly, to not confuse it with \\;
630 * or "\;" or "\\;" etc. extract_first_word would
631 * return the same for all of those. */
632 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
634 p
+= strspn(p
, WHITESPACE
);
635 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
645 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
651 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
659 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
660 "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
664 nce
= new0(ExecCommand
, 1);
670 nce
->ignore
= ignore
;
672 exec_command_append_list(e
, nce
);
674 /* Do not _cleanup_free_ these. */
685 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
686 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
688 int config_parse_socket_bindtodevice(const char* unit
,
689 const char *filename
,
692 unsigned section_line
,
707 if (rvalue
[0] && !streq(rvalue
, "*")) {
714 free(s
->bind_to_device
);
715 s
->bind_to_device
= n
;
720 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
721 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
723 int config_parse_exec_io_class(const char *unit
,
724 const char *filename
,
727 unsigned section_line
,
734 ExecContext
*c
= data
;
742 x
= ioprio_class_from_string(rvalue
);
744 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
745 "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
749 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
750 c
->ioprio_set
= true;
755 int config_parse_exec_io_priority(const char *unit
,
756 const char *filename
,
759 unsigned section_line
,
766 ExecContext
*c
= data
;
774 r
= safe_atoi(rvalue
, &i
);
775 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
776 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
777 "Failed to parse IO priority, ignoring: %s", rvalue
);
781 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
782 c
->ioprio_set
= true;
787 int config_parse_exec_cpu_sched_policy(const char *unit
,
788 const char *filename
,
791 unsigned section_line
,
799 ExecContext
*c
= data
;
807 x
= sched_policy_from_string(rvalue
);
809 log_syntax(unit
, LOG_ERR
, filename
, line
, -x
,
810 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
814 c
->cpu_sched_policy
= x
;
815 /* Moving to or from real-time policy? We need to adjust the priority */
816 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
817 c
->cpu_sched_set
= true;
822 int config_parse_exec_cpu_sched_prio(const char *unit
,
823 const char *filename
,
826 unsigned section_line
,
833 ExecContext
*c
= data
;
841 r
= safe_atoi(rvalue
, &i
);
843 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
844 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
848 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
849 min
= sched_get_priority_min(c
->cpu_sched_policy
);
850 max
= sched_get_priority_max(c
->cpu_sched_policy
);
852 if (i
< min
|| i
> max
) {
853 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
854 "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
858 c
->cpu_sched_priority
= i
;
859 c
->cpu_sched_set
= true;
864 int config_parse_exec_cpu_affinity(const char *unit
,
865 const char *filename
,
868 unsigned section_line
,
875 ExecContext
*c
= data
;
876 const char *word
, *state
;
884 if (isempty(rvalue
)) {
885 /* An empty assignment resets the CPU list */
892 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
893 _cleanup_free_
char *t
= NULL
;
897 t
= strndup(word
, l
);
901 r
= safe_atou(t
, &cpu
);
904 c
->cpuset
= cpu_set_malloc(&c
->cpuset_ncpus
);
909 if (r
< 0 || cpu
>= c
->cpuset_ncpus
) {
910 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
911 "Failed to parse CPU affinity '%s', ignoring: %s", t
, rvalue
);
915 CPU_SET_S(cpu
, CPU_ALLOC_SIZE(c
->cpuset_ncpus
), c
->cpuset
);
918 log_syntax(unit
, LOG_WARNING
, filename
, line
, EINVAL
,
919 "Trailing garbage, ignoring.");
924 int config_parse_exec_capabilities(const char *unit
,
925 const char *filename
,
928 unsigned section_line
,
935 ExecContext
*c
= data
;
943 cap
= cap_from_text(rvalue
);
945 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
,
946 "Failed to parse capabilities, ignoring: %s", rvalue
);
951 cap_free(c
->capabilities
);
952 c
->capabilities
= cap
;
957 int config_parse_exec_secure_bits(const char *unit
,
958 const char *filename
,
961 unsigned section_line
,
968 ExecContext
*c
= data
;
970 const char *word
, *state
;
977 if (isempty(rvalue
)) {
978 /* An empty assignment resets the field */
983 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
984 if (first_word(word
, "keep-caps"))
985 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
986 else if (first_word(word
, "keep-caps-locked"))
987 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
988 else if (first_word(word
, "no-setuid-fixup"))
989 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
990 else if (first_word(word
, "no-setuid-fixup-locked"))
991 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
992 else if (first_word(word
, "noroot"))
993 c
->secure_bits
|= 1<<SECURE_NOROOT
;
994 else if (first_word(word
, "noroot-locked"))
995 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
997 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
998 "Failed to parse secure bits, ignoring: %s", rvalue
);
1002 if (!isempty(state
))
1003 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1004 "Invalid syntax, garbage at the end, ignoring.");
1009 int config_parse_bounding_set(const char *unit
,
1010 const char *filename
,
1012 const char *section
,
1013 unsigned section_line
,
1020 uint64_t *capability_bounding_set_drop
= data
;
1021 const char *word
, *state
;
1023 bool invert
= false;
1031 if (rvalue
[0] == '~') {
1036 /* Note that we store this inverted internally, since the
1037 * kernel wants it like this. But we actually expose it
1038 * non-inverted everywhere to have a fully normalized
1041 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1042 _cleanup_free_
char *t
= NULL
;
1045 t
= strndup(word
, l
);
1049 cap
= capability_from_name(t
);
1051 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
, "Failed to parse capability in bounding set, ignoring: %s", t
);
1055 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1057 if (!isempty(state
))
1058 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1059 "Trailing garbage, ignoring.");
1062 *capability_bounding_set_drop
|= sum
;
1064 *capability_bounding_set_drop
|= ~sum
;
1069 int config_parse_limit(const char *unit
,
1070 const char *filename
,
1072 const char *section
,
1073 unsigned section_line
,
1080 struct rlimit
**rl
= data
;
1081 unsigned long long u
;
1090 if (streq(rvalue
, "infinity"))
1091 u
= (unsigned long long) RLIM_INFINITY
;
1095 r
= safe_atollu(rvalue
, &u
);
1097 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1098 "Failed to parse resource value, ignoring: %s", rvalue
);
1104 *rl
= new(struct rlimit
, 1);
1109 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1113 #ifdef HAVE_SYSV_COMPAT
1114 int config_parse_sysv_priority(const char *unit
,
1115 const char *filename
,
1117 const char *section
,
1118 unsigned section_line
,
1125 int *priority
= data
;
1133 r
= safe_atoi(rvalue
, &i
);
1134 if (r
< 0 || i
< 0) {
1135 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1136 "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1140 *priority
= (int) i
;
1145 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1147 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1149 int config_parse_kill_signal(const char *unit
,
1150 const char *filename
,
1152 const char *section
,
1153 unsigned section_line
,
1168 r
= signal_from_string_try_harder(rvalue
);
1170 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1171 "Failed to parse kill signal, ignoring: %s", rvalue
);
1179 int config_parse_exec_mount_flags(const char *unit
,
1180 const char *filename
,
1182 const char *section
,
1183 unsigned section_line
,
1190 ExecContext
*c
= data
;
1191 const char *word
, *state
;
1193 unsigned long flags
= 0;
1200 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1201 _cleanup_free_
char *t
;
1203 t
= strndup(word
, l
);
1207 if (streq(t
, "shared"))
1209 else if (streq(t
, "slave"))
1211 else if (streq(t
, "private"))
1214 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1218 if (!isempty(state
))
1219 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1221 c
->mount_flags
= flags
;
1225 int config_parse_exec_selinux_context(
1227 const char *filename
,
1229 const char *section
,
1230 unsigned section_line
,
1237 ExecContext
*c
= data
;
1248 if (isempty(rvalue
)) {
1249 free(c
->selinux_context
);
1250 c
->selinux_context
= NULL
;
1251 c
->selinux_context_ignore
= false;
1255 if (rvalue
[0] == '-') {
1261 r
= unit_name_printf(u
, rvalue
, &k
);
1263 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1264 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1268 free(c
->selinux_context
);
1269 c
->selinux_context
= k
;
1270 c
->selinux_context_ignore
= ignore
;
1275 int config_parse_exec_apparmor_profile(
1277 const char *filename
,
1279 const char *section
,
1280 unsigned section_line
,
1287 ExecContext
*c
= data
;
1298 if (isempty(rvalue
)) {
1299 free(c
->apparmor_profile
);
1300 c
->apparmor_profile
= NULL
;
1301 c
->apparmor_profile_ignore
= false;
1305 if (rvalue
[0] == '-') {
1311 r
= unit_name_printf(u
, rvalue
, &k
);
1313 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1314 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1318 free(c
->apparmor_profile
);
1319 c
->apparmor_profile
= k
;
1320 c
->apparmor_profile_ignore
= ignore
;
1325 int config_parse_exec_smack_process_label(
1327 const char *filename
,
1329 const char *section
,
1330 unsigned section_line
,
1337 ExecContext
*c
= data
;
1348 if (isempty(rvalue
)) {
1349 free(c
->smack_process_label
);
1350 c
->smack_process_label
= NULL
;
1351 c
->smack_process_label_ignore
= false;
1355 if (rvalue
[0] == '-') {
1361 r
= unit_name_printf(u
, rvalue
, &k
);
1363 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1364 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1368 free(c
->smack_process_label
);
1369 c
->smack_process_label
= k
;
1370 c
->smack_process_label_ignore
= ignore
;
1375 int config_parse_timer(const char *unit
,
1376 const char *filename
,
1378 const char *section
,
1379 unsigned section_line
,
1390 CalendarSpec
*c
= NULL
;
1397 if (isempty(rvalue
)) {
1398 /* Empty assignment resets list */
1399 timer_free_values(t
);
1403 b
= timer_base_from_string(lvalue
);
1405 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
,
1406 "Failed to parse timer base, ignoring: %s", lvalue
);
1410 if (b
== TIMER_CALENDAR
) {
1411 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1412 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1413 "Failed to parse calendar specification, ignoring: %s",
1418 if (parse_sec(rvalue
, &u
) < 0) {
1419 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1420 "Failed to parse timer value, ignoring: %s",
1426 v
= new0(TimerValue
, 1);
1428 calendar_spec_free(c
);
1434 v
->calendar_spec
= c
;
1436 LIST_PREPEND(value
, t
->values
, v
);
1441 int config_parse_trigger_unit(
1443 const char *filename
,
1445 const char *section
,
1446 unsigned section_line
,
1453 _cleanup_free_
char *p
= NULL
;
1463 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1464 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1465 "Multiple units to trigger specified, ignoring: %s", rvalue
);
1469 r
= unit_name_printf(u
, rvalue
, &p
);
1471 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1472 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1474 type
= unit_name_to_type(p
?: rvalue
);
1476 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1477 "Unit type not valid, ignoring: %s", rvalue
);
1481 if (type
== u
->type
) {
1482 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1483 "Trigger cannot be of same type, ignoring: %s", rvalue
);
1487 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
?: rvalue
, NULL
, true);
1489 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1490 "Failed to add trigger on %s, ignoring: %s", p
?: rvalue
, strerror(-r
));
1497 int config_parse_path_spec(const char *unit
,
1498 const char *filename
,
1500 const char *section
,
1501 unsigned section_line
,
1511 _cleanup_free_
char *k
= NULL
;
1519 if (isempty(rvalue
)) {
1520 /* Empty assignment clears list */
1525 b
= path_type_from_string(lvalue
);
1527 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1528 "Failed to parse path type, ignoring: %s", lvalue
);
1532 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1538 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1539 "Failed to resolve unit specifiers on %s. Ignoring.",
1543 if (!path_is_absolute(k
)) {
1544 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1545 "Path is not absolute, ignoring: %s", k
);
1549 s
= new0(PathSpec
, 1);
1554 s
->path
= path_kill_slashes(k
);
1559 LIST_PREPEND(spec
, p
->specs
, s
);
1564 int config_parse_socket_service(
1566 const char *filename
,
1568 const char *section
,
1569 unsigned section_line
,
1576 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1580 _cleanup_free_
char *p
= NULL
;
1587 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1589 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1593 if (!endswith(p
, ".service")) {
1594 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type service, ignoring: %s", rvalue
);
1598 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1600 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1604 unit_ref_set(&s
->service
, x
);
1609 int config_parse_service_sockets(
1611 const char *filename
,
1613 const char *section
,
1614 unsigned section_line
,
1622 const char *word
, *state
;
1631 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1632 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1634 t
= strndup(word
, l
);
1638 r
= unit_name_printf(UNIT(s
), t
, &k
);
1640 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1644 if (!endswith(k
, ".socket")) {
1645 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type socket, ignoring: %s", k
);
1649 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1651 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1653 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1655 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1657 if (!isempty(state
))
1658 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1663 int config_parse_bus_name(
1665 const char *filename
,
1667 const char *section
,
1668 unsigned section_line
,
1675 _cleanup_free_
char *k
= NULL
;
1684 r
= unit_full_printf(u
, rvalue
, &k
);
1686 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1690 if (!service_name_is_valid(k
)) {
1691 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bus name %s, ignoring.", k
);
1695 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1698 int config_parse_service_timeout(const char *unit
,
1699 const char *filename
,
1701 const char *section
,
1702 unsigned section_line
,
1709 Service
*s
= userdata
;
1717 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1718 rvalue
, data
, userdata
);
1722 if (streq(lvalue
, "TimeoutSec")) {
1723 s
->start_timeout_defined
= true;
1724 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1725 } else if (streq(lvalue
, "TimeoutStartSec"))
1726 s
->start_timeout_defined
= true;
1731 int config_parse_busname_service(
1733 const char *filename
,
1735 const char *section
,
1736 unsigned section_line
,
1743 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1747 _cleanup_free_
char *p
= NULL
;
1754 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1756 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1757 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1761 if (!endswith(p
, ".service")) {
1762 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1763 "Unit must be of type service, ignoring: %s", rvalue
);
1767 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1769 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1770 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1774 unit_ref_set(&n
->service
, x
);
1779 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1781 int config_parse_bus_policy(
1783 const char *filename
,
1785 const char *section
,
1786 unsigned section_line
,
1793 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1794 _cleanup_free_
char *id_str
= NULL
;
1795 BusName
*busname
= data
;
1803 p
= new0(BusNamePolicy
, 1);
1807 if (streq(lvalue
, "AllowUser"))
1808 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1809 else if (streq(lvalue
, "AllowGroup"))
1810 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1812 assert_not_reached("Unknown lvalue");
1814 id_str
= strdup(rvalue
);
1818 access_str
= strpbrk(id_str
, WHITESPACE
);
1820 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1821 "Invalid busname policy value '%s'", rvalue
);
1827 access_str
+= strspn(access_str
, WHITESPACE
);
1829 p
->access
= bus_policy_access_from_string(access_str
);
1830 if (p
->access
< 0) {
1831 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1832 "Invalid busname policy access type '%s'", access_str
);
1839 LIST_PREPEND(policy
, busname
->policy
, p
);
1845 int config_parse_bus_endpoint_policy(
1847 const char *filename
,
1849 const char *section
,
1850 unsigned section_line
,
1857 _cleanup_free_
char *name
= NULL
;
1858 BusPolicyAccess access
;
1859 ExecContext
*c
= data
;
1868 name
= strdup(rvalue
);
1872 access_str
= strpbrk(name
, WHITESPACE
);
1874 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1875 "Invalid endpoint policy value '%s'", rvalue
);
1881 access_str
+= strspn(access_str
, WHITESPACE
);
1883 access
= bus_policy_access_from_string(access_str
);
1884 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1885 access
>= _BUS_POLICY_ACCESS_MAX
) {
1886 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1887 "Invalid endpoint policy access type '%s'", access_str
);
1891 if (!c
->bus_endpoint
) {
1892 r
= bus_endpoint_new(&c
->bus_endpoint
);
1898 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1901 int config_parse_unit_env_file(const char *unit
,
1902 const char *filename
,
1904 const char *section
,
1905 unsigned section_line
,
1914 _cleanup_free_
char *n
= NULL
;
1923 if (isempty(rvalue
)) {
1924 /* Empty assignment frees the list */
1930 r
= unit_full_printf(u
, rvalue
, &n
);
1932 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1933 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1936 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1937 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1938 "Path '%s' is not absolute, ignoring.", s
);
1942 r
= strv_extend(env
, s
);
1949 int config_parse_environ(const char *unit
,
1950 const char *filename
,
1952 const char *section
,
1953 unsigned section_line
,
1962 const char *word
, *state
;
1964 _cleanup_free_
char *k
= NULL
;
1972 if (isempty(rvalue
)) {
1973 /* Empty assignment resets the list */
1980 r
= unit_full_printf(u
, rvalue
, &k
);
1982 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1990 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1991 _cleanup_free_
char *n
= NULL
;
1994 r
= cunescape_length(word
, l
, 0, &n
);
1996 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
2000 if (!env_assignment_is_valid(n
)) {
2001 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid environment assignment, ignoring: %s", rvalue
);
2005 x
= strv_env_set(*env
, n
);
2012 if (!isempty(state
))
2013 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2014 "Trailing garbage, ignoring.");
2019 int config_parse_ip_tos(const char *unit
,
2020 const char *filename
,
2022 const char *section
,
2023 unsigned section_line
,
2030 int *ip_tos
= data
, x
;
2037 x
= ip_tos_from_string(rvalue
);
2039 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2040 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2048 int config_parse_unit_condition_path(
2050 const char *filename
,
2052 const char *section
,
2053 unsigned section_line
,
2060 _cleanup_free_
char *p
= NULL
;
2061 Condition
**list
= data
, *c
;
2062 ConditionType t
= ltype
;
2063 bool trigger
, negate
;
2072 if (isempty(rvalue
)) {
2073 /* Empty assignment resets the list */
2074 *list
= condition_free_list(*list
);
2078 trigger
= rvalue
[0] == '|';
2082 negate
= rvalue
[0] == '!';
2086 r
= unit_full_printf(u
, rvalue
, &p
);
2088 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2092 if (!path_is_absolute(p
)) {
2093 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2097 c
= condition_new(t
, p
, trigger
, negate
);
2101 LIST_PREPEND(conditions
, *list
, c
);
2105 int config_parse_unit_condition_string(
2107 const char *filename
,
2109 const char *section
,
2110 unsigned section_line
,
2117 _cleanup_free_
char *s
= NULL
;
2118 Condition
**list
= data
, *c
;
2119 ConditionType t
= ltype
;
2120 bool trigger
, negate
;
2129 if (isempty(rvalue
)) {
2130 /* Empty assignment resets the list */
2131 *list
= condition_free_list(*list
);
2135 trigger
= rvalue
[0] == '|';
2139 negate
= rvalue
[0] == '!';
2143 r
= unit_full_printf(u
, rvalue
, &s
);
2145 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2149 c
= condition_new(t
, s
, trigger
, negate
);
2153 LIST_PREPEND(conditions
, *list
, c
);
2157 int config_parse_unit_condition_null(
2159 const char *filename
,
2161 const char *section
,
2162 unsigned section_line
,
2169 Condition
**list
= data
, *c
;
2170 bool trigger
, negate
;
2178 if (isempty(rvalue
)) {
2179 /* Empty assignment resets the list */
2180 *list
= condition_free_list(*list
);
2184 trigger
= rvalue
[0] == '|';
2188 negate
= rvalue
[0] == '!';
2192 b
= parse_boolean(rvalue
);
2194 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2201 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2205 LIST_PREPEND(conditions
, *list
, c
);
2209 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2210 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2212 int config_parse_unit_requires_mounts_for(
2214 const char *filename
,
2216 const char *section
,
2217 unsigned section_line
,
2225 const char *word
, *state
;
2233 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2235 _cleanup_free_
char *n
;
2237 n
= strndup(word
, l
);
2241 if (!utf8_is_valid(n
)) {
2242 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2246 r
= unit_require_mounts_for(u
, n
);
2248 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2249 "Failed to add required mount for, ignoring: %s", rvalue
);
2253 if (!isempty(state
))
2254 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2255 "Trailing garbage, ignoring.");
2260 int config_parse_documentation(const char *unit
,
2261 const char *filename
,
2263 const char *section
,
2264 unsigned section_line
,
2280 if (isempty(rvalue
)) {
2281 /* Empty assignment resets the list */
2282 strv_free(u
->documentation
);
2283 u
->documentation
= NULL
;
2287 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2288 rvalue
, data
, userdata
);
2292 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2294 if (documentation_url_is_valid(*a
))
2297 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2298 "Invalid URL, ignoring: %s", *a
);
2309 int config_parse_syscall_filter(
2311 const char *filename
,
2313 const char *section
,
2314 unsigned section_line
,
2321 static const char default_syscalls
[] =
2328 ExecContext
*c
= data
;
2330 bool invert
= false;
2331 const char *word
, *state
;
2340 if (isempty(rvalue
)) {
2341 /* Empty assignment resets the list */
2342 set_free(c
->syscall_filter
);
2343 c
->syscall_filter
= NULL
;
2344 c
->syscall_whitelist
= false;
2348 if (rvalue
[0] == '~') {
2353 if (!c
->syscall_filter
) {
2354 c
->syscall_filter
= set_new(NULL
);
2355 if (!c
->syscall_filter
)
2359 /* Allow everything but the ones listed */
2360 c
->syscall_whitelist
= false;
2364 /* Allow nothing but the ones listed */
2365 c
->syscall_whitelist
= true;
2367 /* Accept default syscalls if we are on a whitelist */
2368 NULSTR_FOREACH(i
, default_syscalls
) {
2371 id
= seccomp_syscall_resolve_name(i
);
2375 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2384 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2385 _cleanup_free_
char *t
= NULL
;
2388 t
= strndup(word
, l
);
2392 id
= seccomp_syscall_resolve_name(t
);
2394 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2395 "Failed to parse system call, ignoring: %s", t
);
2399 /* If we previously wanted to forbid a syscall and now
2400 * we want to allow it, then remove it from the list
2402 if (!invert
== c
->syscall_whitelist
) {
2403 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2409 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2411 if (!isempty(state
))
2412 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2413 "Trailing garbage, ignoring.");
2415 /* Turn on NNP, but only if it wasn't configured explicitly
2416 * before, and only if we are in user mode. */
2417 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2418 c
->no_new_privileges
= true;
2423 int config_parse_syscall_archs(
2425 const char *filename
,
2427 const char *section
,
2428 unsigned section_line
,
2436 const char *word
, *state
;
2440 if (isempty(rvalue
)) {
2446 r
= set_ensure_allocated(archs
, NULL
);
2450 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2451 _cleanup_free_
char *t
= NULL
;
2454 t
= strndup(word
, l
);
2458 r
= seccomp_arch_from_string(t
, &a
);
2460 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2461 "Failed to parse system call architecture, ignoring: %s", t
);
2465 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2471 if (!isempty(state
))
2472 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2473 "Trailing garbage, ignoring.");
2478 int config_parse_syscall_errno(
2480 const char *filename
,
2482 const char *section
,
2483 unsigned section_line
,
2490 ExecContext
*c
= data
;
2497 if (isempty(rvalue
)) {
2498 /* Empty assignment resets to KILL */
2499 c
->syscall_errno
= 0;
2503 e
= errno_from_name(rvalue
);
2505 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2506 "Failed to parse error number, ignoring: %s", rvalue
);
2510 c
->syscall_errno
= e
;
2514 int config_parse_address_families(
2516 const char *filename
,
2518 const char *section
,
2519 unsigned section_line
,
2526 ExecContext
*c
= data
;
2527 bool invert
= false;
2528 const char *word
, *state
;
2536 if (isempty(rvalue
)) {
2537 /* Empty assignment resets the list */
2538 set_free(c
->address_families
);
2539 c
->address_families
= NULL
;
2540 c
->address_families_whitelist
= false;
2544 if (rvalue
[0] == '~') {
2549 if (!c
->address_families
) {
2550 c
->address_families
= set_new(NULL
);
2551 if (!c
->address_families
)
2554 c
->address_families_whitelist
= !invert
;
2557 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2558 _cleanup_free_
char *t
= NULL
;
2561 t
= strndup(word
, l
);
2565 af
= af_from_name(t
);
2567 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2568 "Failed to parse address family, ignoring: %s", t
);
2572 /* If we previously wanted to forbid an address family and now
2573 * we want to allow it, then remove it from the list
2575 if (!invert
== c
->address_families_whitelist
) {
2576 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2582 set_remove(c
->address_families
, INT_TO_PTR(af
));
2584 if (!isempty(state
))
2585 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2586 "Trailing garbage, ignoring.");
2592 int config_parse_unit_slice(
2594 const char *filename
,
2596 const char *section
,
2597 unsigned section_line
,
2604 _cleanup_free_
char *k
= NULL
;
2605 Unit
*u
= userdata
, *slice
;
2613 r
= unit_name_printf(u
, rvalue
, &k
);
2615 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2616 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2623 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2625 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2626 "Failed to load slice unit %s. Ignoring.", k
);
2630 if (slice
->type
!= UNIT_SLICE
) {
2631 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2632 "Slice unit %s is not a slice. Ignoring.", k
);
2636 unit_ref_set(&u
->slice
, slice
);
2640 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2642 int config_parse_cpu_shares(
2644 const char *filename
,
2646 const char *section
,
2647 unsigned section_line
,
2654 unsigned long *shares
= data
, lu
;
2661 if (isempty(rvalue
)) {
2662 *shares
= (unsigned long) -1;
2666 r
= safe_atolu(rvalue
, &lu
);
2667 if (r
< 0 || lu
<= 0) {
2668 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2669 "CPU shares '%s' invalid. Ignoring.", rvalue
);
2677 int config_parse_cpu_quota(
2679 const char *filename
,
2681 const char *section
,
2682 unsigned section_line
,
2689 CGroupContext
*c
= data
;
2696 if (isempty(rvalue
)) {
2697 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2701 if (!endswith(rvalue
, "%")) {
2703 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2704 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2708 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2709 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2710 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2714 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2719 int config_parse_memory_limit(
2721 const char *filename
,
2723 const char *section
,
2724 unsigned section_line
,
2731 CGroupContext
*c
= data
;
2735 if (isempty(rvalue
)) {
2736 c
->memory_limit
= (uint64_t) -1;
2740 assert_cc(sizeof(uint64_t) == sizeof(off_t
));
2742 r
= parse_size(rvalue
, 1024, &bytes
);
2744 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2745 "Memory limit '%s' invalid. Ignoring.", rvalue
);
2749 c
->memory_limit
= (uint64_t) bytes
;
2753 int config_parse_device_allow(
2755 const char *filename
,
2757 const char *section
,
2758 unsigned section_line
,
2765 _cleanup_free_
char *path
= NULL
;
2766 CGroupContext
*c
= data
;
2767 CGroupDeviceAllow
*a
;
2771 if (isempty(rvalue
)) {
2772 while (c
->device_allow
)
2773 cgroup_context_free_device_allow(c
, c
->device_allow
);
2778 n
= strcspn(rvalue
, WHITESPACE
);
2779 path
= strndup(rvalue
, n
);
2783 if (!startswith(path
, "/dev/") &&
2784 !startswith(path
, "block-") &&
2785 !startswith(path
, "char-")) {
2786 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2787 "Invalid device node path '%s'. Ignoring.", path
);
2791 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2795 if (!in_charset(m
, "rwm")) {
2796 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2797 "Invalid device rights '%s'. Ignoring.", m
);
2801 a
= new0(CGroupDeviceAllow
, 1);
2807 a
->r
= !!strchr(m
, 'r');
2808 a
->w
= !!strchr(m
, 'w');
2809 a
->m
= !!strchr(m
, 'm');
2811 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2815 int config_parse_blockio_weight(
2817 const char *filename
,
2819 const char *section
,
2820 unsigned section_line
,
2827 unsigned long *weight
= data
, lu
;
2834 if (isempty(rvalue
)) {
2835 *weight
= (unsigned long) -1;
2839 r
= safe_atolu(rvalue
, &lu
);
2840 if (r
< 0 || lu
< 10 || lu
> 1000) {
2841 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2842 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2850 int config_parse_blockio_device_weight(
2852 const char *filename
,
2854 const char *section
,
2855 unsigned section_line
,
2862 _cleanup_free_
char *path
= NULL
;
2863 CGroupBlockIODeviceWeight
*w
;
2864 CGroupContext
*c
= data
;
2874 if (isempty(rvalue
)) {
2875 while (c
->blockio_device_weights
)
2876 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2881 n
= strcspn(rvalue
, WHITESPACE
);
2882 weight
= rvalue
+ n
;
2884 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2885 "Expected block device and device weight. Ignoring.");
2889 path
= strndup(rvalue
, n
);
2893 if (!path_startswith(path
, "/dev")) {
2894 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2895 "Invalid device node path '%s'. Ignoring.", path
);
2899 weight
+= strspn(weight
, WHITESPACE
);
2900 r
= safe_atolu(weight
, &lu
);
2901 if (r
< 0 || lu
< 10 || lu
> 1000) {
2902 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2903 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2907 w
= new0(CGroupBlockIODeviceWeight
, 1);
2916 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2920 int config_parse_blockio_bandwidth(
2922 const char *filename
,
2924 const char *section
,
2925 unsigned section_line
,
2932 _cleanup_free_
char *path
= NULL
;
2933 CGroupBlockIODeviceBandwidth
*b
;
2934 CGroupContext
*c
= data
;
2935 const char *bandwidth
;
2945 read
= streq("BlockIOReadBandwidth", lvalue
);
2947 if (isempty(rvalue
)) {
2948 CGroupBlockIODeviceBandwidth
*next
;
2950 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2951 if (b
->read
== read
)
2952 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2957 n
= strcspn(rvalue
, WHITESPACE
);
2958 bandwidth
= rvalue
+ n
;
2959 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2962 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2963 "Expected space separated pair of device node and bandwidth. Ignoring.");
2967 path
= strndup(rvalue
, n
);
2971 if (!path_startswith(path
, "/dev")) {
2972 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2973 "Invalid device node path '%s'. Ignoring.", path
);
2977 r
= parse_size(bandwidth
, 1000, &bytes
);
2978 if (r
< 0 || bytes
<= 0) {
2979 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2980 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2984 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2990 b
->bandwidth
= (uint64_t) bytes
;
2993 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2998 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3000 int config_parse_job_mode_isolate(
3002 const char *filename
,
3004 const char *section
,
3005 unsigned section_line
,
3019 r
= parse_boolean(rvalue
);
3021 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3022 "Failed to parse boolean, ignoring: %s", rvalue
);
3026 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3030 int config_parse_personality(
3032 const char *filename
,
3034 const char *section
,
3035 unsigned section_line
,
3042 unsigned long *personality
= data
, p
;
3047 assert(personality
);
3049 p
= personality_from_string(rvalue
);
3050 if (p
== PERSONALITY_INVALID
) {
3051 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3052 "Failed to parse personality, ignoring: %s", rvalue
);
3060 int config_parse_runtime_directory(
3062 const char *filename
,
3064 const char *section
,
3065 unsigned section_line
,
3073 const char *word
, *state
;
3082 if (isempty(rvalue
)) {
3083 /* Empty assignment resets the list */
3089 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3090 _cleanup_free_
char *n
;
3092 n
= strndup(word
, l
);
3096 if (!filename_is_valid(n
)) {
3097 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3098 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3102 r
= strv_push(rt
, n
);
3108 if (!isempty(state
))
3109 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3110 "Trailing garbage, ignoring.");
3115 int config_parse_set_status(
3117 const char *filename
,
3119 const char *section
,
3120 unsigned section_line
,
3128 const char *word
, *state
;
3130 ExitStatusSet
*status_set
= data
;
3137 /* Empty assignment resets the list */
3138 if (isempty(rvalue
)) {
3139 exit_status_set_free(status_set
);
3143 FOREACH_WORD(word
, l
, rvalue
, state
) {
3144 _cleanup_free_
char *temp
;
3148 temp
= strndup(word
, l
);
3152 r
= safe_atoi(temp
, &val
);
3154 val
= signal_from_string_try_harder(temp
);
3157 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3158 "Failed to parse value, ignoring: %s", word
);
3161 set
= &status_set
->signal
;
3163 if (val
< 0 || val
> 255) {
3164 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3165 "Value %d is outside range 0-255, ignoring", val
);
3168 set
= &status_set
->status
;
3171 r
= set_ensure_allocated(set
, NULL
);
3175 r
= set_put(*set
, INT_TO_PTR(val
));
3177 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3178 "Unable to store: %s", word
);
3182 if (!isempty(state
))
3183 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3184 "Trailing garbage, ignoring.");
3189 int config_parse_namespace_path_strv(
3191 const char *filename
,
3193 const char *section
,
3194 unsigned section_line
,
3202 const char *word
, *state
;
3211 if (isempty(rvalue
)) {
3212 /* Empty assignment resets the list */
3218 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3219 _cleanup_free_
char *n
;
3222 n
= strndup(word
, l
);
3226 if (!utf8_is_valid(n
)) {
3227 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3231 offset
= n
[0] == '-';
3232 if (!path_is_absolute(n
+ offset
)) {
3233 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3234 "Not an absolute path, ignoring: %s", rvalue
);
3238 path_kill_slashes(n
);
3240 r
= strv_push(sv
, n
);
3246 if (!isempty(state
))
3247 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3248 "Trailing garbage, ignoring.");
3253 int config_parse_no_new_privileges(
3255 const char *filename
,
3257 const char *section
,
3258 unsigned section_line
,
3265 ExecContext
*c
= data
;
3273 k
= parse_boolean(rvalue
);
3275 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3276 "Failed to parse boolean value, ignoring: %s", rvalue
);
3280 c
->no_new_privileges
= !!k
;
3281 c
->no_new_privileges_set
= true;
3286 int config_parse_protect_home(
3288 const char *filename
,
3290 const char *section
,
3291 unsigned section_line
,
3298 ExecContext
*c
= data
;
3306 /* Our enum shall be a superset of booleans, hence first try
3307 * to parse as as boolean, and then as enum */
3309 k
= parse_boolean(rvalue
);
3311 c
->protect_home
= PROTECT_HOME_YES
;
3313 c
->protect_home
= PROTECT_HOME_NO
;
3317 h
= protect_home_from_string(rvalue
);
3319 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3320 "Failed to parse protect home value, ignoring: %s", rvalue
);
3324 c
->protect_home
= h
;
3330 int config_parse_protect_system(
3332 const char *filename
,
3334 const char *section
,
3335 unsigned section_line
,
3342 ExecContext
*c
= data
;
3350 /* Our enum shall be a superset of booleans, hence first try
3351 * to parse as as boolean, and then as enum */
3353 k
= parse_boolean(rvalue
);
3355 c
->protect_system
= PROTECT_SYSTEM_YES
;
3357 c
->protect_system
= PROTECT_SYSTEM_NO
;
3361 s
= protect_system_from_string(rvalue
);
3363 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3364 "Failed to parse protect system value, ignoring: %s", rvalue
);
3368 c
->protect_system
= s
;
3374 #define FOLLOW_MAX 8
3376 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3387 /* This will update the filename pointer if the loaded file is
3388 * reached by a symlink. The old string will be freed. */
3391 char *target
, *name
;
3393 if (c
++ >= FOLLOW_MAX
)
3396 path_kill_slashes(*filename
);
3398 /* Add the file name we are currently looking at to
3399 * the names of this unit, but only if it is a valid
3401 name
= basename(*filename
);
3403 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3405 id
= set_get(names
, name
);
3411 r
= set_consume(names
, id
);
3417 /* Try to open the file name, but don't if its a symlink */
3418 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3425 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3426 r
= readlink_and_make_absolute(*filename
, &target
);
3434 f
= fdopen(fd
, "re");
3445 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3453 /* Let's try to add in all symlink names we found */
3454 while ((k
= set_steal_first(names
))) {
3456 /* First try to merge in the other name into our
3458 r
= unit_merge_by_name(*u
, k
);
3462 /* Hmm, we couldn't merge the other unit into
3463 * ours? Then let's try it the other way
3466 other
= manager_get_unit((*u
)->manager
, k
);
3470 r
= unit_merge(other
, *u
);
3473 return merge_by_names(u
, names
, NULL
);
3481 unit_choose_id(*u
, id
);
3489 static int load_from_path(Unit
*u
, const char *path
) {
3491 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3492 _cleanup_fclose_
FILE *f
= NULL
;
3493 _cleanup_free_
char *filename
= NULL
;
3501 symlink_names
= set_new(&string_hash_ops
);
3505 if (path_is_absolute(path
)) {
3507 filename
= strdup(path
);
3511 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3513 filename
= mfree(filename
);
3521 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3523 /* Instead of opening the path right away, we manually
3524 * follow all symlinks and add their name to our unit
3525 * name set while doing so */
3526 filename
= path_make_absolute(path
, *p
);
3530 if (u
->manager
->unit_path_cache
&&
3531 !set_get(u
->manager
->unit_path_cache
, filename
))
3534 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3537 filename
= mfree(filename
);
3541 /* Empty the symlink names for the next run */
3542 set_clear_free(symlink_names
);
3551 /* Hmm, no suitable file found? */
3555 r
= merge_by_names(&merged
, symlink_names
, id
);
3560 u
->load_state
= UNIT_MERGED
;
3564 if (fstat(fileno(f
), &st
) < 0)
3567 if (null_or_empty(&st
))
3568 u
->load_state
= UNIT_MASKED
;
3570 u
->load_state
= UNIT_LOADED
;
3572 /* Now, parse the file contents */
3573 r
= config_parse(u
->id
, filename
, f
,
3574 UNIT_VTABLE(u
)->sections
,
3575 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3576 false, true, false, u
);
3581 free(u
->fragment_path
);
3582 u
->fragment_path
= filename
;
3585 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3587 if (u
->source_path
) {
3588 if (stat(u
->source_path
, &st
) >= 0)
3589 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3591 u
->source_mtime
= 0;
3597 int unit_load_fragment(Unit
*u
) {
3603 assert(u
->load_state
== UNIT_STUB
);
3607 u
->load_state
= UNIT_LOADED
;
3611 /* First, try to find the unit under its id. We always look
3612 * for unit files in the default directories, to make it easy
3613 * to override things by placing things in /etc/systemd/system */
3614 r
= load_from_path(u
, u
->id
);
3618 /* Try to find an alias we can load this with */
3619 if (u
->load_state
== UNIT_STUB
) {
3620 SET_FOREACH(t
, u
->names
, i
) {
3625 r
= load_from_path(u
, t
);
3629 if (u
->load_state
!= UNIT_STUB
)
3634 /* And now, try looking for it under the suggested (originally linked) path */
3635 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3637 r
= load_from_path(u
, u
->fragment_path
);
3641 if (u
->load_state
== UNIT_STUB
) {
3642 /* Hmm, this didn't work? Then let's get rid
3643 * of the fragment path stored for us, so that
3644 * we don't point to an invalid location. */
3645 free(u
->fragment_path
);
3646 u
->fragment_path
= NULL
;
3650 /* Look for a template */
3651 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3652 _cleanup_free_
char *k
= NULL
;
3654 r
= unit_name_template(u
->id
, &k
);
3658 r
= load_from_path(u
, k
);
3662 if (u
->load_state
== UNIT_STUB
) {
3663 SET_FOREACH(t
, u
->names
, i
) {
3664 _cleanup_free_
char *z
= NULL
;
3669 r
= unit_name_template(t
, &z
);
3673 r
= load_from_path(u
, z
);
3677 if (u
->load_state
!= UNIT_STUB
)
3686 void unit_dump_config_items(FILE *f
) {
3687 static const struct {
3688 const ConfigParserCallback callback
;
3691 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3692 { config_parse_warn_compat
, "NOTSUPPORTED" },
3694 { config_parse_int
, "INTEGER" },
3695 { config_parse_unsigned
, "UNSIGNED" },
3696 { config_parse_iec_size
, "SIZE" },
3697 { config_parse_iec_off
, "SIZE" },
3698 { config_parse_si_size
, "SIZE" },
3699 { config_parse_bool
, "BOOLEAN" },
3700 { config_parse_string
, "STRING" },
3701 { config_parse_path
, "PATH" },
3702 { config_parse_unit_path_printf
, "PATH" },
3703 { config_parse_strv
, "STRING [...]" },
3704 { config_parse_exec_nice
, "NICE" },
3705 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3706 { config_parse_exec_io_class
, "IOCLASS" },
3707 { config_parse_exec_io_priority
, "IOPRIORITY" },
3708 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3709 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3710 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3711 { config_parse_mode
, "MODE" },
3712 { config_parse_unit_env_file
, "FILE" },
3713 { config_parse_output
, "OUTPUT" },
3714 { config_parse_input
, "INPUT" },
3715 { config_parse_log_facility
, "FACILITY" },
3716 { config_parse_log_level
, "LEVEL" },
3717 { config_parse_exec_capabilities
, "CAPABILITIES" },
3718 { config_parse_exec_secure_bits
, "SECUREBITS" },
3719 { config_parse_bounding_set
, "BOUNDINGSET" },
3720 { config_parse_limit
, "LIMIT" },
3721 { config_parse_unit_deps
, "UNIT [...]" },
3722 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3723 { config_parse_service_type
, "SERVICETYPE" },
3724 { config_parse_service_restart
, "SERVICERESTART" },
3725 #ifdef HAVE_SYSV_COMPAT
3726 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3728 { config_parse_kill_mode
, "KILLMODE" },
3729 { config_parse_kill_signal
, "SIGNAL" },
3730 { config_parse_socket_listen
, "SOCKET [...]" },
3731 { config_parse_socket_bind
, "SOCKETBIND" },
3732 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3733 { config_parse_sec
, "SECONDS" },
3734 { config_parse_nsec
, "NANOSECONDS" },
3735 { config_parse_namespace_path_strv
, "PATH [...]" },
3736 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3737 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3738 { config_parse_unit_string_printf
, "STRING" },
3739 { config_parse_trigger_unit
, "UNIT" },
3740 { config_parse_timer
, "TIMER" },
3741 { config_parse_path_spec
, "PATH" },
3742 { config_parse_notify_access
, "ACCESS" },
3743 { config_parse_ip_tos
, "TOS" },
3744 { config_parse_unit_condition_path
, "CONDITION" },
3745 { config_parse_unit_condition_string
, "CONDITION" },
3746 { config_parse_unit_condition_null
, "CONDITION" },
3747 { config_parse_unit_slice
, "SLICE" },
3748 { config_parse_documentation
, "URL" },
3749 { config_parse_service_timeout
, "SECONDS" },
3750 { config_parse_failure_action
, "ACTION" },
3751 { config_parse_set_status
, "STATUS" },
3752 { config_parse_service_sockets
, "SOCKETS" },
3753 { config_parse_environ
, "ENVIRON" },
3755 { config_parse_syscall_filter
, "SYSCALLS" },
3756 { config_parse_syscall_archs
, "ARCHS" },
3757 { config_parse_syscall_errno
, "ERRNO" },
3758 { config_parse_address_families
, "FAMILIES" },
3760 { config_parse_cpu_shares
, "SHARES" },
3761 { config_parse_memory_limit
, "LIMIT" },
3762 { config_parse_device_allow
, "DEVICE" },
3763 { config_parse_device_policy
, "POLICY" },
3764 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3765 { config_parse_blockio_weight
, "WEIGHT" },
3766 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3767 { config_parse_long
, "LONG" },
3768 { config_parse_socket_service
, "SERVICE" },
3770 { config_parse_exec_selinux_context
, "LABEL" },
3772 { config_parse_job_mode
, "MODE" },
3773 { config_parse_job_mode_isolate
, "BOOLEAN" },
3774 { config_parse_personality
, "PERSONALITY" },
3777 const char *prev
= NULL
;
3782 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3783 const char *rvalue
= "OTHER", *lvalue
;
3787 const ConfigPerfItem
*p
;
3789 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3791 dot
= strchr(i
, '.');
3792 lvalue
= dot
? dot
+ 1 : i
;
3796 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3800 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3803 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3804 if (p
->parse
== table
[j
].callback
) {
3805 rvalue
= table
[j
].rvalue
;
3809 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);