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/>.
26 #include <linux/oom.h>
32 #include <sys/resource.h>
36 #include "bus-error.h"
37 #include "bus-internal.h"
41 #include "conf-parser.h"
42 #include "cpu-set-util.h"
44 #include "errno-list.h"
48 #include "load-fragment.h"
51 #include "path-util.h"
53 #include "seccomp-util.h"
55 #include "securebits.h"
56 #include "signal-util.h"
57 #include "string-util.h"
59 #include "unit-name.h"
60 #include "unit-printf.h"
64 int config_parse_warn_compat(
69 unsigned section_line
,
75 Disabled reason
= ltype
;
78 case DISABLED_CONFIGURATION
:
79 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
80 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
83 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
84 "Support for option %s= has been removed and it is ignored", lvalue
);
86 case DISABLED_EXPERIMENTAL
:
87 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
88 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
95 int config_parse_unit_deps(const char *unit
,
99 unsigned section_line
,
106 UnitDependency d
= ltype
;
108 const char *word
, *state
;
115 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
116 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
119 t
= strndup(word
, l
);
123 r
= unit_name_printf(u
, t
, &k
);
125 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
129 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
131 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
134 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "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(
171 const char *filename
,
174 unsigned section_line
,
182 _cleanup_free_
char *k
= NULL
;
190 r
= unit_full_printf(u
, rvalue
, &k
);
192 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
196 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
199 int config_parse_unit_path_printf(
201 const char *filename
,
204 unsigned section_line
,
211 _cleanup_free_
char *k
= NULL
;
220 r
= unit_full_printf(u
, rvalue
, &k
);
222 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
226 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
229 int config_parse_unit_path_strv_printf(
231 const char *filename
,
234 unsigned section_line
,
242 const char *word
, *state
;
252 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
253 _cleanup_free_
char *k
= NULL
;
259 r
= unit_full_printf(u
, t
, &k
);
261 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", t
);
265 if (!utf8_is_valid(k
)) {
266 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
270 if (!path_is_absolute(k
)) {
271 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Symlink path %s is not absolute, ignoring: %m", k
);
275 path_kill_slashes(k
);
284 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring.");
289 int config_parse_socket_listen(const char *unit
,
290 const char *filename
,
293 unsigned section_line
,
300 _cleanup_free_ SocketPort
*p
= NULL
;
312 if (isempty(rvalue
)) {
313 /* An empty assignment removes all ports */
314 socket_free_ports(s
);
318 p
= new0(SocketPort
, 1);
322 if (ltype
!= SOCKET_SOCKET
) {
325 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
327 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
331 path_kill_slashes(p
->path
);
333 } else if (streq(lvalue
, "ListenNetlink")) {
334 _cleanup_free_
char *k
= NULL
;
336 p
->type
= SOCKET_SOCKET
;
337 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
339 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
343 r
= socket_address_parse_netlink(&p
->address
, k
);
345 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
350 _cleanup_free_
char *k
= NULL
;
352 p
->type
= SOCKET_SOCKET
;
353 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
355 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
359 r
= socket_address_parse_and_warn(&p
->address
, k
);
361 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
365 if (streq(lvalue
, "ListenStream"))
366 p
->address
.type
= SOCK_STREAM
;
367 else if (streq(lvalue
, "ListenDatagram"))
368 p
->address
.type
= SOCK_DGRAM
;
370 assert(streq(lvalue
, "ListenSequentialPacket"));
371 p
->address
.type
= SOCK_SEQPACKET
;
374 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
375 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
381 p
->auxiliary_fds
= NULL
;
382 p
->n_auxiliary_fds
= 0;
386 LIST_FIND_TAIL(port
, s
->ports
, tail
);
387 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
389 LIST_PREPEND(port
, s
->ports
, p
);
395 int config_parse_socket_bind(const char *unit
,
396 const char *filename
,
399 unsigned section_line
,
407 SocketAddressBindIPv6Only b
;
416 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
420 r
= parse_boolean(rvalue
);
422 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
426 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
428 s
->bind_ipv6_only
= b
;
433 int config_parse_exec_nice(const char *unit
,
434 const char *filename
,
437 unsigned section_line
,
444 ExecContext
*c
= data
;
452 r
= safe_atoi(rvalue
, &priority
);
454 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
458 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
459 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Nice priority out of range, ignoring: %s", rvalue
);
469 int config_parse_exec_oom_score_adjust(const char* unit
,
470 const char *filename
,
473 unsigned section_line
,
480 ExecContext
*c
= data
;
488 r
= safe_atoi(rvalue
, &oa
);
490 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
494 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
495 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
499 c
->oom_score_adjust
= oa
;
500 c
->oom_score_adjust_set
= true;
505 int config_parse_exec(
507 const char *filename
,
510 unsigned section_line
,
517 ExecCommand
**e
= data
;
528 rvalue
+= strspn(rvalue
, WHITESPACE
);
530 if (isempty(rvalue
)) {
531 /* An empty assignment resets the list */
532 *e
= exec_command_free_list(*e
);
538 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
539 bool separate_argv0
= false, ignore
= false;
540 _cleanup_free_ ExecCommand
*nce
= NULL
;
541 _cleanup_strv_free_
char **n
= NULL
;
542 size_t nlen
= 0, nbufsize
= 0;
548 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
553 for (i
= 0; i
< 2; i
++) {
554 /* We accept an absolute path as first argument, or
555 * alternatively an absolute prefixed with @ to allow
556 * overriding of argv[0]. */
557 if (*f
== '-' && !ignore
)
559 else if (*f
== '@' && !separate_argv0
)
560 separate_argv0
= true;
567 /* First word is either "-" or "@" with no command. */
568 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty path in command line, ignoring: \"%s\"", rvalue
);
571 if (!string_is_safe(f
)) {
572 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path contains special characters, ignoring: %s", rvalue
);
575 if (!path_is_absolute(f
)) {
576 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path is not absolute, ignoring: %s", rvalue
);
579 if (endswith(f
, "/")) {
580 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path specifies a directory, ignoring: %s", rvalue
);
584 if (f
== firstword
) {
593 if (!separate_argv0
) {
594 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
603 path_kill_slashes(path
);
605 while (!isempty(p
)) {
606 _cleanup_free_
char *word
= NULL
;
608 /* Check explicitly for an unquoted semicolon as
609 * command separator token. */
610 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
612 p
+= strspn(p
, WHITESPACE
);
617 /* Check for \; explicitly, to not confuse it with \\;
618 * or "\;" or "\\;" etc. extract_first_word would
619 * return the same for all of those. */
620 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
622 p
+= strspn(p
, WHITESPACE
);
623 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
633 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
639 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
647 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
651 nce
= new0(ExecCommand
, 1);
657 nce
->ignore
= ignore
;
659 exec_command_append_list(e
, nce
);
661 /* Do not _cleanup_free_ these. */
672 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
673 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
675 int config_parse_socket_bindtodevice(const char* unit
,
676 const char *filename
,
679 unsigned section_line
,
694 if (rvalue
[0] && !streq(rvalue
, "*")) {
701 free(s
->bind_to_device
);
702 s
->bind_to_device
= n
;
707 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
708 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
710 int config_parse_exec_io_class(const char *unit
,
711 const char *filename
,
714 unsigned section_line
,
721 ExecContext
*c
= data
;
729 x
= ioprio_class_from_string(rvalue
);
731 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
735 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
736 c
->ioprio_set
= true;
741 int config_parse_exec_io_priority(const char *unit
,
742 const char *filename
,
745 unsigned section_line
,
752 ExecContext
*c
= data
;
760 r
= safe_atoi(rvalue
, &i
);
761 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
762 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
766 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
767 c
->ioprio_set
= true;
772 int config_parse_exec_cpu_sched_policy(const char *unit
,
773 const char *filename
,
776 unsigned section_line
,
784 ExecContext
*c
= data
;
792 x
= sched_policy_from_string(rvalue
);
794 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
798 c
->cpu_sched_policy
= x
;
799 /* Moving to or from real-time policy? We need to adjust the priority */
800 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
801 c
->cpu_sched_set
= true;
806 int config_parse_exec_cpu_sched_prio(const char *unit
,
807 const char *filename
,
810 unsigned section_line
,
817 ExecContext
*c
= data
;
825 r
= safe_atoi(rvalue
, &i
);
827 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
831 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
832 min
= sched_get_priority_min(c
->cpu_sched_policy
);
833 max
= sched_get_priority_max(c
->cpu_sched_policy
);
835 if (i
< min
|| i
> max
) {
836 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
840 c
->cpu_sched_priority
= i
;
841 c
->cpu_sched_set
= true;
846 int config_parse_exec_cpu_affinity(const char *unit
,
847 const char *filename
,
850 unsigned section_line
,
857 ExecContext
*c
= data
;
858 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
866 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
874 /* An empty assignment resets the CPU list */
880 c
->cpuset_ncpus
= ncpus
;
885 int config_parse_exec_capabilities(const char *unit
,
886 const char *filename
,
889 unsigned section_line
,
896 ExecContext
*c
= data
;
904 cap
= cap_from_text(rvalue
);
906 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
, "Failed to parse capabilities, ignoring: %s", rvalue
);
911 cap_free(c
->capabilities
);
912 c
->capabilities
= cap
;
917 int config_parse_exec_secure_bits(const char *unit
,
918 const char *filename
,
921 unsigned section_line
,
928 ExecContext
*c
= data
;
930 const char *word
, *state
;
937 if (isempty(rvalue
)) {
938 /* An empty assignment resets the field */
943 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
944 if (first_word(word
, "keep-caps"))
945 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
946 else if (first_word(word
, "keep-caps-locked"))
947 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
948 else if (first_word(word
, "no-setuid-fixup"))
949 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
950 else if (first_word(word
, "no-setuid-fixup-locked"))
951 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
952 else if (first_word(word
, "noroot"))
953 c
->secure_bits
|= 1<<SECURE_NOROOT
;
954 else if (first_word(word
, "noroot-locked"))
955 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
957 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse secure bits, ignoring: %s", rvalue
);
962 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, garbage at the end, ignoring.");
967 int config_parse_bounding_set(const char *unit
,
968 const char *filename
,
971 unsigned section_line
,
978 uint64_t *capability_bounding_set_drop
= data
;
979 const char *word
, *state
;
989 if (rvalue
[0] == '~') {
994 /* Note that we store this inverted internally, since the
995 * kernel wants it like this. But we actually expose it
996 * non-inverted everywhere to have a fully normalized
999 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1000 _cleanup_free_
char *t
= NULL
;
1003 t
= strndup(word
, l
);
1007 cap
= capability_from_name(t
);
1009 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability in bounding set, ignoring: %s", t
);
1013 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1015 if (!isempty(state
))
1016 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1019 *capability_bounding_set_drop
|= sum
;
1021 *capability_bounding_set_drop
|= ~sum
;
1026 int config_parse_limit(const char *unit
,
1027 const char *filename
,
1029 const char *section
,
1030 unsigned section_line
,
1037 struct rlimit
**rl
= data
;
1038 unsigned long long u
;
1047 if (streq(rvalue
, "infinity"))
1048 u
= (unsigned long long) RLIM_INFINITY
;
1052 r
= safe_atollu(rvalue
, &u
);
1054 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1060 *rl
= new(struct rlimit
, 1);
1065 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1069 #ifdef HAVE_SYSV_COMPAT
1070 int config_parse_sysv_priority(const char *unit
,
1071 const char *filename
,
1073 const char *section
,
1074 unsigned section_line
,
1081 int *priority
= data
;
1089 r
= safe_atoi(rvalue
, &i
);
1090 if (r
< 0 || i
< 0) {
1091 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1095 *priority
= (int) i
;
1100 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1101 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1103 int config_parse_exec_mount_flags(const char *unit
,
1104 const char *filename
,
1106 const char *section
,
1107 unsigned section_line
,
1114 ExecContext
*c
= data
;
1115 const char *word
, *state
;
1117 unsigned long flags
= 0;
1124 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1125 _cleanup_free_
char *t
;
1127 t
= strndup(word
, l
);
1131 if (streq(t
, "shared"))
1133 else if (streq(t
, "slave"))
1135 else if (streq(t
, "private"))
1138 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1142 if (!isempty(state
))
1143 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1145 c
->mount_flags
= flags
;
1149 int config_parse_exec_selinux_context(
1151 const char *filename
,
1153 const char *section
,
1154 unsigned section_line
,
1161 ExecContext
*c
= data
;
1172 if (isempty(rvalue
)) {
1173 c
->selinux_context
= mfree(c
->selinux_context
);
1174 c
->selinux_context_ignore
= false;
1178 if (rvalue
[0] == '-') {
1184 r
= unit_name_printf(u
, rvalue
, &k
);
1186 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1190 free(c
->selinux_context
);
1191 c
->selinux_context
= k
;
1192 c
->selinux_context_ignore
= ignore
;
1197 int config_parse_exec_apparmor_profile(
1199 const char *filename
,
1201 const char *section
,
1202 unsigned section_line
,
1209 ExecContext
*c
= data
;
1220 if (isempty(rvalue
)) {
1221 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1222 c
->apparmor_profile_ignore
= false;
1226 if (rvalue
[0] == '-') {
1232 r
= unit_name_printf(u
, rvalue
, &k
);
1234 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1238 free(c
->apparmor_profile
);
1239 c
->apparmor_profile
= k
;
1240 c
->apparmor_profile_ignore
= ignore
;
1245 int config_parse_exec_smack_process_label(
1247 const char *filename
,
1249 const char *section
,
1250 unsigned section_line
,
1257 ExecContext
*c
= data
;
1268 if (isempty(rvalue
)) {
1269 c
->smack_process_label
= mfree(c
->smack_process_label
);
1270 c
->smack_process_label_ignore
= false;
1274 if (rvalue
[0] == '-') {
1280 r
= unit_name_printf(u
, rvalue
, &k
);
1282 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1286 free(c
->smack_process_label
);
1287 c
->smack_process_label
= k
;
1288 c
->smack_process_label_ignore
= ignore
;
1293 int config_parse_timer(const char *unit
,
1294 const char *filename
,
1296 const char *section
,
1297 unsigned section_line
,
1308 CalendarSpec
*c
= NULL
;
1315 if (isempty(rvalue
)) {
1316 /* Empty assignment resets list */
1317 timer_free_values(t
);
1321 b
= timer_base_from_string(lvalue
);
1323 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1327 if (b
== TIMER_CALENDAR
) {
1328 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1329 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", rvalue
);
1333 if (parse_sec(rvalue
, &u
) < 0) {
1334 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", rvalue
);
1339 v
= new0(TimerValue
, 1);
1341 calendar_spec_free(c
);
1347 v
->calendar_spec
= c
;
1349 LIST_PREPEND(value
, t
->values
, v
);
1354 int config_parse_trigger_unit(
1356 const char *filename
,
1358 const char *section
,
1359 unsigned section_line
,
1366 _cleanup_free_
char *p
= NULL
;
1376 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1377 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1381 r
= unit_name_printf(u
, rvalue
, &p
);
1383 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1387 type
= unit_name_to_type(p
);
1389 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1393 if (type
== u
->type
) {
1394 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1398 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1400 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1407 int config_parse_path_spec(const char *unit
,
1408 const char *filename
,
1410 const char *section
,
1411 unsigned section_line
,
1421 _cleanup_free_
char *k
= NULL
;
1429 if (isempty(rvalue
)) {
1430 /* Empty assignment clears list */
1435 b
= path_type_from_string(lvalue
);
1437 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1441 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1443 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1447 if (!path_is_absolute(k
)) {
1448 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1452 s
= new0(PathSpec
, 1);
1457 s
->path
= path_kill_slashes(k
);
1462 LIST_PREPEND(spec
, p
->specs
, s
);
1467 int config_parse_socket_service(
1469 const char *filename
,
1471 const char *section
,
1472 unsigned section_line
,
1479 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1480 _cleanup_free_
char *p
= NULL
;
1490 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1492 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1496 if (!endswith(p
, ".service")) {
1497 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1501 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1503 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1507 unit_ref_set(&s
->service
, x
);
1512 int config_parse_fdname(
1514 const char *filename
,
1516 const char *section
,
1517 unsigned section_line
,
1524 _cleanup_free_
char *p
= NULL
;
1533 if (isempty(rvalue
)) {
1534 s
->fdname
= mfree(s
->fdname
);
1538 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1540 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1544 if (!fdname_is_valid(p
)) {
1545 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1556 int config_parse_service_sockets(
1558 const char *filename
,
1560 const char *section
,
1561 unsigned section_line
,
1569 const char *word
, *state
;
1578 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1579 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1581 t
= strndup(word
, l
);
1585 r
= unit_name_printf(UNIT(s
), t
, &k
);
1587 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1591 if (!endswith(k
, ".socket")) {
1592 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1596 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1598 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1600 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1602 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1604 if (!isempty(state
))
1605 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1610 int config_parse_bus_name(
1612 const char *filename
,
1614 const char *section
,
1615 unsigned section_line
,
1622 _cleanup_free_
char *k
= NULL
;
1631 r
= unit_full_printf(u
, rvalue
, &k
);
1633 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1637 if (!service_name_is_valid(k
)) {
1638 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1642 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1645 int config_parse_service_timeout(const char *unit
,
1646 const char *filename
,
1648 const char *section
,
1649 unsigned section_line
,
1656 Service
*s
= userdata
;
1664 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1665 rvalue
, data
, userdata
);
1669 if (streq(lvalue
, "TimeoutSec")) {
1670 s
->start_timeout_defined
= true;
1671 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1672 } else if (streq(lvalue
, "TimeoutStartSec"))
1673 s
->start_timeout_defined
= true;
1678 int config_parse_busname_service(
1680 const char *filename
,
1682 const char *section
,
1683 unsigned section_line
,
1690 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1694 _cleanup_free_
char *p
= NULL
;
1701 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1703 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1707 if (!endswith(p
, ".service")) {
1708 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1712 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1714 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1718 unit_ref_set(&n
->service
, x
);
1723 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1725 int config_parse_bus_policy(
1727 const char *filename
,
1729 const char *section
,
1730 unsigned section_line
,
1737 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1738 _cleanup_free_
char *id_str
= NULL
;
1739 BusName
*busname
= data
;
1747 p
= new0(BusNamePolicy
, 1);
1751 if (streq(lvalue
, "AllowUser"))
1752 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1753 else if (streq(lvalue
, "AllowGroup"))
1754 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1756 assert_not_reached("Unknown lvalue");
1758 id_str
= strdup(rvalue
);
1762 access_str
= strpbrk(id_str
, WHITESPACE
);
1764 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy value '%s'", rvalue
);
1770 access_str
+= strspn(access_str
, WHITESPACE
);
1772 p
->access
= bus_policy_access_from_string(access_str
);
1773 if (p
->access
< 0) {
1774 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy access type '%s'", access_str
);
1781 LIST_PREPEND(policy
, busname
->policy
, p
);
1787 int config_parse_bus_endpoint_policy(
1789 const char *filename
,
1791 const char *section
,
1792 unsigned section_line
,
1799 _cleanup_free_
char *name
= NULL
;
1800 BusPolicyAccess access
;
1801 ExecContext
*c
= data
;
1810 name
= strdup(rvalue
);
1814 access_str
= strpbrk(name
, WHITESPACE
);
1816 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy value '%s'", rvalue
);
1822 access_str
+= strspn(access_str
, WHITESPACE
);
1824 access
= bus_policy_access_from_string(access_str
);
1825 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1826 access
>= _BUS_POLICY_ACCESS_MAX
) {
1827 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy access type '%s'", access_str
);
1831 if (!c
->bus_endpoint
) {
1832 r
= bus_endpoint_new(&c
->bus_endpoint
);
1834 return log_error_errno(r
, "Failed to create bus endpoint object: %m");
1837 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1840 int config_parse_working_directory(
1842 const char *filename
,
1844 const char *section
,
1845 unsigned section_line
,
1852 ExecContext
*c
= data
;
1863 if (rvalue
[0] == '-') {
1869 if (streq(rvalue
, "~")) {
1870 c
->working_directory_home
= true;
1871 c
->working_directory
= mfree(c
->working_directory
);
1873 _cleanup_free_
char *k
= NULL
;
1875 r
= unit_full_printf(u
, rvalue
, &k
);
1877 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
1881 path_kill_slashes(k
);
1883 if (!utf8_is_valid(k
)) {
1884 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
1888 if (!path_is_absolute(k
)) {
1889 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
1893 free(c
->working_directory
);
1894 c
->working_directory
= k
;
1897 c
->working_directory_home
= false;
1900 c
->working_directory_missing_ok
= missing_ok
;
1904 int config_parse_unit_env_file(const char *unit
,
1905 const char *filename
,
1907 const char *section
,
1908 unsigned section_line
,
1917 _cleanup_free_
char *n
= NULL
;
1925 if (isempty(rvalue
)) {
1926 /* Empty assignment frees the list */
1927 *env
= strv_free(*env
);
1931 r
= unit_full_printf(u
, rvalue
, &n
);
1933 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1937 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
1938 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
1942 r
= strv_extend(env
, n
);
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 */
1974 *env
= strv_free(*env
);
1979 r
= unit_full_printf(u
, rvalue
, &k
);
1981 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1992 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1993 _cleanup_free_
char *n
= NULL
;
1996 r
= cunescape_length(word
, l
, 0, &n
);
1998 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
2002 if (!env_assignment_is_valid(n
)) {
2003 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid environment assignment, ignoring: %s", rvalue
);
2007 x
= strv_env_set(*env
, n
);
2014 if (!isempty(state
))
2015 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2020 int config_parse_ip_tos(const char *unit
,
2021 const char *filename
,
2023 const char *section
,
2024 unsigned section_line
,
2031 int *ip_tos
= data
, x
;
2038 x
= ip_tos_from_string(rvalue
);
2040 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "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
, 0, "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_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2246 r
= unit_require_mounts_for(u
, n
);
2248 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount for, ignoring: %s", rvalue
);
2252 if (!isempty(state
))
2253 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2258 int config_parse_documentation(const char *unit
,
2259 const char *filename
,
2261 const char *section
,
2262 unsigned section_line
,
2278 if (isempty(rvalue
)) {
2279 /* Empty assignment resets the list */
2280 u
->documentation
= strv_free(u
->documentation
);
2284 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2285 rvalue
, data
, userdata
);
2289 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2291 if (documentation_url_is_valid(*a
))
2294 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2305 int config_parse_syscall_filter(
2307 const char *filename
,
2309 const char *section
,
2310 unsigned section_line
,
2317 static const char default_syscalls
[] =
2324 ExecContext
*c
= data
;
2326 bool invert
= false;
2327 const char *word
, *state
;
2336 if (isempty(rvalue
)) {
2337 /* Empty assignment resets the list */
2338 c
->syscall_filter
= set_free(c
->syscall_filter
);
2339 c
->syscall_whitelist
= false;
2343 if (rvalue
[0] == '~') {
2348 if (!c
->syscall_filter
) {
2349 c
->syscall_filter
= set_new(NULL
);
2350 if (!c
->syscall_filter
)
2354 /* Allow everything but the ones listed */
2355 c
->syscall_whitelist
= false;
2359 /* Allow nothing but the ones listed */
2360 c
->syscall_whitelist
= true;
2362 /* Accept default syscalls if we are on a whitelist */
2363 NULSTR_FOREACH(i
, default_syscalls
) {
2366 id
= seccomp_syscall_resolve_name(i
);
2370 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2379 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2380 _cleanup_free_
char *t
= NULL
;
2383 t
= strndup(word
, l
);
2387 id
= seccomp_syscall_resolve_name(t
);
2389 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2393 /* If we previously wanted to forbid a syscall and now
2394 * we want to allow it, then remove it from the list
2396 if (!invert
== c
->syscall_whitelist
) {
2397 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2403 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2405 if (!isempty(state
))
2406 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2408 /* Turn on NNP, but only if it wasn't configured explicitly
2409 * before, and only if we are in user mode. */
2410 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2411 c
->no_new_privileges
= true;
2416 int config_parse_syscall_archs(
2418 const char *filename
,
2420 const char *section
,
2421 unsigned section_line
,
2429 const char *word
, *state
;
2433 if (isempty(rvalue
)) {
2434 *archs
= set_free(*archs
);
2438 r
= set_ensure_allocated(archs
, NULL
);
2442 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2443 _cleanup_free_
char *t
= NULL
;
2446 t
= strndup(word
, l
);
2450 r
= seccomp_arch_from_string(t
, &a
);
2452 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call architecture, ignoring: %s", t
);
2456 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2462 if (!isempty(state
))
2463 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2468 int config_parse_syscall_errno(
2470 const char *filename
,
2472 const char *section
,
2473 unsigned section_line
,
2480 ExecContext
*c
= data
;
2487 if (isempty(rvalue
)) {
2488 /* Empty assignment resets to KILL */
2489 c
->syscall_errno
= 0;
2493 e
= errno_from_name(rvalue
);
2495 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2499 c
->syscall_errno
= e
;
2503 int config_parse_address_families(
2505 const char *filename
,
2507 const char *section
,
2508 unsigned section_line
,
2515 ExecContext
*c
= data
;
2516 bool invert
= false;
2517 const char *word
, *state
;
2525 if (isempty(rvalue
)) {
2526 /* Empty assignment resets the list */
2527 c
->address_families
= set_free(c
->address_families
);
2528 c
->address_families_whitelist
= false;
2532 if (rvalue
[0] == '~') {
2537 if (!c
->address_families
) {
2538 c
->address_families
= set_new(NULL
);
2539 if (!c
->address_families
)
2542 c
->address_families_whitelist
= !invert
;
2545 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2546 _cleanup_free_
char *t
= NULL
;
2549 t
= strndup(word
, l
);
2553 af
= af_from_name(t
);
2555 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse address family, ignoring: %s", t
);
2559 /* If we previously wanted to forbid an address family and now
2560 * we want to allow it, then remove it from the list
2562 if (!invert
== c
->address_families_whitelist
) {
2563 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2569 set_remove(c
->address_families
, INT_TO_PTR(af
));
2571 if (!isempty(state
))
2572 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2578 int config_parse_unit_slice(
2580 const char *filename
,
2582 const char *section
,
2583 unsigned section_line
,
2590 _cleanup_free_
char *k
= NULL
;
2591 Unit
*u
= userdata
, *slice
= NULL
;
2599 r
= unit_name_printf(u
, rvalue
, &k
);
2601 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2605 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2607 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2611 r
= unit_set_slice(u
, slice
);
2613 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2620 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2622 int config_parse_cpu_shares(
2624 const char *filename
,
2626 const char *section
,
2627 unsigned section_line
,
2634 uint64_t *shares
= data
;
2641 r
= cg_cpu_shares_parse(rvalue
, shares
);
2643 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2650 int config_parse_cpu_quota(
2652 const char *filename
,
2654 const char *section
,
2655 unsigned section_line
,
2662 CGroupContext
*c
= data
;
2669 if (isempty(rvalue
)) {
2670 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2674 if (!endswith(rvalue
, "%")) {
2675 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2679 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2680 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' invalid. Ignoring.", rvalue
);
2684 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2689 int config_parse_memory_limit(
2691 const char *filename
,
2693 const char *section
,
2694 unsigned section_line
,
2701 CGroupContext
*c
= data
;
2705 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2706 c
->memory_limit
= (uint64_t) -1;
2710 r
= parse_size(rvalue
, 1024, &bytes
);
2711 if (r
< 0 || bytes
< 1) {
2712 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2716 c
->memory_limit
= bytes
;
2720 int config_parse_tasks_max(
2722 const char *filename
,
2724 const char *section
,
2725 unsigned section_line
,
2732 CGroupContext
*c
= data
;
2736 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2737 c
->tasks_max
= (uint64_t) -1;
2741 r
= safe_atou64(rvalue
, &u
);
2742 if (r
< 0 || u
< 1) {
2743 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2751 int config_parse_device_allow(
2753 const char *filename
,
2755 const char *section
,
2756 unsigned section_line
,
2763 _cleanup_free_
char *path
= NULL
;
2764 CGroupContext
*c
= data
;
2765 CGroupDeviceAllow
*a
;
2769 if (isempty(rvalue
)) {
2770 while (c
->device_allow
)
2771 cgroup_context_free_device_allow(c
, c
->device_allow
);
2776 n
= strcspn(rvalue
, WHITESPACE
);
2777 path
= strndup(rvalue
, n
);
2781 if (!startswith(path
, "/dev/") &&
2782 !startswith(path
, "block-") &&
2783 !startswith(path
, "char-")) {
2784 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2788 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2792 if (!in_charset(m
, "rwm")) {
2793 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
2797 a
= new0(CGroupDeviceAllow
, 1);
2803 a
->r
= !!strchr(m
, 'r');
2804 a
->w
= !!strchr(m
, 'w');
2805 a
->m
= !!strchr(m
, 'm');
2807 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2811 int config_parse_blockio_weight(
2813 const char *filename
,
2815 const char *section
,
2816 unsigned section_line
,
2823 uint64_t *weight
= data
;
2830 r
= cg_blkio_weight_parse(rvalue
, weight
);
2832 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2839 int config_parse_blockio_device_weight(
2841 const char *filename
,
2843 const char *section
,
2844 unsigned section_line
,
2851 _cleanup_free_
char *path
= NULL
;
2852 CGroupBlockIODeviceWeight
*w
;
2853 CGroupContext
*c
= data
;
2863 if (isempty(rvalue
)) {
2864 while (c
->blockio_device_weights
)
2865 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2870 n
= strcspn(rvalue
, WHITESPACE
);
2871 weight
= rvalue
+ n
;
2872 weight
+= strspn(weight
, WHITESPACE
);
2874 if (isempty(weight
)) {
2875 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
2879 path
= strndup(rvalue
, n
);
2883 if (!path_startswith(path
, "/dev")) {
2884 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2888 r
= cg_blkio_weight_parse(weight
, &u
);
2890 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2894 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2896 w
= new0(CGroupBlockIODeviceWeight
, 1);
2905 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2909 int config_parse_blockio_bandwidth(
2911 const char *filename
,
2913 const char *section
,
2914 unsigned section_line
,
2921 _cleanup_free_
char *path
= NULL
;
2922 CGroupBlockIODeviceBandwidth
*b
;
2923 CGroupContext
*c
= data
;
2924 const char *bandwidth
;
2934 read
= streq("BlockIOReadBandwidth", lvalue
);
2936 if (isempty(rvalue
)) {
2937 CGroupBlockIODeviceBandwidth
*next
;
2939 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2940 if (b
->read
== read
)
2941 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2946 n
= strcspn(rvalue
, WHITESPACE
);
2947 bandwidth
= rvalue
+ n
;
2948 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2951 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
2955 path
= strndup(rvalue
, n
);
2959 if (!path_startswith(path
, "/dev")) {
2960 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2964 r
= parse_size(bandwidth
, 1000, &bytes
);
2965 if (r
< 0 || bytes
<= 0) {
2966 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2970 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2976 b
->bandwidth
= bytes
;
2979 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2984 int config_parse_netclass(
2986 const char *filename
,
2988 const char *section
,
2989 unsigned section_line
,
2996 CGroupContext
*c
= data
;
3004 if (streq(rvalue
, "auto")) {
3005 c
->netclass_type
= CGROUP_NETCLASS_TYPE_AUTO
;
3009 r
= safe_atou32(rvalue
, &v
);
3011 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Netclass '%s' invalid. Ignoring.", rvalue
);
3015 if (v
> CGROUP_NETCLASS_FIXED_MAX
)
3016 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3017 "Fixed netclass %" PRIu32
" out of allowed range (0-%d). Applying anyway.", v
, (uint32_t) CGROUP_NETCLASS_FIXED_MAX
);
3020 c
->netclass_type
= CGROUP_NETCLASS_TYPE_FIXED
;
3025 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3027 int config_parse_job_mode_isolate(
3029 const char *filename
,
3031 const char *section
,
3032 unsigned section_line
,
3046 r
= parse_boolean(rvalue
);
3048 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3052 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3056 int config_parse_runtime_directory(
3058 const char *filename
,
3060 const char *section
,
3061 unsigned section_line
,
3070 const char *word
, *state
;
3079 if (isempty(rvalue
)) {
3080 /* Empty assignment resets the list */
3081 *rt
= strv_free(*rt
);
3085 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3086 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3088 t
= strndup(word
, l
);
3092 r
= unit_name_printf(u
, t
, &n
);
3094 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
3098 if (!filename_is_valid(n
)) {
3099 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3103 r
= strv_push(rt
, n
);
3109 if (!isempty(state
))
3110 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "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
, 0, "Failed to parse value, ignoring: %s", word
);
3160 set
= &status_set
->signal
;
3162 if (val
< 0 || val
> 255) {
3163 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3166 set
= &status_set
->status
;
3169 r
= set_ensure_allocated(set
, NULL
);
3173 r
= set_put(*set
, INT_TO_PTR(val
));
3175 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3179 if (!isempty(state
))
3180 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3185 int config_parse_namespace_path_strv(
3187 const char *filename
,
3189 const char *section
,
3190 unsigned section_line
,
3207 if (isempty(rvalue
)) {
3208 /* Empty assignment resets the list */
3209 *sv
= strv_free(*sv
);
3213 prev
= cur
= rvalue
;
3215 _cleanup_free_
char *word
= NULL
;
3218 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3220 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring: %s", prev
);
3226 if (!utf8_is_valid(word
)) {
3227 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3232 offset
= word
[0] == '-';
3233 if (!path_is_absolute(word
+ offset
)) {
3234 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", word
);
3239 path_kill_slashes(word
+ offset
);
3241 r
= strv_push(sv
, word
);
3252 int config_parse_no_new_privileges(
3254 const char *filename
,
3256 const char *section
,
3257 unsigned section_line
,
3264 ExecContext
*c
= data
;
3272 k
= parse_boolean(rvalue
);
3274 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3278 c
->no_new_privileges
= !!k
;
3279 c
->no_new_privileges_set
= true;
3284 int config_parse_protect_home(
3286 const char *filename
,
3288 const char *section
,
3289 unsigned section_line
,
3296 ExecContext
*c
= data
;
3304 /* Our enum shall be a superset of booleans, hence first try
3305 * to parse as as boolean, and then as enum */
3307 k
= parse_boolean(rvalue
);
3309 c
->protect_home
= PROTECT_HOME_YES
;
3311 c
->protect_home
= PROTECT_HOME_NO
;
3315 h
= protect_home_from_string(rvalue
);
3317 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
3321 c
->protect_home
= h
;
3327 int config_parse_protect_system(
3329 const char *filename
,
3331 const char *section
,
3332 unsigned section_line
,
3339 ExecContext
*c
= data
;
3347 /* Our enum shall be a superset of booleans, hence first try
3348 * to parse as as boolean, and then as enum */
3350 k
= parse_boolean(rvalue
);
3352 c
->protect_system
= PROTECT_SYSTEM_YES
;
3354 c
->protect_system
= PROTECT_SYSTEM_NO
;
3358 s
= protect_system_from_string(rvalue
);
3360 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
3364 c
->protect_system
= s
;
3370 #define FOLLOW_MAX 8
3372 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3383 /* This will update the filename pointer if the loaded file is
3384 * reached by a symlink. The old string will be freed. */
3387 char *target
, *name
;
3389 if (c
++ >= FOLLOW_MAX
)
3392 path_kill_slashes(*filename
);
3394 /* Add the file name we are currently looking at to
3395 * the names of this unit, but only if it is a valid
3397 name
= basename(*filename
);
3399 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3401 id
= set_get(names
, name
);
3407 r
= set_consume(names
, id
);
3413 /* Try to open the file name, but don't if its a symlink */
3414 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3421 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3422 r
= readlink_and_make_absolute(*filename
, &target
);
3430 f
= fdopen(fd
, "re");
3441 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3449 /* Let's try to add in all symlink names we found */
3450 while ((k
= set_steal_first(names
))) {
3452 /* First try to merge in the other name into our
3454 r
= unit_merge_by_name(*u
, k
);
3458 /* Hmm, we couldn't merge the other unit into
3459 * ours? Then let's try it the other way
3462 other
= manager_get_unit((*u
)->manager
, k
);
3466 r
= unit_merge(other
, *u
);
3469 return merge_by_names(u
, names
, NULL
);
3477 unit_choose_id(*u
, id
);
3485 static int load_from_path(Unit
*u
, const char *path
) {
3487 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3488 _cleanup_fclose_
FILE *f
= NULL
;
3489 _cleanup_free_
char *filename
= NULL
;
3497 symlink_names
= set_new(&string_hash_ops
);
3501 if (path_is_absolute(path
)) {
3503 filename
= strdup(path
);
3507 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3509 filename
= mfree(filename
);
3517 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3519 /* Instead of opening the path right away, we manually
3520 * follow all symlinks and add their name to our unit
3521 * name set while doing so */
3522 filename
= path_make_absolute(path
, *p
);
3526 if (u
->manager
->unit_path_cache
&&
3527 !set_get(u
->manager
->unit_path_cache
, filename
))
3530 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3533 filename
= mfree(filename
);
3537 /* Empty the symlink names for the next run */
3538 set_clear_free(symlink_names
);
3547 /* Hmm, no suitable file found? */
3551 r
= merge_by_names(&merged
, symlink_names
, id
);
3556 u
->load_state
= UNIT_MERGED
;
3560 if (fstat(fileno(f
), &st
) < 0)
3563 if (null_or_empty(&st
))
3564 u
->load_state
= UNIT_MASKED
;
3566 u
->load_state
= UNIT_LOADED
;
3568 /* Now, parse the file contents */
3569 r
= config_parse(u
->id
, filename
, f
,
3570 UNIT_VTABLE(u
)->sections
,
3571 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3572 false, true, false, u
);
3577 free(u
->fragment_path
);
3578 u
->fragment_path
= filename
;
3581 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3583 if (u
->source_path
) {
3584 if (stat(u
->source_path
, &st
) >= 0)
3585 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3587 u
->source_mtime
= 0;
3593 int unit_load_fragment(Unit
*u
) {
3599 assert(u
->load_state
== UNIT_STUB
);
3603 u
->load_state
= UNIT_LOADED
;
3607 /* First, try to find the unit under its id. We always look
3608 * for unit files in the default directories, to make it easy
3609 * to override things by placing things in /etc/systemd/system */
3610 r
= load_from_path(u
, u
->id
);
3614 /* Try to find an alias we can load this with */
3615 if (u
->load_state
== UNIT_STUB
) {
3616 SET_FOREACH(t
, u
->names
, i
) {
3621 r
= load_from_path(u
, t
);
3625 if (u
->load_state
!= UNIT_STUB
)
3630 /* And now, try looking for it under the suggested (originally linked) path */
3631 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3633 r
= load_from_path(u
, u
->fragment_path
);
3637 if (u
->load_state
== UNIT_STUB
)
3638 /* Hmm, this didn't work? Then let's get rid
3639 * of the fragment path stored for us, so that
3640 * we don't point to an invalid location. */
3641 u
->fragment_path
= mfree(u
->fragment_path
);
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_uint64
, "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_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
);