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
;
1481 _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_service_sockets(
1512 const char *filename
,
1514 const char *section
,
1515 unsigned section_line
,
1523 const char *word
, *state
;
1532 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1533 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1535 t
= strndup(word
, l
);
1539 r
= unit_name_printf(UNIT(s
), t
, &k
);
1541 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1545 if (!endswith(k
, ".socket")) {
1546 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1550 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1552 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1554 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1556 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1558 if (!isempty(state
))
1559 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1564 int config_parse_bus_name(
1566 const char *filename
,
1568 const char *section
,
1569 unsigned section_line
,
1576 _cleanup_free_
char *k
= NULL
;
1585 r
= unit_full_printf(u
, rvalue
, &k
);
1587 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1591 if (!service_name_is_valid(k
)) {
1592 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1596 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1599 int config_parse_service_timeout(const char *unit
,
1600 const char *filename
,
1602 const char *section
,
1603 unsigned section_line
,
1610 Service
*s
= userdata
;
1618 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1619 rvalue
, data
, userdata
);
1623 if (streq(lvalue
, "TimeoutSec")) {
1624 s
->start_timeout_defined
= true;
1625 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1626 } else if (streq(lvalue
, "TimeoutStartSec"))
1627 s
->start_timeout_defined
= true;
1632 int config_parse_busname_service(
1634 const char *filename
,
1636 const char *section
,
1637 unsigned section_line
,
1644 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1648 _cleanup_free_
char *p
= NULL
;
1655 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1657 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1661 if (!endswith(p
, ".service")) {
1662 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1666 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1668 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1672 unit_ref_set(&n
->service
, x
);
1677 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1679 int config_parse_bus_policy(
1681 const char *filename
,
1683 const char *section
,
1684 unsigned section_line
,
1691 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1692 _cleanup_free_
char *id_str
= NULL
;
1693 BusName
*busname
= data
;
1701 p
= new0(BusNamePolicy
, 1);
1705 if (streq(lvalue
, "AllowUser"))
1706 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1707 else if (streq(lvalue
, "AllowGroup"))
1708 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1710 assert_not_reached("Unknown lvalue");
1712 id_str
= strdup(rvalue
);
1716 access_str
= strpbrk(id_str
, WHITESPACE
);
1718 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy value '%s'", rvalue
);
1724 access_str
+= strspn(access_str
, WHITESPACE
);
1726 p
->access
= bus_policy_access_from_string(access_str
);
1727 if (p
->access
< 0) {
1728 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy access type '%s'", access_str
);
1735 LIST_PREPEND(policy
, busname
->policy
, p
);
1741 int config_parse_bus_endpoint_policy(
1743 const char *filename
,
1745 const char *section
,
1746 unsigned section_line
,
1753 _cleanup_free_
char *name
= NULL
;
1754 BusPolicyAccess access
;
1755 ExecContext
*c
= data
;
1764 name
= strdup(rvalue
);
1768 access_str
= strpbrk(name
, WHITESPACE
);
1770 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy value '%s'", rvalue
);
1776 access_str
+= strspn(access_str
, WHITESPACE
);
1778 access
= bus_policy_access_from_string(access_str
);
1779 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1780 access
>= _BUS_POLICY_ACCESS_MAX
) {
1781 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy access type '%s'", access_str
);
1785 if (!c
->bus_endpoint
) {
1786 r
= bus_endpoint_new(&c
->bus_endpoint
);
1788 return log_error_errno(r
, "Failed to create bus endpoint object: %m");
1791 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1794 int config_parse_working_directory(
1796 const char *filename
,
1798 const char *section
,
1799 unsigned section_line
,
1806 ExecContext
*c
= data
;
1817 if (rvalue
[0] == '-') {
1823 if (streq(rvalue
, "~")) {
1824 c
->working_directory_home
= true;
1825 c
->working_directory
= mfree(c
->working_directory
);
1827 _cleanup_free_
char *k
= NULL
;
1829 r
= unit_full_printf(u
, rvalue
, &k
);
1831 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
1835 path_kill_slashes(k
);
1837 if (!utf8_is_valid(k
)) {
1838 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
1842 if (!path_is_absolute(k
)) {
1843 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
1847 free(c
->working_directory
);
1848 c
->working_directory
= k
;
1851 c
->working_directory_home
= false;
1854 c
->working_directory_missing_ok
= missing_ok
;
1858 int config_parse_unit_env_file(const char *unit
,
1859 const char *filename
,
1861 const char *section
,
1862 unsigned section_line
,
1871 _cleanup_free_
char *n
= NULL
;
1879 if (isempty(rvalue
)) {
1880 /* Empty assignment frees the list */
1881 *env
= strv_free(*env
);
1885 r
= unit_full_printf(u
, rvalue
, &n
);
1887 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1891 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
1892 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
1896 r
= strv_extend(env
, n
);
1903 int config_parse_environ(const char *unit
,
1904 const char *filename
,
1906 const char *section
,
1907 unsigned section_line
,
1916 const char *word
, *state
;
1918 _cleanup_free_
char *k
= NULL
;
1926 if (isempty(rvalue
)) {
1927 /* Empty assignment resets the list */
1928 *env
= strv_free(*env
);
1933 r
= unit_full_printf(u
, rvalue
, &k
);
1935 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1946 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1947 _cleanup_free_
char *n
= NULL
;
1950 r
= cunescape_length(word
, l
, 0, &n
);
1952 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
1956 if (!env_assignment_is_valid(n
)) {
1957 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid environment assignment, ignoring: %s", rvalue
);
1961 x
= strv_env_set(*env
, n
);
1968 if (!isempty(state
))
1969 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1974 int config_parse_ip_tos(const char *unit
,
1975 const char *filename
,
1977 const char *section
,
1978 unsigned section_line
,
1985 int *ip_tos
= data
, x
;
1992 x
= ip_tos_from_string(rvalue
);
1994 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2002 int config_parse_unit_condition_path(
2004 const char *filename
,
2006 const char *section
,
2007 unsigned section_line
,
2014 _cleanup_free_
char *p
= NULL
;
2015 Condition
**list
= data
, *c
;
2016 ConditionType t
= ltype
;
2017 bool trigger
, negate
;
2026 if (isempty(rvalue
)) {
2027 /* Empty assignment resets the list */
2028 *list
= condition_free_list(*list
);
2032 trigger
= rvalue
[0] == '|';
2036 negate
= rvalue
[0] == '!';
2040 r
= unit_full_printf(u
, rvalue
, &p
);
2042 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2046 if (!path_is_absolute(p
)) {
2047 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2051 c
= condition_new(t
, p
, trigger
, negate
);
2055 LIST_PREPEND(conditions
, *list
, c
);
2059 int config_parse_unit_condition_string(
2061 const char *filename
,
2063 const char *section
,
2064 unsigned section_line
,
2071 _cleanup_free_
char *s
= NULL
;
2072 Condition
**list
= data
, *c
;
2073 ConditionType t
= ltype
;
2074 bool trigger
, negate
;
2083 if (isempty(rvalue
)) {
2084 /* Empty assignment resets the list */
2085 *list
= condition_free_list(*list
);
2089 trigger
= rvalue
[0] == '|';
2093 negate
= rvalue
[0] == '!';
2097 r
= unit_full_printf(u
, rvalue
, &s
);
2099 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2103 c
= condition_new(t
, s
, trigger
, negate
);
2107 LIST_PREPEND(conditions
, *list
, c
);
2111 int config_parse_unit_condition_null(
2113 const char *filename
,
2115 const char *section
,
2116 unsigned section_line
,
2123 Condition
**list
= data
, *c
;
2124 bool trigger
, negate
;
2132 if (isempty(rvalue
)) {
2133 /* Empty assignment resets the list */
2134 *list
= condition_free_list(*list
);
2138 trigger
= rvalue
[0] == '|';
2142 negate
= rvalue
[0] == '!';
2146 b
= parse_boolean(rvalue
);
2148 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2155 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2159 LIST_PREPEND(conditions
, *list
, c
);
2163 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2164 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2166 int config_parse_unit_requires_mounts_for(
2168 const char *filename
,
2170 const char *section
,
2171 unsigned section_line
,
2179 const char *word
, *state
;
2187 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2189 _cleanup_free_
char *n
;
2191 n
= strndup(word
, l
);
2195 if (!utf8_is_valid(n
)) {
2196 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2200 r
= unit_require_mounts_for(u
, n
);
2202 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount for, ignoring: %s", rvalue
);
2206 if (!isempty(state
))
2207 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2212 int config_parse_documentation(const char *unit
,
2213 const char *filename
,
2215 const char *section
,
2216 unsigned section_line
,
2232 if (isempty(rvalue
)) {
2233 /* Empty assignment resets the list */
2234 u
->documentation
= strv_free(u
->documentation
);
2238 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2239 rvalue
, data
, userdata
);
2243 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2245 if (documentation_url_is_valid(*a
))
2248 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2259 int config_parse_syscall_filter(
2261 const char *filename
,
2263 const char *section
,
2264 unsigned section_line
,
2271 static const char default_syscalls
[] =
2278 ExecContext
*c
= data
;
2280 bool invert
= false;
2281 const char *word
, *state
;
2290 if (isempty(rvalue
)) {
2291 /* Empty assignment resets the list */
2292 c
->syscall_filter
= set_free(c
->syscall_filter
);
2293 c
->syscall_whitelist
= false;
2297 if (rvalue
[0] == '~') {
2302 if (!c
->syscall_filter
) {
2303 c
->syscall_filter
= set_new(NULL
);
2304 if (!c
->syscall_filter
)
2308 /* Allow everything but the ones listed */
2309 c
->syscall_whitelist
= false;
2313 /* Allow nothing but the ones listed */
2314 c
->syscall_whitelist
= true;
2316 /* Accept default syscalls if we are on a whitelist */
2317 NULSTR_FOREACH(i
, default_syscalls
) {
2320 id
= seccomp_syscall_resolve_name(i
);
2324 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2333 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2334 _cleanup_free_
char *t
= NULL
;
2337 t
= strndup(word
, l
);
2341 id
= seccomp_syscall_resolve_name(t
);
2343 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2347 /* If we previously wanted to forbid a syscall and now
2348 * we want to allow it, then remove it from the list
2350 if (!invert
== c
->syscall_whitelist
) {
2351 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2357 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2359 if (!isempty(state
))
2360 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2362 /* Turn on NNP, but only if it wasn't configured explicitly
2363 * before, and only if we are in user mode. */
2364 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2365 c
->no_new_privileges
= true;
2370 int config_parse_syscall_archs(
2372 const char *filename
,
2374 const char *section
,
2375 unsigned section_line
,
2383 const char *word
, *state
;
2387 if (isempty(rvalue
)) {
2388 *archs
= set_free(*archs
);
2392 r
= set_ensure_allocated(archs
, NULL
);
2396 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2397 _cleanup_free_
char *t
= NULL
;
2400 t
= strndup(word
, l
);
2404 r
= seccomp_arch_from_string(t
, &a
);
2406 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call architecture, ignoring: %s", t
);
2410 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2416 if (!isempty(state
))
2417 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2422 int config_parse_syscall_errno(
2424 const char *filename
,
2426 const char *section
,
2427 unsigned section_line
,
2434 ExecContext
*c
= data
;
2441 if (isempty(rvalue
)) {
2442 /* Empty assignment resets to KILL */
2443 c
->syscall_errno
= 0;
2447 e
= errno_from_name(rvalue
);
2449 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "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
, 0, "Failed to parse address family, ignoring: %s", t
);
2513 /* If we previously wanted to forbid an address family and now
2514 * we want to allow it, then remove it from the list
2516 if (!invert
== c
->address_families_whitelist
) {
2517 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2523 set_remove(c
->address_families
, INT_TO_PTR(af
));
2525 if (!isempty(state
))
2526 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2532 int config_parse_unit_slice(
2534 const char *filename
,
2536 const char *section
,
2537 unsigned section_line
,
2544 _cleanup_free_
char *k
= NULL
;
2545 Unit
*u
= userdata
, *slice
= NULL
;
2553 r
= unit_name_printf(u
, rvalue
, &k
);
2555 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2559 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2561 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2565 r
= unit_set_slice(u
, slice
);
2567 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2574 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2576 int config_parse_cpu_shares(
2578 const char *filename
,
2580 const char *section
,
2581 unsigned section_line
,
2588 uint64_t *shares
= data
;
2595 r
= cg_cpu_shares_parse(rvalue
, shares
);
2597 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2604 int config_parse_cpu_quota(
2606 const char *filename
,
2608 const char *section
,
2609 unsigned section_line
,
2616 CGroupContext
*c
= data
;
2623 if (isempty(rvalue
)) {
2624 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2628 if (!endswith(rvalue
, "%")) {
2629 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2633 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2634 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' invalid. Ignoring.", rvalue
);
2638 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2643 int config_parse_memory_limit(
2645 const char *filename
,
2647 const char *section
,
2648 unsigned section_line
,
2655 CGroupContext
*c
= data
;
2659 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2660 c
->memory_limit
= (uint64_t) -1;
2664 r
= parse_size(rvalue
, 1024, &bytes
);
2665 if (r
< 0 || bytes
< 1) {
2666 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2670 c
->memory_limit
= bytes
;
2674 int config_parse_tasks_max(
2676 const char *filename
,
2678 const char *section
,
2679 unsigned section_line
,
2686 CGroupContext
*c
= data
;
2690 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2691 c
->tasks_max
= (uint64_t) -1;
2695 r
= safe_atou64(rvalue
, &u
);
2696 if (r
< 0 || u
< 1) {
2697 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2704 int config_parse_device_allow(
2706 const char *filename
,
2708 const char *section
,
2709 unsigned section_line
,
2716 _cleanup_free_
char *path
= NULL
;
2717 CGroupContext
*c
= data
;
2718 CGroupDeviceAllow
*a
;
2722 if (isempty(rvalue
)) {
2723 while (c
->device_allow
)
2724 cgroup_context_free_device_allow(c
, c
->device_allow
);
2729 n
= strcspn(rvalue
, WHITESPACE
);
2730 path
= strndup(rvalue
, n
);
2734 if (!startswith(path
, "/dev/") &&
2735 !startswith(path
, "block-") &&
2736 !startswith(path
, "char-")) {
2737 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2741 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2745 if (!in_charset(m
, "rwm")) {
2746 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
2750 a
= new0(CGroupDeviceAllow
, 1);
2756 a
->r
= !!strchr(m
, 'r');
2757 a
->w
= !!strchr(m
, 'w');
2758 a
->m
= !!strchr(m
, 'm');
2760 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2764 int config_parse_blockio_weight(
2766 const char *filename
,
2768 const char *section
,
2769 unsigned section_line
,
2776 uint64_t *weight
= data
;
2783 r
= cg_blkio_weight_parse(rvalue
, weight
);
2785 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2792 int config_parse_blockio_device_weight(
2794 const char *filename
,
2796 const char *section
,
2797 unsigned section_line
,
2804 _cleanup_free_
char *path
= NULL
;
2805 CGroupBlockIODeviceWeight
*w
;
2806 CGroupContext
*c
= data
;
2816 if (isempty(rvalue
)) {
2817 while (c
->blockio_device_weights
)
2818 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2823 n
= strcspn(rvalue
, WHITESPACE
);
2824 weight
= rvalue
+ n
;
2825 weight
+= strspn(weight
, WHITESPACE
);
2827 if (isempty(weight
)) {
2828 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
2832 path
= strndup(rvalue
, n
);
2836 if (!path_startswith(path
, "/dev")) {
2837 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2841 r
= cg_blkio_weight_parse(weight
, &u
);
2843 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2847 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2849 w
= new0(CGroupBlockIODeviceWeight
, 1);
2858 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2862 int config_parse_blockio_bandwidth(
2864 const char *filename
,
2866 const char *section
,
2867 unsigned section_line
,
2874 _cleanup_free_
char *path
= NULL
;
2875 CGroupBlockIODeviceBandwidth
*b
;
2876 CGroupContext
*c
= data
;
2877 const char *bandwidth
;
2887 read
= streq("BlockIOReadBandwidth", lvalue
);
2889 if (isempty(rvalue
)) {
2890 CGroupBlockIODeviceBandwidth
*next
;
2892 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2893 if (b
->read
== read
)
2894 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2899 n
= strcspn(rvalue
, WHITESPACE
);
2900 bandwidth
= rvalue
+ n
;
2901 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2904 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
2908 path
= strndup(rvalue
, n
);
2912 if (!path_startswith(path
, "/dev")) {
2913 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2917 r
= parse_size(bandwidth
, 1000, &bytes
);
2918 if (r
< 0 || bytes
<= 0) {
2919 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2923 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2929 b
->bandwidth
= bytes
;
2932 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2937 int config_parse_netclass(
2939 const char *filename
,
2941 const char *section
,
2942 unsigned section_line
,
2949 CGroupContext
*c
= data
;
2957 if (streq(rvalue
, "auto")) {
2958 c
->netclass_type
= CGROUP_NETCLASS_TYPE_AUTO
;
2962 r
= safe_atou32(rvalue
, &v
);
2964 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Netclass '%s' invalid. Ignoring.", rvalue
);
2968 if (v
> CGROUP_NETCLASS_FIXED_MAX
)
2969 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2970 "Fixed netclass %" PRIu32
" out of allowed range (0-%d). Applying anyway.", v
, (uint32_t) CGROUP_NETCLASS_FIXED_MAX
);
2973 c
->netclass_type
= CGROUP_NETCLASS_TYPE_FIXED
;
2978 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
2980 int config_parse_job_mode_isolate(
2982 const char *filename
,
2984 const char *section
,
2985 unsigned section_line
,
2999 r
= parse_boolean(rvalue
);
3001 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3005 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3009 int config_parse_runtime_directory(
3011 const char *filename
,
3013 const char *section
,
3014 unsigned section_line
,
3023 const char *word
, *state
;
3032 if (isempty(rvalue
)) {
3033 /* Empty assignment resets the list */
3034 *rt
= strv_free(*rt
);
3038 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3039 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3041 t
= strndup(word
, l
);
3045 r
= unit_name_printf(u
, t
, &n
);
3047 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
3051 if (!filename_is_valid(n
)) {
3052 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3056 r
= strv_push(rt
, n
);
3062 if (!isempty(state
))
3063 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3068 int config_parse_set_status(
3070 const char *filename
,
3072 const char *section
,
3073 unsigned section_line
,
3081 const char *word
, *state
;
3083 ExitStatusSet
*status_set
= data
;
3090 /* Empty assignment resets the list */
3091 if (isempty(rvalue
)) {
3092 exit_status_set_free(status_set
);
3096 FOREACH_WORD(word
, l
, rvalue
, state
) {
3097 _cleanup_free_
char *temp
;
3101 temp
= strndup(word
, l
);
3105 r
= safe_atoi(temp
, &val
);
3107 val
= signal_from_string_try_harder(temp
);
3110 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3113 set
= &status_set
->signal
;
3115 if (val
< 0 || val
> 255) {
3116 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3119 set
= &status_set
->status
;
3122 r
= set_ensure_allocated(set
, NULL
);
3126 r
= set_put(*set
, INT_TO_PTR(val
));
3128 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3132 if (!isempty(state
))
3133 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3138 int config_parse_namespace_path_strv(
3140 const char *filename
,
3142 const char *section
,
3143 unsigned section_line
,
3151 const char *word
, *state
;
3160 if (isempty(rvalue
)) {
3161 /* Empty assignment resets the list */
3162 *sv
= strv_free(*sv
);
3166 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3167 _cleanup_free_
char *n
;
3170 n
= strndup(word
, l
);
3174 if (!utf8_is_valid(n
)) {
3175 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
3179 offset
= n
[0] == '-';
3180 if (!path_is_absolute(n
+ offset
)) {
3181 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", rvalue
);
3185 path_kill_slashes(n
);
3187 r
= strv_push(sv
, n
);
3193 if (!isempty(state
))
3194 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3199 int config_parse_no_new_privileges(
3201 const char *filename
,
3203 const char *section
,
3204 unsigned section_line
,
3211 ExecContext
*c
= data
;
3219 k
= parse_boolean(rvalue
);
3221 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3225 c
->no_new_privileges
= !!k
;
3226 c
->no_new_privileges_set
= true;
3231 int config_parse_protect_home(
3233 const char *filename
,
3235 const char *section
,
3236 unsigned section_line
,
3243 ExecContext
*c
= data
;
3251 /* Our enum shall be a superset of booleans, hence first try
3252 * to parse as as boolean, and then as enum */
3254 k
= parse_boolean(rvalue
);
3256 c
->protect_home
= PROTECT_HOME_YES
;
3258 c
->protect_home
= PROTECT_HOME_NO
;
3262 h
= protect_home_from_string(rvalue
);
3264 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
3268 c
->protect_home
= h
;
3274 int config_parse_protect_system(
3276 const char *filename
,
3278 const char *section
,
3279 unsigned section_line
,
3286 ExecContext
*c
= data
;
3294 /* Our enum shall be a superset of booleans, hence first try
3295 * to parse as as boolean, and then as enum */
3297 k
= parse_boolean(rvalue
);
3299 c
->protect_system
= PROTECT_SYSTEM_YES
;
3301 c
->protect_system
= PROTECT_SYSTEM_NO
;
3305 s
= protect_system_from_string(rvalue
);
3307 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
3311 c
->protect_system
= s
;
3317 #define FOLLOW_MAX 8
3319 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3330 /* This will update the filename pointer if the loaded file is
3331 * reached by a symlink. The old string will be freed. */
3334 char *target
, *name
;
3336 if (c
++ >= FOLLOW_MAX
)
3339 path_kill_slashes(*filename
);
3341 /* Add the file name we are currently looking at to
3342 * the names of this unit, but only if it is a valid
3344 name
= basename(*filename
);
3346 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3348 id
= set_get(names
, name
);
3354 r
= set_consume(names
, id
);
3360 /* Try to open the file name, but don't if its a symlink */
3361 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3368 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3369 r
= readlink_and_make_absolute(*filename
, &target
);
3377 f
= fdopen(fd
, "re");
3388 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3396 /* Let's try to add in all symlink names we found */
3397 while ((k
= set_steal_first(names
))) {
3399 /* First try to merge in the other name into our
3401 r
= unit_merge_by_name(*u
, k
);
3405 /* Hmm, we couldn't merge the other unit into
3406 * ours? Then let's try it the other way
3409 other
= manager_get_unit((*u
)->manager
, k
);
3413 r
= unit_merge(other
, *u
);
3416 return merge_by_names(u
, names
, NULL
);
3424 unit_choose_id(*u
, id
);
3432 static int load_from_path(Unit
*u
, const char *path
) {
3434 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3435 _cleanup_fclose_
FILE *f
= NULL
;
3436 _cleanup_free_
char *filename
= NULL
;
3444 symlink_names
= set_new(&string_hash_ops
);
3448 if (path_is_absolute(path
)) {
3450 filename
= strdup(path
);
3454 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3456 filename
= mfree(filename
);
3464 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3466 /* Instead of opening the path right away, we manually
3467 * follow all symlinks and add their name to our unit
3468 * name set while doing so */
3469 filename
= path_make_absolute(path
, *p
);
3473 if (u
->manager
->unit_path_cache
&&
3474 !set_get(u
->manager
->unit_path_cache
, filename
))
3477 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3480 filename
= mfree(filename
);
3484 /* Empty the symlink names for the next run */
3485 set_clear_free(symlink_names
);
3494 /* Hmm, no suitable file found? */
3498 r
= merge_by_names(&merged
, symlink_names
, id
);
3503 u
->load_state
= UNIT_MERGED
;
3507 if (fstat(fileno(f
), &st
) < 0)
3510 if (null_or_empty(&st
))
3511 u
->load_state
= UNIT_MASKED
;
3513 u
->load_state
= UNIT_LOADED
;
3515 /* Now, parse the file contents */
3516 r
= config_parse(u
->id
, filename
, f
,
3517 UNIT_VTABLE(u
)->sections
,
3518 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3519 false, true, false, u
);
3524 free(u
->fragment_path
);
3525 u
->fragment_path
= filename
;
3528 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3530 if (u
->source_path
) {
3531 if (stat(u
->source_path
, &st
) >= 0)
3532 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3534 u
->source_mtime
= 0;
3540 int unit_load_fragment(Unit
*u
) {
3546 assert(u
->load_state
== UNIT_STUB
);
3550 u
->load_state
= UNIT_LOADED
;
3554 /* First, try to find the unit under its id. We always look
3555 * for unit files in the default directories, to make it easy
3556 * to override things by placing things in /etc/systemd/system */
3557 r
= load_from_path(u
, u
->id
);
3561 /* Try to find an alias we can load this with */
3562 if (u
->load_state
== UNIT_STUB
) {
3563 SET_FOREACH(t
, u
->names
, i
) {
3568 r
= load_from_path(u
, t
);
3572 if (u
->load_state
!= UNIT_STUB
)
3577 /* And now, try looking for it under the suggested (originally linked) path */
3578 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3580 r
= load_from_path(u
, u
->fragment_path
);
3584 if (u
->load_state
== UNIT_STUB
)
3585 /* Hmm, this didn't work? Then let's get rid
3586 * of the fragment path stored for us, so that
3587 * we don't point to an invalid location. */
3588 u
->fragment_path
= mfree(u
->fragment_path
);
3591 /* Look for a template */
3592 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3593 _cleanup_free_
char *k
= NULL
;
3595 r
= unit_name_template(u
->id
, &k
);
3599 r
= load_from_path(u
, k
);
3603 if (u
->load_state
== UNIT_STUB
) {
3604 SET_FOREACH(t
, u
->names
, i
) {
3605 _cleanup_free_
char *z
= NULL
;
3610 r
= unit_name_template(t
, &z
);
3614 r
= load_from_path(u
, z
);
3618 if (u
->load_state
!= UNIT_STUB
)
3627 void unit_dump_config_items(FILE *f
) {
3628 static const struct {
3629 const ConfigParserCallback callback
;
3632 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3633 { config_parse_warn_compat
, "NOTSUPPORTED" },
3635 { config_parse_int
, "INTEGER" },
3636 { config_parse_unsigned
, "UNSIGNED" },
3637 { config_parse_iec_size
, "SIZE" },
3638 { config_parse_iec_uint64
, "SIZE" },
3639 { config_parse_si_size
, "SIZE" },
3640 { config_parse_bool
, "BOOLEAN" },
3641 { config_parse_string
, "STRING" },
3642 { config_parse_path
, "PATH" },
3643 { config_parse_unit_path_printf
, "PATH" },
3644 { config_parse_strv
, "STRING [...]" },
3645 { config_parse_exec_nice
, "NICE" },
3646 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3647 { config_parse_exec_io_class
, "IOCLASS" },
3648 { config_parse_exec_io_priority
, "IOPRIORITY" },
3649 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3650 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3651 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3652 { config_parse_mode
, "MODE" },
3653 { config_parse_unit_env_file
, "FILE" },
3654 { config_parse_output
, "OUTPUT" },
3655 { config_parse_input
, "INPUT" },
3656 { config_parse_log_facility
, "FACILITY" },
3657 { config_parse_log_level
, "LEVEL" },
3658 { config_parse_exec_capabilities
, "CAPABILITIES" },
3659 { config_parse_exec_secure_bits
, "SECUREBITS" },
3660 { config_parse_bounding_set
, "BOUNDINGSET" },
3661 { config_parse_limit
, "LIMIT" },
3662 { config_parse_unit_deps
, "UNIT [...]" },
3663 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3664 { config_parse_service_type
, "SERVICETYPE" },
3665 { config_parse_service_restart
, "SERVICERESTART" },
3666 #ifdef HAVE_SYSV_COMPAT
3667 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3669 { config_parse_kill_mode
, "KILLMODE" },
3670 { config_parse_signal
, "SIGNAL" },
3671 { config_parse_socket_listen
, "SOCKET [...]" },
3672 { config_parse_socket_bind
, "SOCKETBIND" },
3673 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3674 { config_parse_sec
, "SECONDS" },
3675 { config_parse_nsec
, "NANOSECONDS" },
3676 { config_parse_namespace_path_strv
, "PATH [...]" },
3677 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3678 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3679 { config_parse_unit_string_printf
, "STRING" },
3680 { config_parse_trigger_unit
, "UNIT" },
3681 { config_parse_timer
, "TIMER" },
3682 { config_parse_path_spec
, "PATH" },
3683 { config_parse_notify_access
, "ACCESS" },
3684 { config_parse_ip_tos
, "TOS" },
3685 { config_parse_unit_condition_path
, "CONDITION" },
3686 { config_parse_unit_condition_string
, "CONDITION" },
3687 { config_parse_unit_condition_null
, "CONDITION" },
3688 { config_parse_unit_slice
, "SLICE" },
3689 { config_parse_documentation
, "URL" },
3690 { config_parse_service_timeout
, "SECONDS" },
3691 { config_parse_failure_action
, "ACTION" },
3692 { config_parse_set_status
, "STATUS" },
3693 { config_parse_service_sockets
, "SOCKETS" },
3694 { config_parse_environ
, "ENVIRON" },
3696 { config_parse_syscall_filter
, "SYSCALLS" },
3697 { config_parse_syscall_archs
, "ARCHS" },
3698 { config_parse_syscall_errno
, "ERRNO" },
3699 { config_parse_address_families
, "FAMILIES" },
3701 { config_parse_cpu_shares
, "SHARES" },
3702 { config_parse_memory_limit
, "LIMIT" },
3703 { config_parse_device_allow
, "DEVICE" },
3704 { config_parse_device_policy
, "POLICY" },
3705 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3706 { config_parse_blockio_weight
, "WEIGHT" },
3707 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3708 { config_parse_long
, "LONG" },
3709 { config_parse_socket_service
, "SERVICE" },
3711 { config_parse_exec_selinux_context
, "LABEL" },
3713 { config_parse_job_mode
, "MODE" },
3714 { config_parse_job_mode_isolate
, "BOOLEAN" },
3715 { config_parse_personality
, "PERSONALITY" },
3718 const char *prev
= NULL
;
3723 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3724 const char *rvalue
= "OTHER", *lvalue
;
3728 const ConfigPerfItem
*p
;
3730 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3732 dot
= strchr(i
, '.');
3733 lvalue
= dot
? dot
+ 1 : i
;
3737 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3741 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3744 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3745 if (p
->parse
== table
[j
].callback
) {
3746 rvalue
= table
[j
].rvalue
;
3750 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);