1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
7 Copyright 2012 Holger Hans Peter Freyther
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <linux/oom.h>
30 #include <sys/resource.h>
38 #include "conf-parser.h"
39 #include "load-fragment.h"
42 #include "securebits.h"
44 #include "unit-name.h"
45 #include "unit-printf.h"
47 #include "path-util.h"
51 #include "bus-error.h"
52 #include "errno-list.h"
55 #include "signal-util.h"
56 #include "bus-internal.h"
59 #include "seccomp-util.h"
62 int config_parse_warn_compat(
67 unsigned section_line
,
73 Disabled reason
= ltype
;
76 case DISABLED_CONFIGURATION
:
77 log_syntax(unit
, LOG_DEBUG
, filename
, line
, EINVAL
,
78 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
81 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
82 "Support for option %s= has been removed and it is ignored", lvalue
);
84 case DISABLED_EXPERIMENTAL
:
85 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
86 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
93 int config_parse_unit_deps(const char *unit
,
97 unsigned section_line
,
104 UnitDependency d
= ltype
;
106 const char *word
, *state
;
113 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
114 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
117 t
= strndup(word
, l
);
121 r
= unit_name_printf(u
, t
, &k
);
123 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
124 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
128 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
130 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
131 "Failed to add dependency on %s, ignoring: %s", k
, strerror(-r
));
134 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
139 int config_parse_unit_string_printf(
141 const char *filename
,
144 unsigned section_line
,
151 _cleanup_free_
char *k
= NULL
;
160 r
= unit_full_printf(u
, rvalue
, &k
);
162 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
166 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
169 int config_parse_unit_strv_printf(const char *unit
,
170 const char *filename
,
173 unsigned section_line
,
181 _cleanup_free_
char *k
= NULL
;
189 r
= unit_full_printf(u
, rvalue
, &k
);
191 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
192 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
194 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
195 k
? k
: rvalue
, data
, userdata
);
198 int config_parse_unit_path_printf(const char *unit
,
199 const char *filename
,
202 unsigned section_line
,
209 _cleanup_free_
char *k
= NULL
;
218 r
= unit_full_printf(u
, rvalue
, &k
);
220 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
224 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
227 int config_parse_unit_path_strv_printf(
229 const char *filename
,
232 unsigned section_line
,
240 const char *word
, *state
;
250 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
251 _cleanup_free_
char *k
= NULL
;
257 r
= unit_full_printf(u
, t
, &k
);
259 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", t
, strerror(-r
));
263 if (!utf8_is_valid(k
)) {
264 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
268 if (!path_is_absolute(k
)) {
269 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Symlink path %s is not absolute, ignoring: %s", k
, strerror(-r
));
273 path_kill_slashes(k
);
282 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
287 int config_parse_socket_listen(const char *unit
,
288 const char *filename
,
291 unsigned section_line
,
298 _cleanup_free_ SocketPort
*p
= NULL
;
310 if (isempty(rvalue
)) {
311 /* An empty assignment removes all ports */
312 socket_free_ports(s
);
316 p
= new0(SocketPort
, 1);
320 if (ltype
!= SOCKET_SOCKET
) {
323 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
325 p
->path
= strdup(rvalue
);
329 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
330 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
333 path_kill_slashes(p
->path
);
335 } else if (streq(lvalue
, "ListenNetlink")) {
336 _cleanup_free_
char *k
= NULL
;
338 p
->type
= SOCKET_SOCKET
;
339 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
341 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
342 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
344 r
= socket_address_parse_netlink(&p
->address
, k
?: rvalue
);
346 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
347 "Failed to parse address value, ignoring: %s", rvalue
);
352 _cleanup_free_
char *k
= NULL
;
354 p
->type
= SOCKET_SOCKET
;
355 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
357 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
358 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
360 r
= socket_address_parse_and_warn(&p
->address
, k
? k
: rvalue
);
362 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
363 "Failed to parse address value, ignoring: %s", rvalue
);
367 if (streq(lvalue
, "ListenStream"))
368 p
->address
.type
= SOCK_STREAM
;
369 else if (streq(lvalue
, "ListenDatagram"))
370 p
->address
.type
= SOCK_DGRAM
;
372 assert(streq(lvalue
, "ListenSequentialPacket"));
373 p
->address
.type
= SOCK_SEQPACKET
;
376 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
377 log_syntax(unit
, LOG_ERR
, filename
, line
, EOPNOTSUPP
,
378 "Address family not supported, ignoring: %s", rvalue
);
384 p
->auxiliary_fds
= NULL
;
385 p
->n_auxiliary_fds
= 0;
389 LIST_FIND_TAIL(port
, s
->ports
, tail
);
390 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
392 LIST_PREPEND(port
, s
->ports
, p
);
398 int config_parse_socket_bind(const char *unit
,
399 const char *filename
,
402 unsigned section_line
,
410 SocketAddressBindIPv6Only b
;
419 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
423 r
= parse_boolean(rvalue
);
425 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
426 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
430 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
432 s
->bind_ipv6_only
= b
;
437 int config_parse_exec_nice(const char *unit
,
438 const char *filename
,
441 unsigned section_line
,
448 ExecContext
*c
= data
;
456 r
= safe_atoi(rvalue
, &priority
);
458 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
459 "Failed to parse nice priority, ignoring: %s. ", rvalue
);
463 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
464 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
465 "Nice priority out of range, ignoring: %s", rvalue
);
475 int config_parse_exec_oom_score_adjust(const char* unit
,
476 const char *filename
,
479 unsigned section_line
,
486 ExecContext
*c
= data
;
494 r
= safe_atoi(rvalue
, &oa
);
496 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
497 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
501 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
502 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
503 "OOM score adjust value out of range, ignoring: %s", rvalue
);
507 c
->oom_score_adjust
= oa
;
508 c
->oom_score_adjust_set
= true;
513 int config_parse_exec(
515 const char *filename
,
518 unsigned section_line
,
525 ExecCommand
**e
= data
;
537 rvalue
+= strspn(rvalue
, WHITESPACE
);
540 if (isempty(rvalue
)) {
541 /* An empty assignment resets the list */
542 *e
= exec_command_free_list(*e
);
548 _cleanup_strv_free_
char **n
= NULL
;
549 size_t nlen
= 0, nbufsize
= 0;
550 _cleanup_free_ ExecCommand
*nce
= NULL
;
551 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
553 bool separate_argv0
= false, ignore
= false;
557 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
562 for (i
= 0; i
< 2; i
++) {
563 /* We accept an absolute path as first argument, or
564 * alternatively an absolute prefixed with @ to allow
565 * overriding of argv[0]. */
566 if (*f
== '-' && !ignore
)
568 else if (*f
== '@' && !separate_argv0
)
569 separate_argv0
= true;
576 /* First word is either "-" or "@" with no command. */
577 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
578 "Empty path in command line, ignoring: \"%s\"", rvalue
);
582 if (!string_is_safe(f
)) {
583 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
584 "Executable path contains special characters, ignoring: %s", rvalue
);
587 if (!path_is_absolute(f
)) {
588 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
589 "Executable path is not absolute, ignoring: %s", rvalue
);
592 if (endswith(f
, "/")) {
593 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
594 "Executable path specifies a directory, ignoring: %s", rvalue
);
598 if (f
== firstword
) {
607 if (!separate_argv0
) {
608 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
617 path_kill_slashes(path
);
619 while (!isempty(p
)) {
620 _cleanup_free_
char *word
= NULL
;
622 /* Check explicitly for an unquoted semicolon as
623 * command separator token. */
624 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
626 p
+= strspn(p
, WHITESPACE
);
631 /* Check for \; explicitly, to not confuse it with \\;
632 * or "\;" or "\\;" etc. extract_first_word would
633 * return the same for all of those. */
634 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
636 p
+= strspn(p
, WHITESPACE
);
637 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
647 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
653 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
661 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
662 "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
666 nce
= new0(ExecCommand
, 1);
672 nce
->ignore
= ignore
;
674 exec_command_append_list(e
, nce
);
676 /* Do not _cleanup_free_ these. */
687 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
688 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
690 int config_parse_socket_bindtodevice(const char* unit
,
691 const char *filename
,
694 unsigned section_line
,
709 if (rvalue
[0] && !streq(rvalue
, "*")) {
716 free(s
->bind_to_device
);
717 s
->bind_to_device
= n
;
722 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
723 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
725 int config_parse_exec_io_class(const char *unit
,
726 const char *filename
,
729 unsigned section_line
,
736 ExecContext
*c
= data
;
744 x
= ioprio_class_from_string(rvalue
);
746 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
747 "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
751 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
752 c
->ioprio_set
= true;
757 int config_parse_exec_io_priority(const char *unit
,
758 const char *filename
,
761 unsigned section_line
,
768 ExecContext
*c
= data
;
776 r
= safe_atoi(rvalue
, &i
);
777 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
778 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
779 "Failed to parse IO priority, ignoring: %s", rvalue
);
783 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
784 c
->ioprio_set
= true;
789 int config_parse_exec_cpu_sched_policy(const char *unit
,
790 const char *filename
,
793 unsigned section_line
,
801 ExecContext
*c
= data
;
809 x
= sched_policy_from_string(rvalue
);
811 log_syntax(unit
, LOG_ERR
, filename
, line
, -x
,
812 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
816 c
->cpu_sched_policy
= x
;
817 /* Moving to or from real-time policy? We need to adjust the priority */
818 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
819 c
->cpu_sched_set
= true;
824 int config_parse_exec_cpu_sched_prio(const char *unit
,
825 const char *filename
,
828 unsigned section_line
,
835 ExecContext
*c
= data
;
843 r
= safe_atoi(rvalue
, &i
);
845 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
846 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
850 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
851 min
= sched_get_priority_min(c
->cpu_sched_policy
);
852 max
= sched_get_priority_max(c
->cpu_sched_policy
);
854 if (i
< min
|| i
> max
) {
855 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
856 "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
860 c
->cpu_sched_priority
= i
;
861 c
->cpu_sched_set
= true;
866 int config_parse_exec_cpu_affinity(const char *unit
,
867 const char *filename
,
870 unsigned section_line
,
877 ExecContext
*c
= data
;
878 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
886 ncpus
= parse_cpu_set(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
895 /* An empty assignment resets the CPU list */
901 c
->cpuset_ncpus
= ncpus
;
906 int config_parse_exec_capabilities(const char *unit
,
907 const char *filename
,
910 unsigned section_line
,
917 ExecContext
*c
= data
;
925 cap
= cap_from_text(rvalue
);
927 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
,
928 "Failed to parse capabilities, ignoring: %s", rvalue
);
933 cap_free(c
->capabilities
);
934 c
->capabilities
= cap
;
939 int config_parse_exec_secure_bits(const char *unit
,
940 const char *filename
,
943 unsigned section_line
,
950 ExecContext
*c
= data
;
952 const char *word
, *state
;
959 if (isempty(rvalue
)) {
960 /* An empty assignment resets the field */
965 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
966 if (first_word(word
, "keep-caps"))
967 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
968 else if (first_word(word
, "keep-caps-locked"))
969 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
970 else if (first_word(word
, "no-setuid-fixup"))
971 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
972 else if (first_word(word
, "no-setuid-fixup-locked"))
973 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
974 else if (first_word(word
, "noroot"))
975 c
->secure_bits
|= 1<<SECURE_NOROOT
;
976 else if (first_word(word
, "noroot-locked"))
977 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
979 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
980 "Failed to parse secure bits, ignoring: %s", rvalue
);
985 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
986 "Invalid syntax, garbage at the end, ignoring.");
991 int config_parse_bounding_set(const char *unit
,
992 const char *filename
,
995 unsigned section_line
,
1002 uint64_t *capability_bounding_set_drop
= data
;
1003 const char *word
, *state
;
1005 bool invert
= false;
1013 if (rvalue
[0] == '~') {
1018 /* Note that we store this inverted internally, since the
1019 * kernel wants it like this. But we actually expose it
1020 * non-inverted everywhere to have a fully normalized
1023 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1024 _cleanup_free_
char *t
= NULL
;
1027 t
= strndup(word
, l
);
1031 cap
= capability_from_name(t
);
1033 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse capability in bounding set, ignoring: %s", t
);
1037 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1039 if (!isempty(state
))
1040 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1041 "Trailing garbage, ignoring.");
1044 *capability_bounding_set_drop
|= sum
;
1046 *capability_bounding_set_drop
|= ~sum
;
1051 int config_parse_limit(const char *unit
,
1052 const char *filename
,
1054 const char *section
,
1055 unsigned section_line
,
1062 struct rlimit
**rl
= data
;
1063 unsigned long long u
;
1072 if (streq(rvalue
, "infinity"))
1073 u
= (unsigned long long) RLIM_INFINITY
;
1077 r
= safe_atollu(rvalue
, &u
);
1079 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1080 "Failed to parse resource value, ignoring: %s", rvalue
);
1086 *rl
= new(struct rlimit
, 1);
1091 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1095 #ifdef HAVE_SYSV_COMPAT
1096 int config_parse_sysv_priority(const char *unit
,
1097 const char *filename
,
1099 const char *section
,
1100 unsigned section_line
,
1107 int *priority
= data
;
1115 r
= safe_atoi(rvalue
, &i
);
1116 if (r
< 0 || i
< 0) {
1117 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1118 "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1122 *priority
= (int) i
;
1127 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1128 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1130 int config_parse_exec_mount_flags(const char *unit
,
1131 const char *filename
,
1133 const char *section
,
1134 unsigned section_line
,
1141 ExecContext
*c
= data
;
1142 const char *word
, *state
;
1144 unsigned long flags
= 0;
1151 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1152 _cleanup_free_
char *t
;
1154 t
= strndup(word
, l
);
1158 if (streq(t
, "shared"))
1160 else if (streq(t
, "slave"))
1162 else if (streq(t
, "private"))
1165 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1169 if (!isempty(state
))
1170 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1172 c
->mount_flags
= flags
;
1176 int config_parse_exec_selinux_context(
1178 const char *filename
,
1180 const char *section
,
1181 unsigned section_line
,
1188 ExecContext
*c
= data
;
1199 if (isempty(rvalue
)) {
1200 c
->selinux_context
= mfree(c
->selinux_context
);
1201 c
->selinux_context_ignore
= false;
1205 if (rvalue
[0] == '-') {
1211 r
= unit_name_printf(u
, rvalue
, &k
);
1213 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1214 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1218 free(c
->selinux_context
);
1219 c
->selinux_context
= k
;
1220 c
->selinux_context_ignore
= ignore
;
1225 int config_parse_exec_apparmor_profile(
1227 const char *filename
,
1229 const char *section
,
1230 unsigned section_line
,
1237 ExecContext
*c
= data
;
1248 if (isempty(rvalue
)) {
1249 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1250 c
->apparmor_profile_ignore
= false;
1254 if (rvalue
[0] == '-') {
1260 r
= unit_name_printf(u
, rvalue
, &k
);
1262 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1263 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1267 free(c
->apparmor_profile
);
1268 c
->apparmor_profile
= k
;
1269 c
->apparmor_profile_ignore
= ignore
;
1274 int config_parse_exec_smack_process_label(
1276 const char *filename
,
1278 const char *section
,
1279 unsigned section_line
,
1286 ExecContext
*c
= data
;
1297 if (isempty(rvalue
)) {
1298 c
->smack_process_label
= mfree(c
->smack_process_label
);
1299 c
->smack_process_label_ignore
= false;
1303 if (rvalue
[0] == '-') {
1309 r
= unit_name_printf(u
, rvalue
, &k
);
1311 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1312 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1316 free(c
->smack_process_label
);
1317 c
->smack_process_label
= k
;
1318 c
->smack_process_label_ignore
= ignore
;
1323 int config_parse_timer(const char *unit
,
1324 const char *filename
,
1326 const char *section
,
1327 unsigned section_line
,
1338 CalendarSpec
*c
= NULL
;
1345 if (isempty(rvalue
)) {
1346 /* Empty assignment resets list */
1347 timer_free_values(t
);
1351 b
= timer_base_from_string(lvalue
);
1353 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
,
1354 "Failed to parse timer base, ignoring: %s", lvalue
);
1358 if (b
== TIMER_CALENDAR
) {
1359 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1360 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1361 "Failed to parse calendar specification, ignoring: %s",
1366 if (parse_sec(rvalue
, &u
) < 0) {
1367 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1368 "Failed to parse timer value, ignoring: %s",
1374 v
= new0(TimerValue
, 1);
1376 calendar_spec_free(c
);
1382 v
->calendar_spec
= c
;
1384 LIST_PREPEND(value
, t
->values
, v
);
1389 int config_parse_trigger_unit(
1391 const char *filename
,
1393 const char *section
,
1394 unsigned section_line
,
1401 _cleanup_free_
char *p
= NULL
;
1411 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1412 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1413 "Multiple units to trigger specified, ignoring: %s", rvalue
);
1417 r
= unit_name_printf(u
, rvalue
, &p
);
1419 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1420 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1422 type
= unit_name_to_type(p
?: rvalue
);
1424 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1425 "Unit type not valid, ignoring: %s", rvalue
);
1429 if (type
== u
->type
) {
1430 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1431 "Trigger cannot be of same type, ignoring: %s", rvalue
);
1435 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
?: rvalue
, NULL
, true);
1437 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1438 "Failed to add trigger on %s, ignoring: %s", p
?: rvalue
, strerror(-r
));
1445 int config_parse_path_spec(const char *unit
,
1446 const char *filename
,
1448 const char *section
,
1449 unsigned section_line
,
1459 _cleanup_free_
char *k
= NULL
;
1467 if (isempty(rvalue
)) {
1468 /* Empty assignment clears list */
1473 b
= path_type_from_string(lvalue
);
1475 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1476 "Failed to parse path type, ignoring: %s", lvalue
);
1480 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1486 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1487 "Failed to resolve unit specifiers on %s. Ignoring.",
1491 if (!path_is_absolute(k
)) {
1492 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1493 "Path is not absolute, ignoring: %s", k
);
1497 s
= new0(PathSpec
, 1);
1502 s
->path
= path_kill_slashes(k
);
1507 LIST_PREPEND(spec
, p
->specs
, s
);
1512 int config_parse_socket_service(
1514 const char *filename
,
1516 const char *section
,
1517 unsigned section_line
,
1524 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1528 _cleanup_free_
char *p
= NULL
;
1535 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1537 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1541 if (!endswith(p
, ".service")) {
1542 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type service, ignoring: %s", rvalue
);
1546 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1548 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1552 unit_ref_set(&s
->service
, x
);
1557 int config_parse_service_sockets(
1559 const char *filename
,
1561 const char *section
,
1562 unsigned section_line
,
1570 const char *word
, *state
;
1579 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1580 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1582 t
= strndup(word
, l
);
1586 r
= unit_name_printf(UNIT(s
), t
, &k
);
1588 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1592 if (!endswith(k
, ".socket")) {
1593 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type socket, ignoring: %s", k
);
1597 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1599 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1601 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1603 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1605 if (!isempty(state
))
1606 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1611 int config_parse_bus_name(
1613 const char *filename
,
1615 const char *section
,
1616 unsigned section_line
,
1623 _cleanup_free_
char *k
= NULL
;
1632 r
= unit_full_printf(u
, rvalue
, &k
);
1634 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1638 if (!service_name_is_valid(k
)) {
1639 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bus name %s, ignoring.", k
);
1643 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1646 int config_parse_service_timeout(const char *unit
,
1647 const char *filename
,
1649 const char *section
,
1650 unsigned section_line
,
1657 Service
*s
= userdata
;
1665 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1666 rvalue
, data
, userdata
);
1670 if (streq(lvalue
, "TimeoutSec")) {
1671 s
->start_timeout_defined
= true;
1672 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1673 } else if (streq(lvalue
, "TimeoutStartSec"))
1674 s
->start_timeout_defined
= true;
1679 int config_parse_busname_service(
1681 const char *filename
,
1683 const char *section
,
1684 unsigned section_line
,
1691 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1695 _cleanup_free_
char *p
= NULL
;
1702 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1704 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1705 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1709 if (!endswith(p
, ".service")) {
1710 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1711 "Unit must be of type service, ignoring: %s", rvalue
);
1715 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1717 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1718 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1722 unit_ref_set(&n
->service
, x
);
1727 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1729 int config_parse_bus_policy(
1731 const char *filename
,
1733 const char *section
,
1734 unsigned section_line
,
1741 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1742 _cleanup_free_
char *id_str
= NULL
;
1743 BusName
*busname
= data
;
1751 p
= new0(BusNamePolicy
, 1);
1755 if (streq(lvalue
, "AllowUser"))
1756 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1757 else if (streq(lvalue
, "AllowGroup"))
1758 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1760 assert_not_reached("Unknown lvalue");
1762 id_str
= strdup(rvalue
);
1766 access_str
= strpbrk(id_str
, WHITESPACE
);
1768 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1769 "Invalid busname policy value '%s'", rvalue
);
1775 access_str
+= strspn(access_str
, WHITESPACE
);
1777 p
->access
= bus_policy_access_from_string(access_str
);
1778 if (p
->access
< 0) {
1779 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1780 "Invalid busname policy access type '%s'", access_str
);
1787 LIST_PREPEND(policy
, busname
->policy
, p
);
1793 int config_parse_bus_endpoint_policy(
1795 const char *filename
,
1797 const char *section
,
1798 unsigned section_line
,
1805 _cleanup_free_
char *name
= NULL
;
1806 BusPolicyAccess access
;
1807 ExecContext
*c
= data
;
1816 name
= strdup(rvalue
);
1820 access_str
= strpbrk(name
, WHITESPACE
);
1822 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1823 "Invalid endpoint policy value '%s'", rvalue
);
1829 access_str
+= strspn(access_str
, WHITESPACE
);
1831 access
= bus_policy_access_from_string(access_str
);
1832 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1833 access
>= _BUS_POLICY_ACCESS_MAX
) {
1834 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1835 "Invalid endpoint policy access type '%s'", access_str
);
1839 if (!c
->bus_endpoint
) {
1840 r
= bus_endpoint_new(&c
->bus_endpoint
);
1846 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1849 int config_parse_unit_env_file(const char *unit
,
1850 const char *filename
,
1852 const char *section
,
1853 unsigned section_line
,
1862 _cleanup_free_
char *n
= NULL
;
1871 if (isempty(rvalue
)) {
1872 /* Empty assignment frees the list */
1873 *env
= strv_free(*env
);
1877 r
= unit_full_printf(u
, rvalue
, &n
);
1879 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1880 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1883 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1884 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1885 "Path '%s' is not absolute, ignoring.", s
);
1889 r
= strv_extend(env
, s
);
1896 int config_parse_environ(const char *unit
,
1897 const char *filename
,
1899 const char *section
,
1900 unsigned section_line
,
1909 const char *word
, *state
;
1911 _cleanup_free_
char *k
= NULL
;
1919 if (isempty(rvalue
)) {
1920 /* Empty assignment resets the list */
1921 *env
= strv_free(*env
);
1926 r
= unit_full_printf(u
, rvalue
, &k
);
1928 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1936 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1937 _cleanup_free_
char *n
= NULL
;
1940 r
= cunescape_length(word
, l
, 0, &n
);
1942 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
1946 if (!env_assignment_is_valid(n
)) {
1947 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid environment assignment, ignoring: %s", rvalue
);
1951 x
= strv_env_set(*env
, n
);
1958 if (!isempty(state
))
1959 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1960 "Trailing garbage, ignoring.");
1965 int config_parse_ip_tos(const char *unit
,
1966 const char *filename
,
1968 const char *section
,
1969 unsigned section_line
,
1976 int *ip_tos
= data
, x
;
1983 x
= ip_tos_from_string(rvalue
);
1985 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1986 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
1994 int config_parse_unit_condition_path(
1996 const char *filename
,
1998 const char *section
,
1999 unsigned section_line
,
2006 _cleanup_free_
char *p
= NULL
;
2007 Condition
**list
= data
, *c
;
2008 ConditionType t
= ltype
;
2009 bool trigger
, negate
;
2018 if (isempty(rvalue
)) {
2019 /* Empty assignment resets the list */
2020 *list
= condition_free_list(*list
);
2024 trigger
= rvalue
[0] == '|';
2028 negate
= rvalue
[0] == '!';
2032 r
= unit_full_printf(u
, rvalue
, &p
);
2034 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2038 if (!path_is_absolute(p
)) {
2039 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2043 c
= condition_new(t
, p
, trigger
, negate
);
2047 LIST_PREPEND(conditions
, *list
, c
);
2051 int config_parse_unit_condition_string(
2053 const char *filename
,
2055 const char *section
,
2056 unsigned section_line
,
2063 _cleanup_free_
char *s
= NULL
;
2064 Condition
**list
= data
, *c
;
2065 ConditionType t
= ltype
;
2066 bool trigger
, negate
;
2075 if (isempty(rvalue
)) {
2076 /* Empty assignment resets the list */
2077 *list
= condition_free_list(*list
);
2081 trigger
= rvalue
[0] == '|';
2085 negate
= rvalue
[0] == '!';
2089 r
= unit_full_printf(u
, rvalue
, &s
);
2091 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2095 c
= condition_new(t
, s
, trigger
, negate
);
2099 LIST_PREPEND(conditions
, *list
, c
);
2103 int config_parse_unit_condition_null(
2105 const char *filename
,
2107 const char *section
,
2108 unsigned section_line
,
2115 Condition
**list
= data
, *c
;
2116 bool trigger
, negate
;
2124 if (isempty(rvalue
)) {
2125 /* Empty assignment resets the list */
2126 *list
= condition_free_list(*list
);
2130 trigger
= rvalue
[0] == '|';
2134 negate
= rvalue
[0] == '!';
2138 b
= parse_boolean(rvalue
);
2140 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2147 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2151 LIST_PREPEND(conditions
, *list
, c
);
2155 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2156 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2158 int config_parse_unit_requires_mounts_for(
2160 const char *filename
,
2162 const char *section
,
2163 unsigned section_line
,
2171 const char *word
, *state
;
2179 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2181 _cleanup_free_
char *n
;
2183 n
= strndup(word
, l
);
2187 if (!utf8_is_valid(n
)) {
2188 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2192 r
= unit_require_mounts_for(u
, n
);
2194 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2195 "Failed to add required mount for, ignoring: %s", rvalue
);
2199 if (!isempty(state
))
2200 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2201 "Trailing garbage, ignoring.");
2206 int config_parse_documentation(const char *unit
,
2207 const char *filename
,
2209 const char *section
,
2210 unsigned section_line
,
2226 if (isempty(rvalue
)) {
2227 /* Empty assignment resets the list */
2228 u
->documentation
= strv_free(u
->documentation
);
2232 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2233 rvalue
, data
, userdata
);
2237 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2239 if (documentation_url_is_valid(*a
))
2242 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2243 "Invalid URL, ignoring: %s", *a
);
2254 int config_parse_syscall_filter(
2256 const char *filename
,
2258 const char *section
,
2259 unsigned section_line
,
2266 static const char default_syscalls
[] =
2273 ExecContext
*c
= data
;
2275 bool invert
= false;
2276 const char *word
, *state
;
2285 if (isempty(rvalue
)) {
2286 /* Empty assignment resets the list */
2287 c
->syscall_filter
= set_free(c
->syscall_filter
);
2288 c
->syscall_whitelist
= false;
2292 if (rvalue
[0] == '~') {
2297 if (!c
->syscall_filter
) {
2298 c
->syscall_filter
= set_new(NULL
);
2299 if (!c
->syscall_filter
)
2303 /* Allow everything but the ones listed */
2304 c
->syscall_whitelist
= false;
2308 /* Allow nothing but the ones listed */
2309 c
->syscall_whitelist
= true;
2311 /* Accept default syscalls if we are on a whitelist */
2312 NULSTR_FOREACH(i
, default_syscalls
) {
2315 id
= seccomp_syscall_resolve_name(i
);
2319 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2328 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2329 _cleanup_free_
char *t
= NULL
;
2332 t
= strndup(word
, l
);
2336 id
= seccomp_syscall_resolve_name(t
);
2338 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2339 "Failed to parse system call, ignoring: %s", t
);
2343 /* If we previously wanted to forbid a syscall and now
2344 * we want to allow it, then remove it from the list
2346 if (!invert
== c
->syscall_whitelist
) {
2347 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2353 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2355 if (!isempty(state
))
2356 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2357 "Trailing garbage, ignoring.");
2359 /* Turn on NNP, but only if it wasn't configured explicitly
2360 * before, and only if we are in user mode. */
2361 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2362 c
->no_new_privileges
= true;
2367 int config_parse_syscall_archs(
2369 const char *filename
,
2371 const char *section
,
2372 unsigned section_line
,
2380 const char *word
, *state
;
2384 if (isempty(rvalue
)) {
2385 *archs
= set_free(*archs
);
2389 r
= set_ensure_allocated(archs
, NULL
);
2393 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2394 _cleanup_free_
char *t
= NULL
;
2397 t
= strndup(word
, l
);
2401 r
= seccomp_arch_from_string(t
, &a
);
2403 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2404 "Failed to parse system call architecture, ignoring: %s", t
);
2408 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2414 if (!isempty(state
))
2415 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2416 "Trailing garbage, ignoring.");
2421 int config_parse_syscall_errno(
2423 const char *filename
,
2425 const char *section
,
2426 unsigned section_line
,
2433 ExecContext
*c
= data
;
2440 if (isempty(rvalue
)) {
2441 /* Empty assignment resets to KILL */
2442 c
->syscall_errno
= 0;
2446 e
= errno_from_name(rvalue
);
2448 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2449 "Failed to parse error number, ignoring: %s", rvalue
);
2453 c
->syscall_errno
= e
;
2457 int config_parse_address_families(
2459 const char *filename
,
2461 const char *section
,
2462 unsigned section_line
,
2469 ExecContext
*c
= data
;
2470 bool invert
= false;
2471 const char *word
, *state
;
2479 if (isempty(rvalue
)) {
2480 /* Empty assignment resets the list */
2481 c
->address_families
= set_free(c
->address_families
);
2482 c
->address_families_whitelist
= false;
2486 if (rvalue
[0] == '~') {
2491 if (!c
->address_families
) {
2492 c
->address_families
= set_new(NULL
);
2493 if (!c
->address_families
)
2496 c
->address_families_whitelist
= !invert
;
2499 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2500 _cleanup_free_
char *t
= NULL
;
2503 t
= strndup(word
, l
);
2507 af
= af_from_name(t
);
2509 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2510 "Failed to parse address family, ignoring: %s", t
);
2514 /* If we previously wanted to forbid an address family and now
2515 * we want to allow it, then remove it from the list
2517 if (!invert
== c
->address_families_whitelist
) {
2518 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2524 set_remove(c
->address_families
, INT_TO_PTR(af
));
2526 if (!isempty(state
))
2527 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2528 "Trailing garbage, ignoring.");
2534 int config_parse_unit_slice(
2536 const char *filename
,
2538 const char *section
,
2539 unsigned section_line
,
2546 _cleanup_free_
char *k
= NULL
;
2547 Unit
*u
= userdata
, *slice
= NULL
;
2555 r
= unit_name_printf(u
, rvalue
, &k
);
2557 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2561 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2563 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2567 r
= unit_set_slice(u
, slice
);
2569 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2576 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2578 int config_parse_cpu_shares(
2580 const char *filename
,
2582 const char *section
,
2583 unsigned section_line
,
2590 uint64_t *shares
= data
;
2597 r
= cg_cpu_shares_parse(rvalue
, shares
);
2599 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2606 int config_parse_cpu_quota(
2608 const char *filename
,
2610 const char *section
,
2611 unsigned section_line
,
2618 CGroupContext
*c
= data
;
2625 if (isempty(rvalue
)) {
2626 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2630 if (!endswith(rvalue
, "%")) {
2632 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2633 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2637 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2638 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2639 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2643 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2648 int config_parse_memory_limit(
2650 const char *filename
,
2652 const char *section
,
2653 unsigned section_line
,
2660 CGroupContext
*c
= data
;
2664 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2665 c
->memory_limit
= (uint64_t) -1;
2669 r
= parse_size(rvalue
, 1024, &bytes
);
2670 if (r
< 0 || bytes
< 1) {
2671 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2675 c
->memory_limit
= bytes
;
2679 int config_parse_tasks_max(
2681 const char *filename
,
2683 const char *section
,
2684 unsigned section_line
,
2691 CGroupContext
*c
= data
;
2695 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2696 c
->tasks_max
= (uint64_t) -1;
2700 r
= safe_atou64(rvalue
, &u
);
2701 if (r
< 0 || u
< 1) {
2702 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2709 int config_parse_device_allow(
2711 const char *filename
,
2713 const char *section
,
2714 unsigned section_line
,
2721 _cleanup_free_
char *path
= NULL
;
2722 CGroupContext
*c
= data
;
2723 CGroupDeviceAllow
*a
;
2727 if (isempty(rvalue
)) {
2728 while (c
->device_allow
)
2729 cgroup_context_free_device_allow(c
, c
->device_allow
);
2734 n
= strcspn(rvalue
, WHITESPACE
);
2735 path
= strndup(rvalue
, n
);
2739 if (!startswith(path
, "/dev/") &&
2740 !startswith(path
, "block-") &&
2741 !startswith(path
, "char-")) {
2742 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2743 "Invalid device node path '%s'. Ignoring.", path
);
2747 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2751 if (!in_charset(m
, "rwm")) {
2752 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2753 "Invalid device rights '%s'. Ignoring.", m
);
2757 a
= new0(CGroupDeviceAllow
, 1);
2763 a
->r
= !!strchr(m
, 'r');
2764 a
->w
= !!strchr(m
, 'w');
2765 a
->m
= !!strchr(m
, 'm');
2767 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2771 int config_parse_blockio_weight(
2773 const char *filename
,
2775 const char *section
,
2776 unsigned section_line
,
2783 uint64_t *weight
= data
;
2790 r
= cg_blkio_weight_parse(rvalue
, weight
);
2792 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2799 int config_parse_blockio_device_weight(
2801 const char *filename
,
2803 const char *section
,
2804 unsigned section_line
,
2811 _cleanup_free_
char *path
= NULL
;
2812 CGroupBlockIODeviceWeight
*w
;
2813 CGroupContext
*c
= data
;
2823 if (isempty(rvalue
)) {
2824 while (c
->blockio_device_weights
)
2825 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2830 n
= strcspn(rvalue
, WHITESPACE
);
2831 weight
= rvalue
+ n
;
2832 weight
+= strspn(weight
, WHITESPACE
);
2834 if (isempty(weight
)) {
2835 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Expected block device and device weight. Ignoring.");
2839 path
= strndup(rvalue
, n
);
2843 if (!path_startswith(path
, "/dev")) {
2844 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid device node path '%s'. Ignoring.", path
);
2848 r
= cg_blkio_weight_parse(weight
, &u
);
2850 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2854 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2856 w
= new0(CGroupBlockIODeviceWeight
, 1);
2865 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2869 int config_parse_blockio_bandwidth(
2871 const char *filename
,
2873 const char *section
,
2874 unsigned section_line
,
2881 _cleanup_free_
char *path
= NULL
;
2882 CGroupBlockIODeviceBandwidth
*b
;
2883 CGroupContext
*c
= data
;
2884 const char *bandwidth
;
2894 read
= streq("BlockIOReadBandwidth", lvalue
);
2896 if (isempty(rvalue
)) {
2897 CGroupBlockIODeviceBandwidth
*next
;
2899 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2900 if (b
->read
== read
)
2901 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2906 n
= strcspn(rvalue
, WHITESPACE
);
2907 bandwidth
= rvalue
+ n
;
2908 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2911 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2912 "Expected space separated pair of device node and bandwidth. Ignoring.");
2916 path
= strndup(rvalue
, n
);
2920 if (!path_startswith(path
, "/dev")) {
2921 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2922 "Invalid device node path '%s'. Ignoring.", path
);
2926 r
= parse_size(bandwidth
, 1000, &bytes
);
2927 if (r
< 0 || bytes
<= 0) {
2928 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2929 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2933 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2939 b
->bandwidth
= bytes
;
2942 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2947 int config_parse_netclass(
2949 const char *filename
,
2951 const char *section
,
2952 unsigned section_line
,
2959 CGroupContext
*c
= data
;
2967 if (streq(rvalue
, "auto")) {
2968 c
->netclass_type
= CGROUP_NETCLASS_TYPE_AUTO
;
2972 r
= safe_atou32(rvalue
, &v
);
2974 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2975 "Netclass '%s' invalid. Ignoring.", rvalue
);
2979 if (v
> CGROUP_NETCLASS_FIXED_MAX
)
2980 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2981 "Fixed netclass %" PRIu32
" out of allowed range (0-%d). Applying anyway.", v
, (uint32_t) CGROUP_NETCLASS_FIXED_MAX
);
2984 c
->netclass_type
= CGROUP_NETCLASS_TYPE_FIXED
;
2989 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
2991 int config_parse_job_mode_isolate(
2993 const char *filename
,
2995 const char *section
,
2996 unsigned section_line
,
3010 r
= parse_boolean(rvalue
);
3012 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3013 "Failed to parse boolean, ignoring: %s", rvalue
);
3017 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3021 int config_parse_runtime_directory(
3023 const char *filename
,
3025 const char *section
,
3026 unsigned section_line
,
3035 const char *word
, *state
;
3044 if (isempty(rvalue
)) {
3045 /* Empty assignment resets the list */
3046 *rt
= strv_free(*rt
);
3050 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3051 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3053 t
= strndup(word
, l
);
3057 r
= unit_name_printf(u
, t
, &n
);
3059 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3060 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
3064 if (!filename_is_valid(n
)) {
3065 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3066 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3070 r
= strv_push(rt
, n
);
3076 if (!isempty(state
))
3077 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3078 "Trailing garbage, ignoring.");
3083 int config_parse_set_status(
3085 const char *filename
,
3087 const char *section
,
3088 unsigned section_line
,
3096 const char *word
, *state
;
3098 ExitStatusSet
*status_set
= data
;
3105 /* Empty assignment resets the list */
3106 if (isempty(rvalue
)) {
3107 exit_status_set_free(status_set
);
3111 FOREACH_WORD(word
, l
, rvalue
, state
) {
3112 _cleanup_free_
char *temp
;
3116 temp
= strndup(word
, l
);
3120 r
= safe_atoi(temp
, &val
);
3122 val
= signal_from_string_try_harder(temp
);
3125 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3126 "Failed to parse value, ignoring: %s", word
);
3129 set
= &status_set
->signal
;
3131 if (val
< 0 || val
> 255) {
3132 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3133 "Value %d is outside range 0-255, ignoring", val
);
3136 set
= &status_set
->status
;
3139 r
= set_ensure_allocated(set
, NULL
);
3143 r
= set_put(*set
, INT_TO_PTR(val
));
3145 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3146 "Unable to store: %s", word
);
3150 if (!isempty(state
))
3151 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3152 "Trailing garbage, ignoring.");
3157 int config_parse_namespace_path_strv(
3159 const char *filename
,
3161 const char *section
,
3162 unsigned section_line
,
3170 const char *word
, *state
;
3179 if (isempty(rvalue
)) {
3180 /* Empty assignment resets the list */
3181 *sv
= strv_free(*sv
);
3185 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3186 _cleanup_free_
char *n
;
3189 n
= strndup(word
, l
);
3193 if (!utf8_is_valid(n
)) {
3194 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3198 offset
= n
[0] == '-';
3199 if (!path_is_absolute(n
+ offset
)) {
3200 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3201 "Not an absolute path, ignoring: %s", rvalue
);
3205 path_kill_slashes(n
);
3207 r
= strv_push(sv
, n
);
3213 if (!isempty(state
))
3214 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3215 "Trailing garbage, ignoring.");
3220 int config_parse_no_new_privileges(
3222 const char *filename
,
3224 const char *section
,
3225 unsigned section_line
,
3232 ExecContext
*c
= data
;
3240 k
= parse_boolean(rvalue
);
3242 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3243 "Failed to parse boolean value, ignoring: %s", rvalue
);
3247 c
->no_new_privileges
= !!k
;
3248 c
->no_new_privileges_set
= true;
3253 int config_parse_protect_home(
3255 const char *filename
,
3257 const char *section
,
3258 unsigned section_line
,
3265 ExecContext
*c
= data
;
3273 /* Our enum shall be a superset of booleans, hence first try
3274 * to parse as as boolean, and then as enum */
3276 k
= parse_boolean(rvalue
);
3278 c
->protect_home
= PROTECT_HOME_YES
;
3280 c
->protect_home
= PROTECT_HOME_NO
;
3284 h
= protect_home_from_string(rvalue
);
3286 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3287 "Failed to parse protect home value, ignoring: %s", rvalue
);
3291 c
->protect_home
= h
;
3297 int config_parse_protect_system(
3299 const char *filename
,
3301 const char *section
,
3302 unsigned section_line
,
3309 ExecContext
*c
= data
;
3317 /* Our enum shall be a superset of booleans, hence first try
3318 * to parse as as boolean, and then as enum */
3320 k
= parse_boolean(rvalue
);
3322 c
->protect_system
= PROTECT_SYSTEM_YES
;
3324 c
->protect_system
= PROTECT_SYSTEM_NO
;
3328 s
= protect_system_from_string(rvalue
);
3330 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3331 "Failed to parse protect system value, ignoring: %s", rvalue
);
3335 c
->protect_system
= s
;
3341 #define FOLLOW_MAX 8
3343 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3354 /* This will update the filename pointer if the loaded file is
3355 * reached by a symlink. The old string will be freed. */
3358 char *target
, *name
;
3360 if (c
++ >= FOLLOW_MAX
)
3363 path_kill_slashes(*filename
);
3365 /* Add the file name we are currently looking at to
3366 * the names of this unit, but only if it is a valid
3368 name
= basename(*filename
);
3370 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3372 id
= set_get(names
, name
);
3378 r
= set_consume(names
, id
);
3384 /* Try to open the file name, but don't if its a symlink */
3385 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3392 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3393 r
= readlink_and_make_absolute(*filename
, &target
);
3401 f
= fdopen(fd
, "re");
3412 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3420 /* Let's try to add in all symlink names we found */
3421 while ((k
= set_steal_first(names
))) {
3423 /* First try to merge in the other name into our
3425 r
= unit_merge_by_name(*u
, k
);
3429 /* Hmm, we couldn't merge the other unit into
3430 * ours? Then let's try it the other way
3433 other
= manager_get_unit((*u
)->manager
, k
);
3437 r
= unit_merge(other
, *u
);
3440 return merge_by_names(u
, names
, NULL
);
3448 unit_choose_id(*u
, id
);
3456 static int load_from_path(Unit
*u
, const char *path
) {
3458 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3459 _cleanup_fclose_
FILE *f
= NULL
;
3460 _cleanup_free_
char *filename
= NULL
;
3468 symlink_names
= set_new(&string_hash_ops
);
3472 if (path_is_absolute(path
)) {
3474 filename
= strdup(path
);
3478 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3480 filename
= mfree(filename
);
3488 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3490 /* Instead of opening the path right away, we manually
3491 * follow all symlinks and add their name to our unit
3492 * name set while doing so */
3493 filename
= path_make_absolute(path
, *p
);
3497 if (u
->manager
->unit_path_cache
&&
3498 !set_get(u
->manager
->unit_path_cache
, filename
))
3501 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3504 filename
= mfree(filename
);
3508 /* Empty the symlink names for the next run */
3509 set_clear_free(symlink_names
);
3518 /* Hmm, no suitable file found? */
3522 r
= merge_by_names(&merged
, symlink_names
, id
);
3527 u
->load_state
= UNIT_MERGED
;
3531 if (fstat(fileno(f
), &st
) < 0)
3534 if (null_or_empty(&st
))
3535 u
->load_state
= UNIT_MASKED
;
3537 u
->load_state
= UNIT_LOADED
;
3539 /* Now, parse the file contents */
3540 r
= config_parse(u
->id
, filename
, f
,
3541 UNIT_VTABLE(u
)->sections
,
3542 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3543 false, true, false, u
);
3548 free(u
->fragment_path
);
3549 u
->fragment_path
= filename
;
3552 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3554 if (u
->source_path
) {
3555 if (stat(u
->source_path
, &st
) >= 0)
3556 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3558 u
->source_mtime
= 0;
3564 int unit_load_fragment(Unit
*u
) {
3570 assert(u
->load_state
== UNIT_STUB
);
3574 u
->load_state
= UNIT_LOADED
;
3578 /* First, try to find the unit under its id. We always look
3579 * for unit files in the default directories, to make it easy
3580 * to override things by placing things in /etc/systemd/system */
3581 r
= load_from_path(u
, u
->id
);
3585 /* Try to find an alias we can load this with */
3586 if (u
->load_state
== UNIT_STUB
) {
3587 SET_FOREACH(t
, u
->names
, i
) {
3592 r
= load_from_path(u
, t
);
3596 if (u
->load_state
!= UNIT_STUB
)
3601 /* And now, try looking for it under the suggested (originally linked) path */
3602 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3604 r
= load_from_path(u
, u
->fragment_path
);
3608 if (u
->load_state
== UNIT_STUB
)
3609 /* Hmm, this didn't work? Then let's get rid
3610 * of the fragment path stored for us, so that
3611 * we don't point to an invalid location. */
3612 u
->fragment_path
= mfree(u
->fragment_path
);
3615 /* Look for a template */
3616 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3617 _cleanup_free_
char *k
= NULL
;
3619 r
= unit_name_template(u
->id
, &k
);
3623 r
= load_from_path(u
, k
);
3627 if (u
->load_state
== UNIT_STUB
) {
3628 SET_FOREACH(t
, u
->names
, i
) {
3629 _cleanup_free_
char *z
= NULL
;
3634 r
= unit_name_template(t
, &z
);
3638 r
= load_from_path(u
, z
);
3642 if (u
->load_state
!= UNIT_STUB
)
3651 void unit_dump_config_items(FILE *f
) {
3652 static const struct {
3653 const ConfigParserCallback callback
;
3656 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3657 { config_parse_warn_compat
, "NOTSUPPORTED" },
3659 { config_parse_int
, "INTEGER" },
3660 { config_parse_unsigned
, "UNSIGNED" },
3661 { config_parse_iec_size
, "SIZE" },
3662 { config_parse_iec_uint64
, "SIZE" },
3663 { config_parse_si_size
, "SIZE" },
3664 { config_parse_bool
, "BOOLEAN" },
3665 { config_parse_string
, "STRING" },
3666 { config_parse_path
, "PATH" },
3667 { config_parse_unit_path_printf
, "PATH" },
3668 { config_parse_strv
, "STRING [...]" },
3669 { config_parse_exec_nice
, "NICE" },
3670 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3671 { config_parse_exec_io_class
, "IOCLASS" },
3672 { config_parse_exec_io_priority
, "IOPRIORITY" },
3673 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3674 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3675 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3676 { config_parse_mode
, "MODE" },
3677 { config_parse_unit_env_file
, "FILE" },
3678 { config_parse_output
, "OUTPUT" },
3679 { config_parse_input
, "INPUT" },
3680 { config_parse_log_facility
, "FACILITY" },
3681 { config_parse_log_level
, "LEVEL" },
3682 { config_parse_exec_capabilities
, "CAPABILITIES" },
3683 { config_parse_exec_secure_bits
, "SECUREBITS" },
3684 { config_parse_bounding_set
, "BOUNDINGSET" },
3685 { config_parse_limit
, "LIMIT" },
3686 { config_parse_unit_deps
, "UNIT [...]" },
3687 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3688 { config_parse_service_type
, "SERVICETYPE" },
3689 { config_parse_service_restart
, "SERVICERESTART" },
3690 #ifdef HAVE_SYSV_COMPAT
3691 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3693 { config_parse_kill_mode
, "KILLMODE" },
3694 { config_parse_signal
, "SIGNAL" },
3695 { config_parse_socket_listen
, "SOCKET [...]" },
3696 { config_parse_socket_bind
, "SOCKETBIND" },
3697 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3698 { config_parse_sec
, "SECONDS" },
3699 { config_parse_nsec
, "NANOSECONDS" },
3700 { config_parse_namespace_path_strv
, "PATH [...]" },
3701 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3702 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3703 { config_parse_unit_string_printf
, "STRING" },
3704 { config_parse_trigger_unit
, "UNIT" },
3705 { config_parse_timer
, "TIMER" },
3706 { config_parse_path_spec
, "PATH" },
3707 { config_parse_notify_access
, "ACCESS" },
3708 { config_parse_ip_tos
, "TOS" },
3709 { config_parse_unit_condition_path
, "CONDITION" },
3710 { config_parse_unit_condition_string
, "CONDITION" },
3711 { config_parse_unit_condition_null
, "CONDITION" },
3712 { config_parse_unit_slice
, "SLICE" },
3713 { config_parse_documentation
, "URL" },
3714 { config_parse_service_timeout
, "SECONDS" },
3715 { config_parse_failure_action
, "ACTION" },
3716 { config_parse_set_status
, "STATUS" },
3717 { config_parse_service_sockets
, "SOCKETS" },
3718 { config_parse_environ
, "ENVIRON" },
3720 { config_parse_syscall_filter
, "SYSCALLS" },
3721 { config_parse_syscall_archs
, "ARCHS" },
3722 { config_parse_syscall_errno
, "ERRNO" },
3723 { config_parse_address_families
, "FAMILIES" },
3725 { config_parse_cpu_shares
, "SHARES" },
3726 { config_parse_memory_limit
, "LIMIT" },
3727 { config_parse_device_allow
, "DEVICE" },
3728 { config_parse_device_policy
, "POLICY" },
3729 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3730 { config_parse_blockio_weight
, "WEIGHT" },
3731 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3732 { config_parse_long
, "LONG" },
3733 { config_parse_socket_service
, "SERVICE" },
3735 { config_parse_exec_selinux_context
, "LABEL" },
3737 { config_parse_job_mode
, "MODE" },
3738 { config_parse_job_mode_isolate
, "BOOLEAN" },
3739 { config_parse_personality
, "PERSONALITY" },
3742 const char *prev
= NULL
;
3747 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3748 const char *rvalue
= "OTHER", *lvalue
;
3752 const ConfigPerfItem
*p
;
3754 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3756 dot
= strchr(i
, '.');
3757 lvalue
= dot
? dot
+ 1 : i
;
3761 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3765 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3768 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3769 if (p
->parse
== table
[j
].callback
) {
3770 rvalue
= table
[j
].rvalue
;
3774 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);