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
= NULL
;
2613 r
= unit_name_printf(u
, rvalue
, &k
);
2615 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2619 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2621 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2625 r
= unit_set_slice(u
, slice
);
2627 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2634 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2636 int config_parse_cpu_shares(
2638 const char *filename
,
2640 const char *section
,
2641 unsigned section_line
,
2648 unsigned long *shares
= data
, lu
;
2655 if (isempty(rvalue
)) {
2656 *shares
= (unsigned long) -1;
2660 r
= safe_atolu(rvalue
, &lu
);
2661 if (r
< 0 || lu
<= 0) {
2662 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2663 "CPU shares '%s' invalid. Ignoring.", rvalue
);
2671 int config_parse_cpu_quota(
2673 const char *filename
,
2675 const char *section
,
2676 unsigned section_line
,
2683 CGroupContext
*c
= data
;
2690 if (isempty(rvalue
)) {
2691 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2695 if (!endswith(rvalue
, "%")) {
2697 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2698 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2702 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2703 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2704 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2708 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2713 int config_parse_memory_limit(
2715 const char *filename
,
2717 const char *section
,
2718 unsigned section_line
,
2725 CGroupContext
*c
= data
;
2729 if (isempty(rvalue
)) {
2730 c
->memory_limit
= (uint64_t) -1;
2734 assert_cc(sizeof(uint64_t) == sizeof(off_t
));
2736 r
= parse_size(rvalue
, 1024, &bytes
);
2738 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2739 "Memory limit '%s' invalid. Ignoring.", rvalue
);
2743 c
->memory_limit
= (uint64_t) bytes
;
2747 int config_parse_device_allow(
2749 const char *filename
,
2751 const char *section
,
2752 unsigned section_line
,
2759 _cleanup_free_
char *path
= NULL
;
2760 CGroupContext
*c
= data
;
2761 CGroupDeviceAllow
*a
;
2765 if (isempty(rvalue
)) {
2766 while (c
->device_allow
)
2767 cgroup_context_free_device_allow(c
, c
->device_allow
);
2772 n
= strcspn(rvalue
, WHITESPACE
);
2773 path
= strndup(rvalue
, n
);
2777 if (!startswith(path
, "/dev/") &&
2778 !startswith(path
, "block-") &&
2779 !startswith(path
, "char-")) {
2780 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2781 "Invalid device node path '%s'. Ignoring.", path
);
2785 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2789 if (!in_charset(m
, "rwm")) {
2790 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2791 "Invalid device rights '%s'. Ignoring.", m
);
2795 a
= new0(CGroupDeviceAllow
, 1);
2801 a
->r
= !!strchr(m
, 'r');
2802 a
->w
= !!strchr(m
, 'w');
2803 a
->m
= !!strchr(m
, 'm');
2805 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2809 int config_parse_blockio_weight(
2811 const char *filename
,
2813 const char *section
,
2814 unsigned section_line
,
2821 unsigned long *weight
= data
, lu
;
2828 if (isempty(rvalue
)) {
2829 *weight
= (unsigned long) -1;
2833 r
= safe_atolu(rvalue
, &lu
);
2834 if (r
< 0 || lu
< 10 || lu
> 1000) {
2835 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2836 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2844 int config_parse_blockio_device_weight(
2846 const char *filename
,
2848 const char *section
,
2849 unsigned section_line
,
2856 _cleanup_free_
char *path
= NULL
;
2857 CGroupBlockIODeviceWeight
*w
;
2858 CGroupContext
*c
= data
;
2868 if (isempty(rvalue
)) {
2869 while (c
->blockio_device_weights
)
2870 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2875 n
= strcspn(rvalue
, WHITESPACE
);
2876 weight
= rvalue
+ n
;
2878 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2879 "Expected block device and device weight. Ignoring.");
2883 path
= strndup(rvalue
, n
);
2887 if (!path_startswith(path
, "/dev")) {
2888 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2889 "Invalid device node path '%s'. Ignoring.", path
);
2893 weight
+= strspn(weight
, WHITESPACE
);
2894 r
= safe_atolu(weight
, &lu
);
2895 if (r
< 0 || lu
< 10 || lu
> 1000) {
2896 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2897 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2901 w
= new0(CGroupBlockIODeviceWeight
, 1);
2910 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2914 int config_parse_blockio_bandwidth(
2916 const char *filename
,
2918 const char *section
,
2919 unsigned section_line
,
2926 _cleanup_free_
char *path
= NULL
;
2927 CGroupBlockIODeviceBandwidth
*b
;
2928 CGroupContext
*c
= data
;
2929 const char *bandwidth
;
2939 read
= streq("BlockIOReadBandwidth", lvalue
);
2941 if (isempty(rvalue
)) {
2942 CGroupBlockIODeviceBandwidth
*next
;
2944 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2945 if (b
->read
== read
)
2946 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2951 n
= strcspn(rvalue
, WHITESPACE
);
2952 bandwidth
= rvalue
+ n
;
2953 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2956 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2957 "Expected space separated pair of device node and bandwidth. Ignoring.");
2961 path
= strndup(rvalue
, n
);
2965 if (!path_startswith(path
, "/dev")) {
2966 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2967 "Invalid device node path '%s'. Ignoring.", path
);
2971 r
= parse_size(bandwidth
, 1000, &bytes
);
2972 if (r
< 0 || bytes
<= 0) {
2973 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2974 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2978 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2984 b
->bandwidth
= (uint64_t) bytes
;
2987 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2992 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
2994 int config_parse_job_mode_isolate(
2996 const char *filename
,
2998 const char *section
,
2999 unsigned section_line
,
3013 r
= parse_boolean(rvalue
);
3015 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3016 "Failed to parse boolean, ignoring: %s", rvalue
);
3020 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3024 int config_parse_personality(
3026 const char *filename
,
3028 const char *section
,
3029 unsigned section_line
,
3036 unsigned long *personality
= data
, p
;
3041 assert(personality
);
3043 p
= personality_from_string(rvalue
);
3044 if (p
== PERSONALITY_INVALID
) {
3045 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3046 "Failed to parse personality, ignoring: %s", rvalue
);
3054 int config_parse_runtime_directory(
3056 const char *filename
,
3058 const char *section
,
3059 unsigned section_line
,
3067 const char *word
, *state
;
3076 if (isempty(rvalue
)) {
3077 /* Empty assignment resets the list */
3083 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3084 _cleanup_free_
char *n
;
3086 n
= strndup(word
, l
);
3090 if (!filename_is_valid(n
)) {
3091 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3092 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3096 r
= strv_push(rt
, n
);
3102 if (!isempty(state
))
3103 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3104 "Trailing garbage, ignoring.");
3109 int config_parse_set_status(
3111 const char *filename
,
3113 const char *section
,
3114 unsigned section_line
,
3122 const char *word
, *state
;
3124 ExitStatusSet
*status_set
= data
;
3131 /* Empty assignment resets the list */
3132 if (isempty(rvalue
)) {
3133 exit_status_set_free(status_set
);
3137 FOREACH_WORD(word
, l
, rvalue
, state
) {
3138 _cleanup_free_
char *temp
;
3142 temp
= strndup(word
, l
);
3146 r
= safe_atoi(temp
, &val
);
3148 val
= signal_from_string_try_harder(temp
);
3151 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3152 "Failed to parse value, ignoring: %s", word
);
3155 set
= &status_set
->signal
;
3157 if (val
< 0 || val
> 255) {
3158 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3159 "Value %d is outside range 0-255, ignoring", val
);
3162 set
= &status_set
->status
;
3165 r
= set_ensure_allocated(set
, NULL
);
3169 r
= set_put(*set
, INT_TO_PTR(val
));
3171 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3172 "Unable to store: %s", word
);
3176 if (!isempty(state
))
3177 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3178 "Trailing garbage, ignoring.");
3183 int config_parse_namespace_path_strv(
3185 const char *filename
,
3187 const char *section
,
3188 unsigned section_line
,
3196 const char *word
, *state
;
3205 if (isempty(rvalue
)) {
3206 /* Empty assignment resets the list */
3212 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3213 _cleanup_free_
char *n
;
3216 n
= strndup(word
, l
);
3220 if (!utf8_is_valid(n
)) {
3221 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3225 offset
= n
[0] == '-';
3226 if (!path_is_absolute(n
+ offset
)) {
3227 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3228 "Not an absolute path, ignoring: %s", rvalue
);
3232 path_kill_slashes(n
);
3234 r
= strv_push(sv
, n
);
3240 if (!isempty(state
))
3241 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3242 "Trailing garbage, ignoring.");
3247 int config_parse_no_new_privileges(
3249 const char *filename
,
3251 const char *section
,
3252 unsigned section_line
,
3259 ExecContext
*c
= data
;
3267 k
= parse_boolean(rvalue
);
3269 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3270 "Failed to parse boolean value, ignoring: %s", rvalue
);
3274 c
->no_new_privileges
= !!k
;
3275 c
->no_new_privileges_set
= true;
3280 int config_parse_protect_home(
3282 const char *filename
,
3284 const char *section
,
3285 unsigned section_line
,
3292 ExecContext
*c
= data
;
3300 /* Our enum shall be a superset of booleans, hence first try
3301 * to parse as as boolean, and then as enum */
3303 k
= parse_boolean(rvalue
);
3305 c
->protect_home
= PROTECT_HOME_YES
;
3307 c
->protect_home
= PROTECT_HOME_NO
;
3311 h
= protect_home_from_string(rvalue
);
3313 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3314 "Failed to parse protect home value, ignoring: %s", rvalue
);
3318 c
->protect_home
= h
;
3324 int config_parse_protect_system(
3326 const char *filename
,
3328 const char *section
,
3329 unsigned section_line
,
3336 ExecContext
*c
= data
;
3344 /* Our enum shall be a superset of booleans, hence first try
3345 * to parse as as boolean, and then as enum */
3347 k
= parse_boolean(rvalue
);
3349 c
->protect_system
= PROTECT_SYSTEM_YES
;
3351 c
->protect_system
= PROTECT_SYSTEM_NO
;
3355 s
= protect_system_from_string(rvalue
);
3357 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3358 "Failed to parse protect system value, ignoring: %s", rvalue
);
3362 c
->protect_system
= s
;
3368 #define FOLLOW_MAX 8
3370 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3381 /* This will update the filename pointer if the loaded file is
3382 * reached by a symlink. The old string will be freed. */
3385 char *target
, *name
;
3387 if (c
++ >= FOLLOW_MAX
)
3390 path_kill_slashes(*filename
);
3392 /* Add the file name we are currently looking at to
3393 * the names of this unit, but only if it is a valid
3395 name
= basename(*filename
);
3397 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3399 id
= set_get(names
, name
);
3405 r
= set_consume(names
, id
);
3411 /* Try to open the file name, but don't if its a symlink */
3412 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3419 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3420 r
= readlink_and_make_absolute(*filename
, &target
);
3428 f
= fdopen(fd
, "re");
3439 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3447 /* Let's try to add in all symlink names we found */
3448 while ((k
= set_steal_first(names
))) {
3450 /* First try to merge in the other name into our
3452 r
= unit_merge_by_name(*u
, k
);
3456 /* Hmm, we couldn't merge the other unit into
3457 * ours? Then let's try it the other way
3460 other
= manager_get_unit((*u
)->manager
, k
);
3464 r
= unit_merge(other
, *u
);
3467 return merge_by_names(u
, names
, NULL
);
3475 unit_choose_id(*u
, id
);
3483 static int load_from_path(Unit
*u
, const char *path
) {
3485 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3486 _cleanup_fclose_
FILE *f
= NULL
;
3487 _cleanup_free_
char *filename
= NULL
;
3495 symlink_names
= set_new(&string_hash_ops
);
3499 if (path_is_absolute(path
)) {
3501 filename
= strdup(path
);
3505 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3507 filename
= mfree(filename
);
3515 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3517 /* Instead of opening the path right away, we manually
3518 * follow all symlinks and add their name to our unit
3519 * name set while doing so */
3520 filename
= path_make_absolute(path
, *p
);
3524 if (u
->manager
->unit_path_cache
&&
3525 !set_get(u
->manager
->unit_path_cache
, filename
))
3528 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3531 filename
= mfree(filename
);
3535 /* Empty the symlink names for the next run */
3536 set_clear_free(symlink_names
);
3545 /* Hmm, no suitable file found? */
3549 r
= merge_by_names(&merged
, symlink_names
, id
);
3554 u
->load_state
= UNIT_MERGED
;
3558 if (fstat(fileno(f
), &st
) < 0)
3561 if (null_or_empty(&st
))
3562 u
->load_state
= UNIT_MASKED
;
3564 u
->load_state
= UNIT_LOADED
;
3566 /* Now, parse the file contents */
3567 r
= config_parse(u
->id
, filename
, f
,
3568 UNIT_VTABLE(u
)->sections
,
3569 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3570 false, true, false, u
);
3575 free(u
->fragment_path
);
3576 u
->fragment_path
= filename
;
3579 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3581 if (u
->source_path
) {
3582 if (stat(u
->source_path
, &st
) >= 0)
3583 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3585 u
->source_mtime
= 0;
3591 int unit_load_fragment(Unit
*u
) {
3597 assert(u
->load_state
== UNIT_STUB
);
3601 u
->load_state
= UNIT_LOADED
;
3605 /* First, try to find the unit under its id. We always look
3606 * for unit files in the default directories, to make it easy
3607 * to override things by placing things in /etc/systemd/system */
3608 r
= load_from_path(u
, u
->id
);
3612 /* Try to find an alias we can load this with */
3613 if (u
->load_state
== UNIT_STUB
) {
3614 SET_FOREACH(t
, u
->names
, i
) {
3619 r
= load_from_path(u
, t
);
3623 if (u
->load_state
!= UNIT_STUB
)
3628 /* And now, try looking for it under the suggested (originally linked) path */
3629 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3631 r
= load_from_path(u
, u
->fragment_path
);
3635 if (u
->load_state
== UNIT_STUB
) {
3636 /* Hmm, this didn't work? Then let's get rid
3637 * of the fragment path stored for us, so that
3638 * we don't point to an invalid location. */
3639 free(u
->fragment_path
);
3640 u
->fragment_path
= NULL
;
3644 /* Look for a template */
3645 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3646 _cleanup_free_
char *k
= NULL
;
3648 r
= unit_name_template(u
->id
, &k
);
3652 r
= load_from_path(u
, k
);
3656 if (u
->load_state
== UNIT_STUB
) {
3657 SET_FOREACH(t
, u
->names
, i
) {
3658 _cleanup_free_
char *z
= NULL
;
3663 r
= unit_name_template(t
, &z
);
3667 r
= load_from_path(u
, z
);
3671 if (u
->load_state
!= UNIT_STUB
)
3680 void unit_dump_config_items(FILE *f
) {
3681 static const struct {
3682 const ConfigParserCallback callback
;
3685 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3686 { config_parse_warn_compat
, "NOTSUPPORTED" },
3688 { config_parse_int
, "INTEGER" },
3689 { config_parse_unsigned
, "UNSIGNED" },
3690 { config_parse_iec_size
, "SIZE" },
3691 { config_parse_iec_off
, "SIZE" },
3692 { config_parse_si_size
, "SIZE" },
3693 { config_parse_bool
, "BOOLEAN" },
3694 { config_parse_string
, "STRING" },
3695 { config_parse_path
, "PATH" },
3696 { config_parse_unit_path_printf
, "PATH" },
3697 { config_parse_strv
, "STRING [...]" },
3698 { config_parse_exec_nice
, "NICE" },
3699 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3700 { config_parse_exec_io_class
, "IOCLASS" },
3701 { config_parse_exec_io_priority
, "IOPRIORITY" },
3702 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3703 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3704 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3705 { config_parse_mode
, "MODE" },
3706 { config_parse_unit_env_file
, "FILE" },
3707 { config_parse_output
, "OUTPUT" },
3708 { config_parse_input
, "INPUT" },
3709 { config_parse_log_facility
, "FACILITY" },
3710 { config_parse_log_level
, "LEVEL" },
3711 { config_parse_exec_capabilities
, "CAPABILITIES" },
3712 { config_parse_exec_secure_bits
, "SECUREBITS" },
3713 { config_parse_bounding_set
, "BOUNDINGSET" },
3714 { config_parse_limit
, "LIMIT" },
3715 { config_parse_unit_deps
, "UNIT [...]" },
3716 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3717 { config_parse_service_type
, "SERVICETYPE" },
3718 { config_parse_service_restart
, "SERVICERESTART" },
3719 #ifdef HAVE_SYSV_COMPAT
3720 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3722 { config_parse_kill_mode
, "KILLMODE" },
3723 { config_parse_kill_signal
, "SIGNAL" },
3724 { config_parse_socket_listen
, "SOCKET [...]" },
3725 { config_parse_socket_bind
, "SOCKETBIND" },
3726 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3727 { config_parse_sec
, "SECONDS" },
3728 { config_parse_nsec
, "NANOSECONDS" },
3729 { config_parse_namespace_path_strv
, "PATH [...]" },
3730 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3731 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3732 { config_parse_unit_string_printf
, "STRING" },
3733 { config_parse_trigger_unit
, "UNIT" },
3734 { config_parse_timer
, "TIMER" },
3735 { config_parse_path_spec
, "PATH" },
3736 { config_parse_notify_access
, "ACCESS" },
3737 { config_parse_ip_tos
, "TOS" },
3738 { config_parse_unit_condition_path
, "CONDITION" },
3739 { config_parse_unit_condition_string
, "CONDITION" },
3740 { config_parse_unit_condition_null
, "CONDITION" },
3741 { config_parse_unit_slice
, "SLICE" },
3742 { config_parse_documentation
, "URL" },
3743 { config_parse_service_timeout
, "SECONDS" },
3744 { config_parse_failure_action
, "ACTION" },
3745 { config_parse_set_status
, "STATUS" },
3746 { config_parse_service_sockets
, "SOCKETS" },
3747 { config_parse_environ
, "ENVIRON" },
3749 { config_parse_syscall_filter
, "SYSCALLS" },
3750 { config_parse_syscall_archs
, "ARCHS" },
3751 { config_parse_syscall_errno
, "ERRNO" },
3752 { config_parse_address_families
, "FAMILIES" },
3754 { config_parse_cpu_shares
, "SHARES" },
3755 { config_parse_memory_limit
, "LIMIT" },
3756 { config_parse_device_allow
, "DEVICE" },
3757 { config_parse_device_policy
, "POLICY" },
3758 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3759 { config_parse_blockio_weight
, "WEIGHT" },
3760 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3761 { config_parse_long
, "LONG" },
3762 { config_parse_socket_service
, "SERVICE" },
3764 { config_parse_exec_selinux_context
, "LABEL" },
3766 { config_parse_job_mode
, "MODE" },
3767 { config_parse_job_mode_isolate
, "BOOLEAN" },
3768 { config_parse_personality
, "PERSONALITY" },
3771 const char *prev
= NULL
;
3776 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3777 const char *rvalue
= "OTHER", *lvalue
;
3781 const ConfigPerfItem
*p
;
3783 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3785 dot
= strchr(i
, '.');
3786 lvalue
= dot
? dot
+ 1 : i
;
3790 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3794 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3797 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3798 if (p
->parse
== table
[j
].callback
) {
3799 rvalue
= table
[j
].rvalue
;
3803 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);