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 "path-util.h"
50 #include "seccomp-util.h"
52 #include "securebits.h"
53 #include "signal-util.h"
55 #include "unit-name.h"
56 #include "unit-printf.h"
59 #include "load-fragment.h"
61 int config_parse_warn_compat(
66 unsigned section_line
,
72 Disabled reason
= ltype
;
75 case DISABLED_CONFIGURATION
:
76 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
77 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
80 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
81 "Support for option %s= has been removed and it is ignored", lvalue
);
83 case DISABLED_EXPERIMENTAL
:
84 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
85 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
92 int config_parse_unit_deps(const char *unit
,
96 unsigned section_line
,
103 UnitDependency d
= ltype
;
105 const char *word
, *state
;
112 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
113 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
116 t
= strndup(word
, l
);
120 r
= unit_name_printf(u
, t
, &k
);
122 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
126 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
128 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
131 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring.");
136 int config_parse_unit_string_printf(
138 const char *filename
,
141 unsigned section_line
,
148 _cleanup_free_
char *k
= NULL
;
157 r
= unit_full_printf(u
, rvalue
, &k
);
159 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
163 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
166 int config_parse_unit_strv_printf(
168 const char *filename
,
171 unsigned section_line
,
179 _cleanup_free_
char *k
= NULL
;
187 r
= unit_full_printf(u
, rvalue
, &k
);
189 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
193 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
196 int config_parse_unit_path_printf(
198 const char *filename
,
201 unsigned section_line
,
208 _cleanup_free_
char *k
= NULL
;
217 r
= unit_full_printf(u
, rvalue
, &k
);
219 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
223 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
226 int config_parse_unit_path_strv_printf(
228 const char *filename
,
231 unsigned section_line
,
239 const char *word
, *state
;
249 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
250 _cleanup_free_
char *k
= NULL
;
256 r
= unit_full_printf(u
, t
, &k
);
258 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", t
);
262 if (!utf8_is_valid(k
)) {
263 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
267 if (!path_is_absolute(k
)) {
268 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Symlink path %s is not absolute, ignoring: %m", k
);
272 path_kill_slashes(k
);
281 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring.");
286 int config_parse_socket_listen(const char *unit
,
287 const char *filename
,
290 unsigned section_line
,
297 _cleanup_free_ SocketPort
*p
= NULL
;
309 if (isempty(rvalue
)) {
310 /* An empty assignment removes all ports */
311 socket_free_ports(s
);
315 p
= new0(SocketPort
, 1);
319 if (ltype
!= SOCKET_SOCKET
) {
322 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
324 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
328 path_kill_slashes(p
->path
);
330 } else if (streq(lvalue
, "ListenNetlink")) {
331 _cleanup_free_
char *k
= NULL
;
333 p
->type
= SOCKET_SOCKET
;
334 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
336 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
340 r
= socket_address_parse_netlink(&p
->address
, k
);
342 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
347 _cleanup_free_
char *k
= NULL
;
349 p
->type
= SOCKET_SOCKET
;
350 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
352 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
356 r
= socket_address_parse_and_warn(&p
->address
, k
);
358 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
362 if (streq(lvalue
, "ListenStream"))
363 p
->address
.type
= SOCK_STREAM
;
364 else if (streq(lvalue
, "ListenDatagram"))
365 p
->address
.type
= SOCK_DGRAM
;
367 assert(streq(lvalue
, "ListenSequentialPacket"));
368 p
->address
.type
= SOCK_SEQPACKET
;
371 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
372 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
378 p
->auxiliary_fds
= NULL
;
379 p
->n_auxiliary_fds
= 0;
383 LIST_FIND_TAIL(port
, s
->ports
, tail
);
384 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
386 LIST_PREPEND(port
, s
->ports
, p
);
392 int config_parse_socket_bind(const char *unit
,
393 const char *filename
,
396 unsigned section_line
,
404 SocketAddressBindIPv6Only b
;
413 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
417 r
= parse_boolean(rvalue
);
419 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
423 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
425 s
->bind_ipv6_only
= b
;
430 int config_parse_exec_nice(const char *unit
,
431 const char *filename
,
434 unsigned section_line
,
441 ExecContext
*c
= data
;
449 r
= safe_atoi(rvalue
, &priority
);
451 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
455 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
456 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Nice priority out of range, ignoring: %s", rvalue
);
466 int config_parse_exec_oom_score_adjust(const char* unit
,
467 const char *filename
,
470 unsigned section_line
,
477 ExecContext
*c
= data
;
485 r
= safe_atoi(rvalue
, &oa
);
487 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
491 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
492 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
496 c
->oom_score_adjust
= oa
;
497 c
->oom_score_adjust_set
= true;
502 int config_parse_exec(
504 const char *filename
,
507 unsigned section_line
,
514 ExecCommand
**e
= data
;
526 rvalue
+= strspn(rvalue
, WHITESPACE
);
529 if (isempty(rvalue
)) {
530 /* An empty assignment resets the list */
531 *e
= exec_command_free_list(*e
);
537 _cleanup_strv_free_
char **n
= NULL
;
538 size_t nlen
= 0, nbufsize
= 0;
539 _cleanup_free_ ExecCommand
*nce
= NULL
;
540 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
542 bool separate_argv0
= false, ignore
= false;
546 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
551 for (i
= 0; i
< 2; i
++) {
552 /* We accept an absolute path as first argument, or
553 * alternatively an absolute prefixed with @ to allow
554 * overriding of argv[0]. */
555 if (*f
== '-' && !ignore
)
557 else if (*f
== '@' && !separate_argv0
)
558 separate_argv0
= true;
565 /* First word is either "-" or "@" with no command. */
566 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty path in command line, ignoring: \"%s\"", rvalue
);
569 if (!string_is_safe(f
)) {
570 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path contains special characters, ignoring: %s", rvalue
);
573 if (!path_is_absolute(f
)) {
574 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path is not absolute, ignoring: %s", rvalue
);
577 if (endswith(f
, "/")) {
578 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path specifies a directory, ignoring: %s", rvalue
);
582 if (f
== firstword
) {
591 if (!separate_argv0
) {
592 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
601 path_kill_slashes(path
);
603 while (!isempty(p
)) {
604 _cleanup_free_
char *word
= NULL
;
606 /* Check explicitly for an unquoted semicolon as
607 * command separator token. */
608 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
610 p
+= strspn(p
, WHITESPACE
);
615 /* Check for \; explicitly, to not confuse it with \\;
616 * or "\;" or "\\;" etc. extract_first_word would
617 * return the same for all of those. */
618 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
620 p
+= strspn(p
, WHITESPACE
);
621 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
631 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
637 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
645 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
649 nce
= new0(ExecCommand
, 1);
655 nce
->ignore
= ignore
;
657 exec_command_append_list(e
, nce
);
659 /* Do not _cleanup_free_ these. */
670 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
671 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
673 int config_parse_socket_bindtodevice(const char* unit
,
674 const char *filename
,
677 unsigned section_line
,
692 if (rvalue
[0] && !streq(rvalue
, "*")) {
699 free(s
->bind_to_device
);
700 s
->bind_to_device
= n
;
705 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
706 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
708 int config_parse_exec_io_class(const char *unit
,
709 const char *filename
,
712 unsigned section_line
,
719 ExecContext
*c
= data
;
727 x
= ioprio_class_from_string(rvalue
);
729 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
733 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
734 c
->ioprio_set
= true;
739 int config_parse_exec_io_priority(const char *unit
,
740 const char *filename
,
743 unsigned section_line
,
750 ExecContext
*c
= data
;
758 r
= safe_atoi(rvalue
, &i
);
759 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
760 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
764 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
765 c
->ioprio_set
= true;
770 int config_parse_exec_cpu_sched_policy(const char *unit
,
771 const char *filename
,
774 unsigned section_line
,
782 ExecContext
*c
= data
;
790 x
= sched_policy_from_string(rvalue
);
792 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
796 c
->cpu_sched_policy
= x
;
797 /* Moving to or from real-time policy? We need to adjust the priority */
798 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
799 c
->cpu_sched_set
= true;
804 int config_parse_exec_cpu_sched_prio(const char *unit
,
805 const char *filename
,
808 unsigned section_line
,
815 ExecContext
*c
= data
;
823 r
= safe_atoi(rvalue
, &i
);
825 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
829 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
830 min
= sched_get_priority_min(c
->cpu_sched_policy
);
831 max
= sched_get_priority_max(c
->cpu_sched_policy
);
833 if (i
< min
|| i
> max
) {
834 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
838 c
->cpu_sched_priority
= i
;
839 c
->cpu_sched_set
= true;
844 int config_parse_exec_cpu_affinity(const char *unit
,
845 const char *filename
,
848 unsigned section_line
,
855 ExecContext
*c
= data
;
856 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
864 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
872 /* An empty assignment resets the CPU list */
878 c
->cpuset_ncpus
= ncpus
;
883 int config_parse_exec_capabilities(const char *unit
,
884 const char *filename
,
887 unsigned section_line
,
894 ExecContext
*c
= data
;
902 cap
= cap_from_text(rvalue
);
904 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
, "Failed to parse capabilities, ignoring: %s", rvalue
);
909 cap_free(c
->capabilities
);
910 c
->capabilities
= cap
;
915 int config_parse_exec_secure_bits(const char *unit
,
916 const char *filename
,
919 unsigned section_line
,
926 ExecContext
*c
= data
;
928 const char *word
, *state
;
935 if (isempty(rvalue
)) {
936 /* An empty assignment resets the field */
941 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
942 if (first_word(word
, "keep-caps"))
943 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
944 else if (first_word(word
, "keep-caps-locked"))
945 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
946 else if (first_word(word
, "no-setuid-fixup"))
947 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
948 else if (first_word(word
, "no-setuid-fixup-locked"))
949 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
950 else if (first_word(word
, "noroot"))
951 c
->secure_bits
|= 1<<SECURE_NOROOT
;
952 else if (first_word(word
, "noroot-locked"))
953 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
955 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse secure bits, ignoring: %s", rvalue
);
960 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, garbage at the end, ignoring.");
965 int config_parse_bounding_set(const char *unit
,
966 const char *filename
,
969 unsigned section_line
,
976 uint64_t *capability_bounding_set_drop
= data
;
977 const char *word
, *state
;
987 if (rvalue
[0] == '~') {
992 /* Note that we store this inverted internally, since the
993 * kernel wants it like this. But we actually expose it
994 * non-inverted everywhere to have a fully normalized
997 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
998 _cleanup_free_
char *t
= NULL
;
1001 t
= strndup(word
, l
);
1005 cap
= capability_from_name(t
);
1007 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability in bounding set, ignoring: %s", t
);
1011 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1013 if (!isempty(state
))
1014 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1017 *capability_bounding_set_drop
|= sum
;
1019 *capability_bounding_set_drop
|= ~sum
;
1024 int config_parse_limit(const char *unit
,
1025 const char *filename
,
1027 const char *section
,
1028 unsigned section_line
,
1035 struct rlimit
**rl
= data
;
1036 unsigned long long u
;
1045 if (streq(rvalue
, "infinity"))
1046 u
= (unsigned long long) RLIM_INFINITY
;
1050 r
= safe_atollu(rvalue
, &u
);
1052 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1058 *rl
= new(struct rlimit
, 1);
1063 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1067 #ifdef HAVE_SYSV_COMPAT
1068 int config_parse_sysv_priority(const char *unit
,
1069 const char *filename
,
1071 const char *section
,
1072 unsigned section_line
,
1079 int *priority
= data
;
1087 r
= safe_atoi(rvalue
, &i
);
1088 if (r
< 0 || i
< 0) {
1089 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1093 *priority
= (int) i
;
1098 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1099 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1101 int config_parse_exec_mount_flags(const char *unit
,
1102 const char *filename
,
1104 const char *section
,
1105 unsigned section_line
,
1112 ExecContext
*c
= data
;
1113 const char *word
, *state
;
1115 unsigned long flags
= 0;
1122 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1123 _cleanup_free_
char *t
;
1125 t
= strndup(word
, l
);
1129 if (streq(t
, "shared"))
1131 else if (streq(t
, "slave"))
1133 else if (streq(t
, "private"))
1136 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1140 if (!isempty(state
))
1141 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1143 c
->mount_flags
= flags
;
1147 int config_parse_exec_selinux_context(
1149 const char *filename
,
1151 const char *section
,
1152 unsigned section_line
,
1159 ExecContext
*c
= data
;
1170 if (isempty(rvalue
)) {
1171 c
->selinux_context
= mfree(c
->selinux_context
);
1172 c
->selinux_context_ignore
= false;
1176 if (rvalue
[0] == '-') {
1182 r
= unit_name_printf(u
, rvalue
, &k
);
1184 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1188 free(c
->selinux_context
);
1189 c
->selinux_context
= k
;
1190 c
->selinux_context_ignore
= ignore
;
1195 int config_parse_exec_apparmor_profile(
1197 const char *filename
,
1199 const char *section
,
1200 unsigned section_line
,
1207 ExecContext
*c
= data
;
1218 if (isempty(rvalue
)) {
1219 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1220 c
->apparmor_profile_ignore
= false;
1224 if (rvalue
[0] == '-') {
1230 r
= unit_name_printf(u
, rvalue
, &k
);
1232 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1236 free(c
->apparmor_profile
);
1237 c
->apparmor_profile
= k
;
1238 c
->apparmor_profile_ignore
= ignore
;
1243 int config_parse_exec_smack_process_label(
1245 const char *filename
,
1247 const char *section
,
1248 unsigned section_line
,
1255 ExecContext
*c
= data
;
1266 if (isempty(rvalue
)) {
1267 c
->smack_process_label
= mfree(c
->smack_process_label
);
1268 c
->smack_process_label_ignore
= false;
1272 if (rvalue
[0] == '-') {
1278 r
= unit_name_printf(u
, rvalue
, &k
);
1280 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1284 free(c
->smack_process_label
);
1285 c
->smack_process_label
= k
;
1286 c
->smack_process_label_ignore
= ignore
;
1291 int config_parse_timer(const char *unit
,
1292 const char *filename
,
1294 const char *section
,
1295 unsigned section_line
,
1306 CalendarSpec
*c
= NULL
;
1313 if (isempty(rvalue
)) {
1314 /* Empty assignment resets list */
1315 timer_free_values(t
);
1319 b
= timer_base_from_string(lvalue
);
1321 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1325 if (b
== TIMER_CALENDAR
) {
1326 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1327 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", rvalue
);
1331 if (parse_sec(rvalue
, &u
) < 0) {
1332 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", rvalue
);
1337 v
= new0(TimerValue
, 1);
1339 calendar_spec_free(c
);
1345 v
->calendar_spec
= c
;
1347 LIST_PREPEND(value
, t
->values
, v
);
1352 int config_parse_trigger_unit(
1354 const char *filename
,
1356 const char *section
,
1357 unsigned section_line
,
1364 _cleanup_free_
char *p
= NULL
;
1374 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1375 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1379 r
= unit_name_printf(u
, rvalue
, &p
);
1381 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1385 type
= unit_name_to_type(p
);
1387 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1391 if (type
== u
->type
) {
1392 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1396 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1398 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1405 int config_parse_path_spec(const char *unit
,
1406 const char *filename
,
1408 const char *section
,
1409 unsigned section_line
,
1419 _cleanup_free_
char *k
= NULL
;
1427 if (isempty(rvalue
)) {
1428 /* Empty assignment clears list */
1433 b
= path_type_from_string(lvalue
);
1435 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1439 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1441 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1445 if (!path_is_absolute(k
)) {
1446 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1450 s
= new0(PathSpec
, 1);
1455 s
->path
= path_kill_slashes(k
);
1460 LIST_PREPEND(spec
, p
->specs
, s
);
1465 int config_parse_socket_service(
1467 const char *filename
,
1469 const char *section
,
1470 unsigned section_line
,
1477 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1478 _cleanup_free_
char *p
= NULL
;
1488 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1490 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1494 if (!endswith(p
, ".service")) {
1495 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1499 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1501 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1505 unit_ref_set(&s
->service
, x
);
1510 int config_parse_fdname(
1512 const char *filename
,
1514 const char *section
,
1515 unsigned section_line
,
1522 _cleanup_free_
char *p
= NULL
;
1531 if (isempty(rvalue
)) {
1532 s
->fdname
= mfree(s
->fdname
);
1536 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1538 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1542 if (!fdname_is_valid(p
)) {
1543 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1554 int config_parse_service_sockets(
1556 const char *filename
,
1558 const char *section
,
1559 unsigned section_line
,
1567 const char *word
, *state
;
1576 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1577 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1579 t
= strndup(word
, l
);
1583 r
= unit_name_printf(UNIT(s
), t
, &k
);
1585 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1589 if (!endswith(k
, ".socket")) {
1590 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1594 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1596 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1598 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1600 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1602 if (!isempty(state
))
1603 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1608 int config_parse_bus_name(
1610 const char *filename
,
1612 const char *section
,
1613 unsigned section_line
,
1620 _cleanup_free_
char *k
= NULL
;
1629 r
= unit_full_printf(u
, rvalue
, &k
);
1631 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1635 if (!service_name_is_valid(k
)) {
1636 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1640 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1643 int config_parse_service_timeout(const char *unit
,
1644 const char *filename
,
1646 const char *section
,
1647 unsigned section_line
,
1654 Service
*s
= userdata
;
1662 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1663 rvalue
, data
, userdata
);
1667 if (streq(lvalue
, "TimeoutSec")) {
1668 s
->start_timeout_defined
= true;
1669 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1670 } else if (streq(lvalue
, "TimeoutStartSec"))
1671 s
->start_timeout_defined
= true;
1676 int config_parse_busname_service(
1678 const char *filename
,
1680 const char *section
,
1681 unsigned section_line
,
1688 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1692 _cleanup_free_
char *p
= NULL
;
1699 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1701 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1705 if (!endswith(p
, ".service")) {
1706 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1710 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1712 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1716 unit_ref_set(&n
->service
, x
);
1721 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1723 int config_parse_bus_policy(
1725 const char *filename
,
1727 const char *section
,
1728 unsigned section_line
,
1735 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1736 _cleanup_free_
char *id_str
= NULL
;
1737 BusName
*busname
= data
;
1745 p
= new0(BusNamePolicy
, 1);
1749 if (streq(lvalue
, "AllowUser"))
1750 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1751 else if (streq(lvalue
, "AllowGroup"))
1752 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1754 assert_not_reached("Unknown lvalue");
1756 id_str
= strdup(rvalue
);
1760 access_str
= strpbrk(id_str
, WHITESPACE
);
1762 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy value '%s'", rvalue
);
1768 access_str
+= strspn(access_str
, WHITESPACE
);
1770 p
->access
= bus_policy_access_from_string(access_str
);
1771 if (p
->access
< 0) {
1772 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy access type '%s'", access_str
);
1779 LIST_PREPEND(policy
, busname
->policy
, p
);
1785 int config_parse_bus_endpoint_policy(
1787 const char *filename
,
1789 const char *section
,
1790 unsigned section_line
,
1797 _cleanup_free_
char *name
= NULL
;
1798 BusPolicyAccess access
;
1799 ExecContext
*c
= data
;
1808 name
= strdup(rvalue
);
1812 access_str
= strpbrk(name
, WHITESPACE
);
1814 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy value '%s'", rvalue
);
1820 access_str
+= strspn(access_str
, WHITESPACE
);
1822 access
= bus_policy_access_from_string(access_str
);
1823 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1824 access
>= _BUS_POLICY_ACCESS_MAX
) {
1825 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy access type '%s'", access_str
);
1829 if (!c
->bus_endpoint
) {
1830 r
= bus_endpoint_new(&c
->bus_endpoint
);
1832 return log_error_errno(r
, "Failed to create bus endpoint object: %m");
1835 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1838 int config_parse_working_directory(
1840 const char *filename
,
1842 const char *section
,
1843 unsigned section_line
,
1850 ExecContext
*c
= data
;
1861 if (rvalue
[0] == '-') {
1867 if (streq(rvalue
, "~")) {
1868 c
->working_directory_home
= true;
1869 c
->working_directory
= mfree(c
->working_directory
);
1871 _cleanup_free_
char *k
= NULL
;
1873 r
= unit_full_printf(u
, rvalue
, &k
);
1875 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
1879 path_kill_slashes(k
);
1881 if (!utf8_is_valid(k
)) {
1882 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
1886 if (!path_is_absolute(k
)) {
1887 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
1891 free(c
->working_directory
);
1892 c
->working_directory
= k
;
1895 c
->working_directory_home
= false;
1898 c
->working_directory_missing_ok
= missing_ok
;
1902 int config_parse_unit_env_file(const char *unit
,
1903 const char *filename
,
1905 const char *section
,
1906 unsigned section_line
,
1915 _cleanup_free_
char *n
= NULL
;
1923 if (isempty(rvalue
)) {
1924 /* Empty assignment frees the list */
1925 *env
= strv_free(*env
);
1929 r
= unit_full_printf(u
, rvalue
, &n
);
1931 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1935 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
1936 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
1940 r
= strv_extend(env
, n
);
1947 int config_parse_environ(const char *unit
,
1948 const char *filename
,
1950 const char *section
,
1951 unsigned section_line
,
1960 const char *word
, *state
;
1962 _cleanup_free_
char *k
= NULL
;
1970 if (isempty(rvalue
)) {
1971 /* Empty assignment resets the list */
1972 *env
= strv_free(*env
);
1977 r
= unit_full_printf(u
, rvalue
, &k
);
1979 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
, 0, "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
, 0, "Trailing garbage, ignoring.");
2018 int config_parse_ip_tos(const char *unit
,
2019 const char *filename
,
2021 const char *section
,
2022 unsigned section_line
,
2029 int *ip_tos
= data
, x
;
2036 x
= ip_tos_from_string(rvalue
);
2038 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2046 int config_parse_unit_condition_path(
2048 const char *filename
,
2050 const char *section
,
2051 unsigned section_line
,
2058 _cleanup_free_
char *p
= NULL
;
2059 Condition
**list
= data
, *c
;
2060 ConditionType t
= ltype
;
2061 bool trigger
, negate
;
2070 if (isempty(rvalue
)) {
2071 /* Empty assignment resets the list */
2072 *list
= condition_free_list(*list
);
2076 trigger
= rvalue
[0] == '|';
2080 negate
= rvalue
[0] == '!';
2084 r
= unit_full_printf(u
, rvalue
, &p
);
2086 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2090 if (!path_is_absolute(p
)) {
2091 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2095 c
= condition_new(t
, p
, trigger
, negate
);
2099 LIST_PREPEND(conditions
, *list
, c
);
2103 int config_parse_unit_condition_string(
2105 const char *filename
,
2107 const char *section
,
2108 unsigned section_line
,
2115 _cleanup_free_
char *s
= NULL
;
2116 Condition
**list
= data
, *c
;
2117 ConditionType t
= ltype
;
2118 bool trigger
, negate
;
2127 if (isempty(rvalue
)) {
2128 /* Empty assignment resets the list */
2129 *list
= condition_free_list(*list
);
2133 trigger
= rvalue
[0] == '|';
2137 negate
= rvalue
[0] == '!';
2141 r
= unit_full_printf(u
, rvalue
, &s
);
2143 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2147 c
= condition_new(t
, s
, trigger
, negate
);
2151 LIST_PREPEND(conditions
, *list
, c
);
2155 int config_parse_unit_condition_null(
2157 const char *filename
,
2159 const char *section
,
2160 unsigned section_line
,
2167 Condition
**list
= data
, *c
;
2168 bool trigger
, negate
;
2176 if (isempty(rvalue
)) {
2177 /* Empty assignment resets the list */
2178 *list
= condition_free_list(*list
);
2182 trigger
= rvalue
[0] == '|';
2186 negate
= rvalue
[0] == '!';
2190 b
= parse_boolean(rvalue
);
2192 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2199 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2203 LIST_PREPEND(conditions
, *list
, c
);
2207 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2208 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2210 int config_parse_unit_requires_mounts_for(
2212 const char *filename
,
2214 const char *section
,
2215 unsigned section_line
,
2223 const char *word
, *state
;
2231 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2233 _cleanup_free_
char *n
;
2235 n
= strndup(word
, l
);
2239 if (!utf8_is_valid(n
)) {
2240 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2244 r
= unit_require_mounts_for(u
, n
);
2246 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount for, ignoring: %s", rvalue
);
2250 if (!isempty(state
))
2251 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2256 int config_parse_documentation(const char *unit
,
2257 const char *filename
,
2259 const char *section
,
2260 unsigned section_line
,
2276 if (isempty(rvalue
)) {
2277 /* Empty assignment resets the list */
2278 u
->documentation
= strv_free(u
->documentation
);
2282 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2283 rvalue
, data
, userdata
);
2287 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2289 if (documentation_url_is_valid(*a
))
2292 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2303 int config_parse_syscall_filter(
2305 const char *filename
,
2307 const char *section
,
2308 unsigned section_line
,
2315 static const char default_syscalls
[] =
2322 ExecContext
*c
= data
;
2324 bool invert
= false;
2325 const char *word
, *state
;
2334 if (isempty(rvalue
)) {
2335 /* Empty assignment resets the list */
2336 c
->syscall_filter
= set_free(c
->syscall_filter
);
2337 c
->syscall_whitelist
= false;
2341 if (rvalue
[0] == '~') {
2346 if (!c
->syscall_filter
) {
2347 c
->syscall_filter
= set_new(NULL
);
2348 if (!c
->syscall_filter
)
2352 /* Allow everything but the ones listed */
2353 c
->syscall_whitelist
= false;
2357 /* Allow nothing but the ones listed */
2358 c
->syscall_whitelist
= true;
2360 /* Accept default syscalls if we are on a whitelist */
2361 NULSTR_FOREACH(i
, default_syscalls
) {
2364 id
= seccomp_syscall_resolve_name(i
);
2368 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2377 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2378 _cleanup_free_
char *t
= NULL
;
2381 t
= strndup(word
, l
);
2385 id
= seccomp_syscall_resolve_name(t
);
2387 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2391 /* If we previously wanted to forbid a syscall and now
2392 * we want to allow it, then remove it from the list
2394 if (!invert
== c
->syscall_whitelist
) {
2395 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2401 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2403 if (!isempty(state
))
2404 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2406 /* Turn on NNP, but only if it wasn't configured explicitly
2407 * before, and only if we are in user mode. */
2408 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2409 c
->no_new_privileges
= true;
2414 int config_parse_syscall_archs(
2416 const char *filename
,
2418 const char *section
,
2419 unsigned section_line
,
2427 const char *word
, *state
;
2431 if (isempty(rvalue
)) {
2432 *archs
= set_free(*archs
);
2436 r
= set_ensure_allocated(archs
, NULL
);
2440 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2441 _cleanup_free_
char *t
= NULL
;
2444 t
= strndup(word
, l
);
2448 r
= seccomp_arch_from_string(t
, &a
);
2450 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call architecture, ignoring: %s", t
);
2454 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2460 if (!isempty(state
))
2461 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2466 int config_parse_syscall_errno(
2468 const char *filename
,
2470 const char *section
,
2471 unsigned section_line
,
2478 ExecContext
*c
= data
;
2485 if (isempty(rvalue
)) {
2486 /* Empty assignment resets to KILL */
2487 c
->syscall_errno
= 0;
2491 e
= errno_from_name(rvalue
);
2493 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2497 c
->syscall_errno
= e
;
2501 int config_parse_address_families(
2503 const char *filename
,
2505 const char *section
,
2506 unsigned section_line
,
2513 ExecContext
*c
= data
;
2514 bool invert
= false;
2515 const char *word
, *state
;
2523 if (isempty(rvalue
)) {
2524 /* Empty assignment resets the list */
2525 c
->address_families
= set_free(c
->address_families
);
2526 c
->address_families_whitelist
= false;
2530 if (rvalue
[0] == '~') {
2535 if (!c
->address_families
) {
2536 c
->address_families
= set_new(NULL
);
2537 if (!c
->address_families
)
2540 c
->address_families_whitelist
= !invert
;
2543 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2544 _cleanup_free_
char *t
= NULL
;
2547 t
= strndup(word
, l
);
2551 af
= af_from_name(t
);
2553 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse address family, ignoring: %s", t
);
2557 /* If we previously wanted to forbid an address family and now
2558 * we want to allow it, then remove it from the list
2560 if (!invert
== c
->address_families_whitelist
) {
2561 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2567 set_remove(c
->address_families
, INT_TO_PTR(af
));
2569 if (!isempty(state
))
2570 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2576 int config_parse_unit_slice(
2578 const char *filename
,
2580 const char *section
,
2581 unsigned section_line
,
2588 _cleanup_free_
char *k
= NULL
;
2589 Unit
*u
= userdata
, *slice
= NULL
;
2597 r
= unit_name_printf(u
, rvalue
, &k
);
2599 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2603 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2605 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2609 r
= unit_set_slice(u
, slice
);
2611 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2618 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2620 int config_parse_cpu_shares(
2622 const char *filename
,
2624 const char *section
,
2625 unsigned section_line
,
2632 uint64_t *shares
= data
;
2639 r
= cg_cpu_shares_parse(rvalue
, shares
);
2641 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2648 int config_parse_cpu_quota(
2650 const char *filename
,
2652 const char *section
,
2653 unsigned section_line
,
2660 CGroupContext
*c
= data
;
2667 if (isempty(rvalue
)) {
2668 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2672 if (!endswith(rvalue
, "%")) {
2673 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2677 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2678 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' invalid. Ignoring.", rvalue
);
2682 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2687 int config_parse_memory_limit(
2689 const char *filename
,
2691 const char *section
,
2692 unsigned section_line
,
2699 CGroupContext
*c
= data
;
2703 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2704 c
->memory_limit
= (uint64_t) -1;
2708 r
= parse_size(rvalue
, 1024, &bytes
);
2709 if (r
< 0 || bytes
< 1) {
2710 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2714 c
->memory_limit
= bytes
;
2718 int config_parse_tasks_max(
2720 const char *filename
,
2722 const char *section
,
2723 unsigned section_line
,
2730 CGroupContext
*c
= data
;
2734 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2735 c
->tasks_max
= (uint64_t) -1;
2739 r
= safe_atou64(rvalue
, &u
);
2740 if (r
< 0 || u
< 1) {
2741 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2748 int config_parse_device_allow(
2750 const char *filename
,
2752 const char *section
,
2753 unsigned section_line
,
2760 _cleanup_free_
char *path
= NULL
;
2761 CGroupContext
*c
= data
;
2762 CGroupDeviceAllow
*a
;
2766 if (isempty(rvalue
)) {
2767 while (c
->device_allow
)
2768 cgroup_context_free_device_allow(c
, c
->device_allow
);
2773 n
= strcspn(rvalue
, WHITESPACE
);
2774 path
= strndup(rvalue
, n
);
2778 if (!startswith(path
, "/dev/") &&
2779 !startswith(path
, "block-") &&
2780 !startswith(path
, "char-")) {
2781 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "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
, 0, "Invalid device rights '%s'. Ignoring.", m
);
2794 a
= new0(CGroupDeviceAllow
, 1);
2800 a
->r
= !!strchr(m
, 'r');
2801 a
->w
= !!strchr(m
, 'w');
2802 a
->m
= !!strchr(m
, 'm');
2804 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2808 int config_parse_blockio_weight(
2810 const char *filename
,
2812 const char *section
,
2813 unsigned section_line
,
2820 uint64_t *weight
= data
;
2827 r
= cg_blkio_weight_parse(rvalue
, weight
);
2829 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2836 int config_parse_blockio_device_weight(
2838 const char *filename
,
2840 const char *section
,
2841 unsigned section_line
,
2848 _cleanup_free_
char *path
= NULL
;
2849 CGroupBlockIODeviceWeight
*w
;
2850 CGroupContext
*c
= data
;
2860 if (isempty(rvalue
)) {
2861 while (c
->blockio_device_weights
)
2862 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2867 n
= strcspn(rvalue
, WHITESPACE
);
2868 weight
= rvalue
+ n
;
2869 weight
+= strspn(weight
, WHITESPACE
);
2871 if (isempty(weight
)) {
2872 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
2876 path
= strndup(rvalue
, n
);
2880 if (!path_startswith(path
, "/dev")) {
2881 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2885 r
= cg_blkio_weight_parse(weight
, &u
);
2887 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2891 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2893 w
= new0(CGroupBlockIODeviceWeight
, 1);
2902 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2906 int config_parse_blockio_bandwidth(
2908 const char *filename
,
2910 const char *section
,
2911 unsigned section_line
,
2918 _cleanup_free_
char *path
= NULL
;
2919 CGroupBlockIODeviceBandwidth
*b
;
2920 CGroupContext
*c
= data
;
2921 const char *bandwidth
;
2931 read
= streq("BlockIOReadBandwidth", lvalue
);
2933 if (isempty(rvalue
)) {
2934 CGroupBlockIODeviceBandwidth
*next
;
2936 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2937 if (b
->read
== read
)
2938 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2943 n
= strcspn(rvalue
, WHITESPACE
);
2944 bandwidth
= rvalue
+ n
;
2945 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2948 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
2952 path
= strndup(rvalue
, n
);
2956 if (!path_startswith(path
, "/dev")) {
2957 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2961 r
= parse_size(bandwidth
, 1000, &bytes
);
2962 if (r
< 0 || bytes
<= 0) {
2963 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2967 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2973 b
->bandwidth
= bytes
;
2976 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2981 int config_parse_netclass(
2983 const char *filename
,
2985 const char *section
,
2986 unsigned section_line
,
2993 CGroupContext
*c
= data
;
3001 if (streq(rvalue
, "auto")) {
3002 c
->netclass_type
= CGROUP_NETCLASS_TYPE_AUTO
;
3006 r
= safe_atou32(rvalue
, &v
);
3008 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Netclass '%s' invalid. Ignoring.", rvalue
);
3012 if (v
> CGROUP_NETCLASS_FIXED_MAX
)
3013 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3014 "Fixed netclass %" PRIu32
" out of allowed range (0-%d). Applying anyway.", v
, (uint32_t) CGROUP_NETCLASS_FIXED_MAX
);
3017 c
->netclass_type
= CGROUP_NETCLASS_TYPE_FIXED
;
3022 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3024 int config_parse_job_mode_isolate(
3026 const char *filename
,
3028 const char *section
,
3029 unsigned section_line
,
3043 r
= parse_boolean(rvalue
);
3045 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3049 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3053 int config_parse_runtime_directory(
3055 const char *filename
,
3057 const char *section
,
3058 unsigned section_line
,
3067 const char *word
, *state
;
3076 if (isempty(rvalue
)) {
3077 /* Empty assignment resets the list */
3078 *rt
= strv_free(*rt
);
3082 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3083 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3085 t
= strndup(word
, l
);
3089 r
= unit_name_printf(u
, t
, &n
);
3091 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
3095 if (!filename_is_valid(n
)) {
3096 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3100 r
= strv_push(rt
, n
);
3106 if (!isempty(state
))
3107 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3112 int config_parse_set_status(
3114 const char *filename
,
3116 const char *section
,
3117 unsigned section_line
,
3125 const char *word
, *state
;
3127 ExitStatusSet
*status_set
= data
;
3134 /* Empty assignment resets the list */
3135 if (isempty(rvalue
)) {
3136 exit_status_set_free(status_set
);
3140 FOREACH_WORD(word
, l
, rvalue
, state
) {
3141 _cleanup_free_
char *temp
;
3145 temp
= strndup(word
, l
);
3149 r
= safe_atoi(temp
, &val
);
3151 val
= signal_from_string_try_harder(temp
);
3154 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3157 set
= &status_set
->signal
;
3159 if (val
< 0 || val
> 255) {
3160 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3163 set
= &status_set
->status
;
3166 r
= set_ensure_allocated(set
, NULL
);
3170 r
= set_put(*set
, INT_TO_PTR(val
));
3172 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3176 if (!isempty(state
))
3177 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3182 int config_parse_namespace_path_strv(
3184 const char *filename
,
3186 const char *section
,
3187 unsigned section_line
,
3195 const char *word
, *state
;
3204 if (isempty(rvalue
)) {
3205 /* Empty assignment resets the list */
3206 *sv
= strv_free(*sv
);
3210 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3211 _cleanup_free_
char *n
;
3214 n
= strndup(word
, l
);
3218 if (!utf8_is_valid(n
)) {
3219 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
3223 offset
= n
[0] == '-';
3224 if (!path_is_absolute(n
+ offset
)) {
3225 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", rvalue
);
3229 path_kill_slashes(n
);
3231 r
= strv_push(sv
, n
);
3237 if (!isempty(state
))
3238 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3243 int config_parse_no_new_privileges(
3245 const char *filename
,
3247 const char *section
,
3248 unsigned section_line
,
3255 ExecContext
*c
= data
;
3263 k
= parse_boolean(rvalue
);
3265 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3269 c
->no_new_privileges
= !!k
;
3270 c
->no_new_privileges_set
= true;
3275 int config_parse_protect_home(
3277 const char *filename
,
3279 const char *section
,
3280 unsigned section_line
,
3287 ExecContext
*c
= data
;
3295 /* Our enum shall be a superset of booleans, hence first try
3296 * to parse as as boolean, and then as enum */
3298 k
= parse_boolean(rvalue
);
3300 c
->protect_home
= PROTECT_HOME_YES
;
3302 c
->protect_home
= PROTECT_HOME_NO
;
3306 h
= protect_home_from_string(rvalue
);
3308 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
3312 c
->protect_home
= h
;
3318 int config_parse_protect_system(
3320 const char *filename
,
3322 const char *section
,
3323 unsigned section_line
,
3330 ExecContext
*c
= data
;
3338 /* Our enum shall be a superset of booleans, hence first try
3339 * to parse as as boolean, and then as enum */
3341 k
= parse_boolean(rvalue
);
3343 c
->protect_system
= PROTECT_SYSTEM_YES
;
3345 c
->protect_system
= PROTECT_SYSTEM_NO
;
3349 s
= protect_system_from_string(rvalue
);
3351 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
3355 c
->protect_system
= s
;
3361 #define FOLLOW_MAX 8
3363 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3374 /* This will update the filename pointer if the loaded file is
3375 * reached by a symlink. The old string will be freed. */
3378 char *target
, *name
;
3380 if (c
++ >= FOLLOW_MAX
)
3383 path_kill_slashes(*filename
);
3385 /* Add the file name we are currently looking at to
3386 * the names of this unit, but only if it is a valid
3388 name
= basename(*filename
);
3390 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3392 id
= set_get(names
, name
);
3398 r
= set_consume(names
, id
);
3404 /* Try to open the file name, but don't if its a symlink */
3405 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3412 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3413 r
= readlink_and_make_absolute(*filename
, &target
);
3421 f
= fdopen(fd
, "re");
3432 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3440 /* Let's try to add in all symlink names we found */
3441 while ((k
= set_steal_first(names
))) {
3443 /* First try to merge in the other name into our
3445 r
= unit_merge_by_name(*u
, k
);
3449 /* Hmm, we couldn't merge the other unit into
3450 * ours? Then let's try it the other way
3453 other
= manager_get_unit((*u
)->manager
, k
);
3457 r
= unit_merge(other
, *u
);
3460 return merge_by_names(u
, names
, NULL
);
3468 unit_choose_id(*u
, id
);
3476 static int load_from_path(Unit
*u
, const char *path
) {
3478 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3479 _cleanup_fclose_
FILE *f
= NULL
;
3480 _cleanup_free_
char *filename
= NULL
;
3488 symlink_names
= set_new(&string_hash_ops
);
3492 if (path_is_absolute(path
)) {
3494 filename
= strdup(path
);
3498 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3500 filename
= mfree(filename
);
3508 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3510 /* Instead of opening the path right away, we manually
3511 * follow all symlinks and add their name to our unit
3512 * name set while doing so */
3513 filename
= path_make_absolute(path
, *p
);
3517 if (u
->manager
->unit_path_cache
&&
3518 !set_get(u
->manager
->unit_path_cache
, filename
))
3521 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3524 filename
= mfree(filename
);
3528 /* Empty the symlink names for the next run */
3529 set_clear_free(symlink_names
);
3538 /* Hmm, no suitable file found? */
3542 r
= merge_by_names(&merged
, symlink_names
, id
);
3547 u
->load_state
= UNIT_MERGED
;
3551 if (fstat(fileno(f
), &st
) < 0)
3554 if (null_or_empty(&st
))
3555 u
->load_state
= UNIT_MASKED
;
3557 u
->load_state
= UNIT_LOADED
;
3559 /* Now, parse the file contents */
3560 r
= config_parse(u
->id
, filename
, f
,
3561 UNIT_VTABLE(u
)->sections
,
3562 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3563 false, true, false, u
);
3568 free(u
->fragment_path
);
3569 u
->fragment_path
= filename
;
3572 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3574 if (u
->source_path
) {
3575 if (stat(u
->source_path
, &st
) >= 0)
3576 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3578 u
->source_mtime
= 0;
3584 int unit_load_fragment(Unit
*u
) {
3590 assert(u
->load_state
== UNIT_STUB
);
3594 u
->load_state
= UNIT_LOADED
;
3598 /* First, try to find the unit under its id. We always look
3599 * for unit files in the default directories, to make it easy
3600 * to override things by placing things in /etc/systemd/system */
3601 r
= load_from_path(u
, u
->id
);
3605 /* Try to find an alias we can load this with */
3606 if (u
->load_state
== UNIT_STUB
) {
3607 SET_FOREACH(t
, u
->names
, i
) {
3612 r
= load_from_path(u
, t
);
3616 if (u
->load_state
!= UNIT_STUB
)
3621 /* And now, try looking for it under the suggested (originally linked) path */
3622 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3624 r
= load_from_path(u
, u
->fragment_path
);
3628 if (u
->load_state
== UNIT_STUB
)
3629 /* Hmm, this didn't work? Then let's get rid
3630 * of the fragment path stored for us, so that
3631 * we don't point to an invalid location. */
3632 u
->fragment_path
= mfree(u
->fragment_path
);
3635 /* Look for a template */
3636 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3637 _cleanup_free_
char *k
= NULL
;
3639 r
= unit_name_template(u
->id
, &k
);
3643 r
= load_from_path(u
, k
);
3647 if (u
->load_state
== UNIT_STUB
) {
3648 SET_FOREACH(t
, u
->names
, i
) {
3649 _cleanup_free_
char *z
= NULL
;
3654 r
= unit_name_template(t
, &z
);
3658 r
= load_from_path(u
, z
);
3662 if (u
->load_state
!= UNIT_STUB
)
3671 void unit_dump_config_items(FILE *f
) {
3672 static const struct {
3673 const ConfigParserCallback callback
;
3676 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3677 { config_parse_warn_compat
, "NOTSUPPORTED" },
3679 { config_parse_int
, "INTEGER" },
3680 { config_parse_unsigned
, "UNSIGNED" },
3681 { config_parse_iec_size
, "SIZE" },
3682 { config_parse_iec_uint64
, "SIZE" },
3683 { config_parse_si_size
, "SIZE" },
3684 { config_parse_bool
, "BOOLEAN" },
3685 { config_parse_string
, "STRING" },
3686 { config_parse_path
, "PATH" },
3687 { config_parse_unit_path_printf
, "PATH" },
3688 { config_parse_strv
, "STRING [...]" },
3689 { config_parse_exec_nice
, "NICE" },
3690 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3691 { config_parse_exec_io_class
, "IOCLASS" },
3692 { config_parse_exec_io_priority
, "IOPRIORITY" },
3693 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3694 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3695 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3696 { config_parse_mode
, "MODE" },
3697 { config_parse_unit_env_file
, "FILE" },
3698 { config_parse_output
, "OUTPUT" },
3699 { config_parse_input
, "INPUT" },
3700 { config_parse_log_facility
, "FACILITY" },
3701 { config_parse_log_level
, "LEVEL" },
3702 { config_parse_exec_capabilities
, "CAPABILITIES" },
3703 { config_parse_exec_secure_bits
, "SECUREBITS" },
3704 { config_parse_bounding_set
, "BOUNDINGSET" },
3705 { config_parse_limit
, "LIMIT" },
3706 { config_parse_unit_deps
, "UNIT [...]" },
3707 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3708 { config_parse_service_type
, "SERVICETYPE" },
3709 { config_parse_service_restart
, "SERVICERESTART" },
3710 #ifdef HAVE_SYSV_COMPAT
3711 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3713 { config_parse_kill_mode
, "KILLMODE" },
3714 { config_parse_signal
, "SIGNAL" },
3715 { config_parse_socket_listen
, "SOCKET [...]" },
3716 { config_parse_socket_bind
, "SOCKETBIND" },
3717 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3718 { config_parse_sec
, "SECONDS" },
3719 { config_parse_nsec
, "NANOSECONDS" },
3720 { config_parse_namespace_path_strv
, "PATH [...]" },
3721 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3722 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3723 { config_parse_unit_string_printf
, "STRING" },
3724 { config_parse_trigger_unit
, "UNIT" },
3725 { config_parse_timer
, "TIMER" },
3726 { config_parse_path_spec
, "PATH" },
3727 { config_parse_notify_access
, "ACCESS" },
3728 { config_parse_ip_tos
, "TOS" },
3729 { config_parse_unit_condition_path
, "CONDITION" },
3730 { config_parse_unit_condition_string
, "CONDITION" },
3731 { config_parse_unit_condition_null
, "CONDITION" },
3732 { config_parse_unit_slice
, "SLICE" },
3733 { config_parse_documentation
, "URL" },
3734 { config_parse_service_timeout
, "SECONDS" },
3735 { config_parse_failure_action
, "ACTION" },
3736 { config_parse_set_status
, "STATUS" },
3737 { config_parse_service_sockets
, "SOCKETS" },
3738 { config_parse_environ
, "ENVIRON" },
3740 { config_parse_syscall_filter
, "SYSCALLS" },
3741 { config_parse_syscall_archs
, "ARCHS" },
3742 { config_parse_syscall_errno
, "ERRNO" },
3743 { config_parse_address_families
, "FAMILIES" },
3745 { config_parse_cpu_shares
, "SHARES" },
3746 { config_parse_memory_limit
, "LIMIT" },
3747 { config_parse_device_allow
, "DEVICE" },
3748 { config_parse_device_policy
, "POLICY" },
3749 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3750 { config_parse_blockio_weight
, "WEIGHT" },
3751 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3752 { config_parse_long
, "LONG" },
3753 { config_parse_socket_service
, "SERVICE" },
3755 { config_parse_exec_selinux_context
, "LABEL" },
3757 { config_parse_job_mode
, "MODE" },
3758 { config_parse_job_mode_isolate
, "BOOLEAN" },
3759 { config_parse_personality
, "PERSONALITY" },
3762 const char *prev
= NULL
;
3767 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3768 const char *rvalue
= "OTHER", *lvalue
;
3772 const ConfigPerfItem
*p
;
3774 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3776 dot
= strchr(i
, '.');
3777 lvalue
= dot
? dot
+ 1 : i
;
3781 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3785 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3788 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3789 if (p
->parse
== table
[j
].callback
) {
3790 rvalue
= table
[j
].rvalue
;
3794 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);