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>
29 #include <sys/resource.h>
36 #include "bus-error.h"
37 #include "bus-internal.h"
41 #include "conf-parser.h"
43 #include "errno-list.h"
47 #include "path-util.h"
49 #include "seccomp-util.h"
51 #include "securebits.h"
52 #include "signal-util.h"
54 #include "unit-name.h"
55 #include "unit-printf.h"
58 #include "load-fragment.h"
60 int config_parse_warn_compat(
65 unsigned section_line
,
71 Disabled reason
= ltype
;
74 case DISABLED_CONFIGURATION
:
75 log_syntax(unit
, LOG_DEBUG
, filename
, line
, EINVAL
,
76 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
79 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
80 "Support for option %s= has been removed and it is ignored", lvalue
);
82 case DISABLED_EXPERIMENTAL
:
83 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
84 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
91 int config_parse_unit_deps(const char *unit
,
95 unsigned section_line
,
102 UnitDependency d
= ltype
;
104 const char *word
, *state
;
111 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
112 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
115 t
= strndup(word
, l
);
119 r
= unit_name_printf(u
, t
, &k
);
121 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
122 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
126 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
128 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
129 "Failed to add dependency on %s, ignoring: %s", k
, strerror(-r
));
132 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
137 int config_parse_unit_string_printf(
139 const char *filename
,
142 unsigned section_line
,
149 _cleanup_free_
char *k
= NULL
;
158 r
= unit_full_printf(u
, rvalue
, &k
);
160 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
164 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
167 int config_parse_unit_strv_printf(const char *unit
,
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
,
190 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
192 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
193 k
? k
: rvalue
, 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: %s", rvalue
, strerror(-r
));
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: %s", t
, strerror(-r
));
262 if (!utf8_is_valid(k
)) {
263 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
267 if (!path_is_absolute(k
)) {
268 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Symlink path %s is not absolute, ignoring: %s", k
, strerror(-r
));
272 path_kill_slashes(k
);
281 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "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 p
->path
= strdup(rvalue
);
328 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
329 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
332 path_kill_slashes(p
->path
);
334 } else if (streq(lvalue
, "ListenNetlink")) {
335 _cleanup_free_
char *k
= NULL
;
337 p
->type
= SOCKET_SOCKET
;
338 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
340 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
341 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
343 r
= socket_address_parse_netlink(&p
->address
, k
?: rvalue
);
345 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
346 "Failed to parse address value, ignoring: %s", rvalue
);
351 _cleanup_free_
char *k
= NULL
;
353 p
->type
= SOCKET_SOCKET
;
354 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
356 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
357 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
359 r
= socket_address_parse_and_warn(&p
->address
, k
? k
: rvalue
);
361 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
362 "Failed to parse address value, ignoring: %s", rvalue
);
366 if (streq(lvalue
, "ListenStream"))
367 p
->address
.type
= SOCK_STREAM
;
368 else if (streq(lvalue
, "ListenDatagram"))
369 p
->address
.type
= SOCK_DGRAM
;
371 assert(streq(lvalue
, "ListenSequentialPacket"));
372 p
->address
.type
= SOCK_SEQPACKET
;
375 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
376 log_syntax(unit
, LOG_ERR
, filename
, line
, EOPNOTSUPP
,
377 "Address family not supported, ignoring: %s", rvalue
);
383 p
->auxiliary_fds
= NULL
;
384 p
->n_auxiliary_fds
= 0;
388 LIST_FIND_TAIL(port
, s
->ports
, tail
);
389 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
391 LIST_PREPEND(port
, s
->ports
, p
);
397 int config_parse_socket_bind(const char *unit
,
398 const char *filename
,
401 unsigned section_line
,
409 SocketAddressBindIPv6Only b
;
418 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
422 r
= parse_boolean(rvalue
);
424 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
425 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
429 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
431 s
->bind_ipv6_only
= b
;
436 int config_parse_exec_nice(const char *unit
,
437 const char *filename
,
440 unsigned section_line
,
447 ExecContext
*c
= data
;
455 r
= safe_atoi(rvalue
, &priority
);
457 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
458 "Failed to parse nice priority, ignoring: %s. ", rvalue
);
462 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
463 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
464 "Nice priority out of range, ignoring: %s", rvalue
);
474 int config_parse_exec_oom_score_adjust(const char* unit
,
475 const char *filename
,
478 unsigned section_line
,
485 ExecContext
*c
= data
;
493 r
= safe_atoi(rvalue
, &oa
);
495 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
496 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
500 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
501 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
502 "OOM score adjust value out of range, ignoring: %s", rvalue
);
506 c
->oom_score_adjust
= oa
;
507 c
->oom_score_adjust_set
= true;
512 int config_parse_exec(
514 const char *filename
,
517 unsigned section_line
,
524 ExecCommand
**e
= data
;
536 rvalue
+= strspn(rvalue
, WHITESPACE
);
539 if (isempty(rvalue
)) {
540 /* An empty assignment resets the list */
541 *e
= exec_command_free_list(*e
);
547 _cleanup_strv_free_
char **n
= NULL
;
548 size_t nlen
= 0, nbufsize
= 0;
549 _cleanup_free_ ExecCommand
*nce
= NULL
;
550 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
552 bool separate_argv0
= false, ignore
= false;
556 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
561 for (i
= 0; i
< 2; i
++) {
562 /* We accept an absolute path as first argument, or
563 * alternatively an absolute prefixed with @ to allow
564 * overriding of argv[0]. */
565 if (*f
== '-' && !ignore
)
567 else if (*f
== '@' && !separate_argv0
)
568 separate_argv0
= true;
575 /* First word is either "-" or "@" with no command. */
576 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
577 "Empty path in command line, ignoring: \"%s\"", rvalue
);
581 if (!string_is_safe(f
)) {
582 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
583 "Executable path contains special characters, ignoring: %s", rvalue
);
586 if (!path_is_absolute(f
)) {
587 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
588 "Executable path is not absolute, ignoring: %s", rvalue
);
591 if (endswith(f
, "/")) {
592 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
593 "Executable path specifies a directory, ignoring: %s", rvalue
);
597 if (f
== firstword
) {
606 if (!separate_argv0
) {
607 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
616 path_kill_slashes(path
);
618 while (!isempty(p
)) {
619 _cleanup_free_
char *word
= NULL
;
621 /* Check explicitly for an unquoted semicolon as
622 * command separator token. */
623 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
625 p
+= strspn(p
, WHITESPACE
);
630 /* Check for \; explicitly, to not confuse it with \\;
631 * or "\;" or "\\;" etc. extract_first_word would
632 * return the same for all of those. */
633 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
635 p
+= strspn(p
, WHITESPACE
);
636 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
646 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
652 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
660 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
661 "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
665 nce
= new0(ExecCommand
, 1);
671 nce
->ignore
= ignore
;
673 exec_command_append_list(e
, nce
);
675 /* Do not _cleanup_free_ these. */
686 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
687 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
689 int config_parse_socket_bindtodevice(const char* unit
,
690 const char *filename
,
693 unsigned section_line
,
708 if (rvalue
[0] && !streq(rvalue
, "*")) {
715 free(s
->bind_to_device
);
716 s
->bind_to_device
= n
;
721 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
722 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
724 int config_parse_exec_io_class(const char *unit
,
725 const char *filename
,
728 unsigned section_line
,
735 ExecContext
*c
= data
;
743 x
= ioprio_class_from_string(rvalue
);
745 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
746 "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
750 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
751 c
->ioprio_set
= true;
756 int config_parse_exec_io_priority(const char *unit
,
757 const char *filename
,
760 unsigned section_line
,
767 ExecContext
*c
= data
;
775 r
= safe_atoi(rvalue
, &i
);
776 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
777 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
778 "Failed to parse IO priority, ignoring: %s", rvalue
);
782 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
783 c
->ioprio_set
= true;
788 int config_parse_exec_cpu_sched_policy(const char *unit
,
789 const char *filename
,
792 unsigned section_line
,
800 ExecContext
*c
= data
;
808 x
= sched_policy_from_string(rvalue
);
810 log_syntax(unit
, LOG_ERR
, filename
, line
, -x
,
811 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
815 c
->cpu_sched_policy
= x
;
816 /* Moving to or from real-time policy? We need to adjust the priority */
817 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
818 c
->cpu_sched_set
= true;
823 int config_parse_exec_cpu_sched_prio(const char *unit
,
824 const char *filename
,
827 unsigned section_line
,
834 ExecContext
*c
= data
;
842 r
= safe_atoi(rvalue
, &i
);
844 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
845 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
849 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
850 min
= sched_get_priority_min(c
->cpu_sched_policy
);
851 max
= sched_get_priority_max(c
->cpu_sched_policy
);
853 if (i
< min
|| i
> max
) {
854 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
855 "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
859 c
->cpu_sched_priority
= i
;
860 c
->cpu_sched_set
= true;
865 int config_parse_exec_cpu_affinity(const char *unit
,
866 const char *filename
,
869 unsigned section_line
,
876 ExecContext
*c
= data
;
877 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
885 ncpus
= parse_cpu_set(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
894 /* An empty assignment resets the CPU list */
900 c
->cpuset_ncpus
= ncpus
;
905 int config_parse_exec_capabilities(const char *unit
,
906 const char *filename
,
909 unsigned section_line
,
916 ExecContext
*c
= data
;
924 cap
= cap_from_text(rvalue
);
926 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
,
927 "Failed to parse capabilities, ignoring: %s", rvalue
);
932 cap_free(c
->capabilities
);
933 c
->capabilities
= cap
;
938 int config_parse_exec_secure_bits(const char *unit
,
939 const char *filename
,
942 unsigned section_line
,
949 ExecContext
*c
= data
;
951 const char *word
, *state
;
958 if (isempty(rvalue
)) {
959 /* An empty assignment resets the field */
964 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
965 if (first_word(word
, "keep-caps"))
966 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
967 else if (first_word(word
, "keep-caps-locked"))
968 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
969 else if (first_word(word
, "no-setuid-fixup"))
970 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
971 else if (first_word(word
, "no-setuid-fixup-locked"))
972 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
973 else if (first_word(word
, "noroot"))
974 c
->secure_bits
|= 1<<SECURE_NOROOT
;
975 else if (first_word(word
, "noroot-locked"))
976 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
978 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
979 "Failed to parse secure bits, ignoring: %s", rvalue
);
984 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
985 "Invalid syntax, garbage at the end, ignoring.");
990 int config_parse_bounding_set(const char *unit
,
991 const char *filename
,
994 unsigned section_line
,
1001 uint64_t *capability_bounding_set_drop
= data
;
1002 const char *word
, *state
;
1004 bool invert
= false;
1012 if (rvalue
[0] == '~') {
1017 /* Note that we store this inverted internally, since the
1018 * kernel wants it like this. But we actually expose it
1019 * non-inverted everywhere to have a fully normalized
1022 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1023 _cleanup_free_
char *t
= NULL
;
1026 t
= strndup(word
, l
);
1030 cap
= capability_from_name(t
);
1032 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse capability in bounding set, ignoring: %s", t
);
1036 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1038 if (!isempty(state
))
1039 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1040 "Trailing garbage, ignoring.");
1043 *capability_bounding_set_drop
|= sum
;
1045 *capability_bounding_set_drop
|= ~sum
;
1050 int config_parse_limit(const char *unit
,
1051 const char *filename
,
1053 const char *section
,
1054 unsigned section_line
,
1061 struct rlimit
**rl
= data
;
1062 unsigned long long u
;
1071 if (streq(rvalue
, "infinity"))
1072 u
= (unsigned long long) RLIM_INFINITY
;
1076 r
= safe_atollu(rvalue
, &u
);
1078 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1079 "Failed to parse resource value, ignoring: %s", rvalue
);
1085 *rl
= new(struct rlimit
, 1);
1090 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1094 #ifdef HAVE_SYSV_COMPAT
1095 int config_parse_sysv_priority(const char *unit
,
1096 const char *filename
,
1098 const char *section
,
1099 unsigned section_line
,
1106 int *priority
= data
;
1114 r
= safe_atoi(rvalue
, &i
);
1115 if (r
< 0 || i
< 0) {
1116 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1117 "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1121 *priority
= (int) i
;
1126 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1127 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1129 int config_parse_exec_mount_flags(const char *unit
,
1130 const char *filename
,
1132 const char *section
,
1133 unsigned section_line
,
1140 ExecContext
*c
= data
;
1141 const char *word
, *state
;
1143 unsigned long flags
= 0;
1150 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1151 _cleanup_free_
char *t
;
1153 t
= strndup(word
, l
);
1157 if (streq(t
, "shared"))
1159 else if (streq(t
, "slave"))
1161 else if (streq(t
, "private"))
1164 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1168 if (!isempty(state
))
1169 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1171 c
->mount_flags
= flags
;
1175 int config_parse_exec_selinux_context(
1177 const char *filename
,
1179 const char *section
,
1180 unsigned section_line
,
1187 ExecContext
*c
= data
;
1198 if (isempty(rvalue
)) {
1199 c
->selinux_context
= mfree(c
->selinux_context
);
1200 c
->selinux_context_ignore
= false;
1204 if (rvalue
[0] == '-') {
1210 r
= unit_name_printf(u
, rvalue
, &k
);
1212 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1213 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1217 free(c
->selinux_context
);
1218 c
->selinux_context
= k
;
1219 c
->selinux_context_ignore
= ignore
;
1224 int config_parse_exec_apparmor_profile(
1226 const char *filename
,
1228 const char *section
,
1229 unsigned section_line
,
1236 ExecContext
*c
= data
;
1247 if (isempty(rvalue
)) {
1248 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1249 c
->apparmor_profile_ignore
= false;
1253 if (rvalue
[0] == '-') {
1259 r
= unit_name_printf(u
, rvalue
, &k
);
1261 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1262 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1266 free(c
->apparmor_profile
);
1267 c
->apparmor_profile
= k
;
1268 c
->apparmor_profile_ignore
= ignore
;
1273 int config_parse_exec_smack_process_label(
1275 const char *filename
,
1277 const char *section
,
1278 unsigned section_line
,
1285 ExecContext
*c
= data
;
1296 if (isempty(rvalue
)) {
1297 c
->smack_process_label
= mfree(c
->smack_process_label
);
1298 c
->smack_process_label_ignore
= false;
1302 if (rvalue
[0] == '-') {
1308 r
= unit_name_printf(u
, rvalue
, &k
);
1310 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1311 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1315 free(c
->smack_process_label
);
1316 c
->smack_process_label
= k
;
1317 c
->smack_process_label_ignore
= ignore
;
1322 int config_parse_timer(const char *unit
,
1323 const char *filename
,
1325 const char *section
,
1326 unsigned section_line
,
1337 CalendarSpec
*c
= NULL
;
1344 if (isempty(rvalue
)) {
1345 /* Empty assignment resets list */
1346 timer_free_values(t
);
1350 b
= timer_base_from_string(lvalue
);
1352 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
,
1353 "Failed to parse timer base, ignoring: %s", lvalue
);
1357 if (b
== TIMER_CALENDAR
) {
1358 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1359 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1360 "Failed to parse calendar specification, ignoring: %s",
1365 if (parse_sec(rvalue
, &u
) < 0) {
1366 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1367 "Failed to parse timer value, ignoring: %s",
1373 v
= new0(TimerValue
, 1);
1375 calendar_spec_free(c
);
1381 v
->calendar_spec
= c
;
1383 LIST_PREPEND(value
, t
->values
, v
);
1388 int config_parse_trigger_unit(
1390 const char *filename
,
1392 const char *section
,
1393 unsigned section_line
,
1400 _cleanup_free_
char *p
= NULL
;
1410 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1411 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1412 "Multiple units to trigger specified, ignoring: %s", rvalue
);
1416 r
= unit_name_printf(u
, rvalue
, &p
);
1418 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1419 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1421 type
= unit_name_to_type(p
?: rvalue
);
1423 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1424 "Unit type not valid, ignoring: %s", rvalue
);
1428 if (type
== u
->type
) {
1429 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1430 "Trigger cannot be of same type, ignoring: %s", rvalue
);
1434 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
?: rvalue
, NULL
, true);
1436 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1437 "Failed to add trigger on %s, ignoring: %s", p
?: rvalue
, strerror(-r
));
1444 int config_parse_path_spec(const char *unit
,
1445 const char *filename
,
1447 const char *section
,
1448 unsigned section_line
,
1458 _cleanup_free_
char *k
= NULL
;
1466 if (isempty(rvalue
)) {
1467 /* Empty assignment clears list */
1472 b
= path_type_from_string(lvalue
);
1474 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1475 "Failed to parse path type, ignoring: %s", lvalue
);
1479 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1485 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1486 "Failed to resolve unit specifiers on %s. Ignoring.",
1490 if (!path_is_absolute(k
)) {
1491 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1492 "Path is not absolute, ignoring: %s", k
);
1496 s
= new0(PathSpec
, 1);
1501 s
->path
= path_kill_slashes(k
);
1506 LIST_PREPEND(spec
, p
->specs
, s
);
1511 int config_parse_socket_service(
1513 const char *filename
,
1515 const char *section
,
1516 unsigned section_line
,
1523 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1527 _cleanup_free_
char *p
= NULL
;
1534 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1536 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1540 if (!endswith(p
, ".service")) {
1541 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type service, ignoring: %s", rvalue
);
1545 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1547 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1551 unit_ref_set(&s
->service
, x
);
1556 int config_parse_service_sockets(
1558 const char *filename
,
1560 const char *section
,
1561 unsigned section_line
,
1569 const char *word
, *state
;
1578 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1579 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1581 t
= strndup(word
, l
);
1585 r
= unit_name_printf(UNIT(s
), t
, &k
);
1587 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1591 if (!endswith(k
, ".socket")) {
1592 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Unit must be of type socket, ignoring: %s", k
);
1596 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1598 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1600 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1602 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1604 if (!isempty(state
))
1605 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Trailing garbage, ignoring.");
1610 int config_parse_bus_name(
1612 const char *filename
,
1614 const char *section
,
1615 unsigned section_line
,
1622 _cleanup_free_
char *k
= NULL
;
1631 r
= unit_full_printf(u
, rvalue
, &k
);
1633 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1637 if (!service_name_is_valid(k
)) {
1638 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid bus name %s, ignoring.", k
);
1642 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1645 int config_parse_service_timeout(const char *unit
,
1646 const char *filename
,
1648 const char *section
,
1649 unsigned section_line
,
1656 Service
*s
= userdata
;
1664 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1665 rvalue
, data
, userdata
);
1669 if (streq(lvalue
, "TimeoutSec")) {
1670 s
->start_timeout_defined
= true;
1671 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1672 } else if (streq(lvalue
, "TimeoutStartSec"))
1673 s
->start_timeout_defined
= true;
1678 int config_parse_busname_service(
1680 const char *filename
,
1682 const char *section
,
1683 unsigned section_line
,
1690 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1694 _cleanup_free_
char *p
= NULL
;
1701 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1703 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1704 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1708 if (!endswith(p
, ".service")) {
1709 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1710 "Unit must be of type service, ignoring: %s", rvalue
);
1714 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1716 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1717 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1721 unit_ref_set(&n
->service
, x
);
1726 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1728 int config_parse_bus_policy(
1730 const char *filename
,
1732 const char *section
,
1733 unsigned section_line
,
1740 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1741 _cleanup_free_
char *id_str
= NULL
;
1742 BusName
*busname
= data
;
1750 p
= new0(BusNamePolicy
, 1);
1754 if (streq(lvalue
, "AllowUser"))
1755 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1756 else if (streq(lvalue
, "AllowGroup"))
1757 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1759 assert_not_reached("Unknown lvalue");
1761 id_str
= strdup(rvalue
);
1765 access_str
= strpbrk(id_str
, WHITESPACE
);
1767 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1768 "Invalid busname policy value '%s'", rvalue
);
1774 access_str
+= strspn(access_str
, WHITESPACE
);
1776 p
->access
= bus_policy_access_from_string(access_str
);
1777 if (p
->access
< 0) {
1778 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1779 "Invalid busname policy access type '%s'", access_str
);
1786 LIST_PREPEND(policy
, busname
->policy
, p
);
1792 int config_parse_bus_endpoint_policy(
1794 const char *filename
,
1796 const char *section
,
1797 unsigned section_line
,
1804 _cleanup_free_
char *name
= NULL
;
1805 BusPolicyAccess access
;
1806 ExecContext
*c
= data
;
1815 name
= strdup(rvalue
);
1819 access_str
= strpbrk(name
, WHITESPACE
);
1821 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1822 "Invalid endpoint policy value '%s'", rvalue
);
1828 access_str
+= strspn(access_str
, WHITESPACE
);
1830 access
= bus_policy_access_from_string(access_str
);
1831 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1832 access
>= _BUS_POLICY_ACCESS_MAX
) {
1833 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1834 "Invalid endpoint policy access type '%s'", access_str
);
1838 if (!c
->bus_endpoint
) {
1839 r
= bus_endpoint_new(&c
->bus_endpoint
);
1845 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1848 int config_parse_working_directory(
1850 const char *filename
,
1852 const char *section
,
1853 unsigned section_line
,
1860 ExecContext
*c
= data
;
1871 if (rvalue
[0] == '-') {
1877 if (streq(rvalue
, "~")) {
1878 c
->working_directory_home
= true;
1879 c
->working_directory
= mfree(c
->working_directory
);
1881 _cleanup_free_
char *k
= NULL
;
1883 r
= unit_full_printf(u
, rvalue
, &k
);
1885 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
1889 path_kill_slashes(k
);
1891 if (!utf8_is_valid(k
)) {
1892 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, 0, rvalue
);
1896 if (!path_is_absolute(k
)) {
1897 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
1901 free(c
->working_directory
);
1902 c
->working_directory
= k
;
1905 c
->working_directory_home
= false;
1908 c
->working_directory_missing_ok
= missing_ok
;
1912 int config_parse_unit_env_file(const char *unit
,
1913 const char *filename
,
1915 const char *section
,
1916 unsigned section_line
,
1925 _cleanup_free_
char *n
= NULL
;
1934 if (isempty(rvalue
)) {
1935 /* Empty assignment frees the list */
1936 *env
= strv_free(*env
);
1940 r
= unit_full_printf(u
, rvalue
, &n
);
1942 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1943 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1946 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1947 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1948 "Path '%s' is not absolute, ignoring.", s
);
1952 r
= strv_extend(env
, s
);
1959 int config_parse_environ(const char *unit
,
1960 const char *filename
,
1962 const char *section
,
1963 unsigned section_line
,
1972 const char *word
, *state
;
1974 _cleanup_free_
char *k
= NULL
;
1982 if (isempty(rvalue
)) {
1983 /* Empty assignment resets the list */
1984 *env
= strv_free(*env
);
1989 r
= unit_full_printf(u
, rvalue
, &k
);
1991 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1999 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
2000 _cleanup_free_
char *n
= NULL
;
2003 r
= cunescape_length(word
, l
, 0, &n
);
2005 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
2009 if (!env_assignment_is_valid(n
)) {
2010 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid environment assignment, ignoring: %s", rvalue
);
2014 x
= strv_env_set(*env
, n
);
2021 if (!isempty(state
))
2022 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2023 "Trailing garbage, ignoring.");
2028 int config_parse_ip_tos(const char *unit
,
2029 const char *filename
,
2031 const char *section
,
2032 unsigned section_line
,
2039 int *ip_tos
= data
, x
;
2046 x
= ip_tos_from_string(rvalue
);
2048 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2049 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2057 int config_parse_unit_condition_path(
2059 const char *filename
,
2061 const char *section
,
2062 unsigned section_line
,
2069 _cleanup_free_
char *p
= NULL
;
2070 Condition
**list
= data
, *c
;
2071 ConditionType t
= ltype
;
2072 bool trigger
, negate
;
2081 if (isempty(rvalue
)) {
2082 /* Empty assignment resets the list */
2083 *list
= condition_free_list(*list
);
2087 trigger
= rvalue
[0] == '|';
2091 negate
= rvalue
[0] == '!';
2095 r
= unit_full_printf(u
, rvalue
, &p
);
2097 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2101 if (!path_is_absolute(p
)) {
2102 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2106 c
= condition_new(t
, p
, trigger
, negate
);
2110 LIST_PREPEND(conditions
, *list
, c
);
2114 int config_parse_unit_condition_string(
2116 const char *filename
,
2118 const char *section
,
2119 unsigned section_line
,
2126 _cleanup_free_
char *s
= NULL
;
2127 Condition
**list
= data
, *c
;
2128 ConditionType t
= ltype
;
2129 bool trigger
, negate
;
2138 if (isempty(rvalue
)) {
2139 /* Empty assignment resets the list */
2140 *list
= condition_free_list(*list
);
2144 trigger
= rvalue
[0] == '|';
2148 negate
= rvalue
[0] == '!';
2152 r
= unit_full_printf(u
, rvalue
, &s
);
2154 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2158 c
= condition_new(t
, s
, trigger
, negate
);
2162 LIST_PREPEND(conditions
, *list
, c
);
2166 int config_parse_unit_condition_null(
2168 const char *filename
,
2170 const char *section
,
2171 unsigned section_line
,
2178 Condition
**list
= data
, *c
;
2179 bool trigger
, negate
;
2187 if (isempty(rvalue
)) {
2188 /* Empty assignment resets the list */
2189 *list
= condition_free_list(*list
);
2193 trigger
= rvalue
[0] == '|';
2197 negate
= rvalue
[0] == '!';
2201 b
= parse_boolean(rvalue
);
2203 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2210 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2214 LIST_PREPEND(conditions
, *list
, c
);
2218 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2219 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2221 int config_parse_unit_requires_mounts_for(
2223 const char *filename
,
2225 const char *section
,
2226 unsigned section_line
,
2234 const char *word
, *state
;
2242 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2244 _cleanup_free_
char *n
;
2246 n
= strndup(word
, l
);
2250 if (!utf8_is_valid(n
)) {
2251 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2255 r
= unit_require_mounts_for(u
, n
);
2257 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2258 "Failed to add required mount for, ignoring: %s", rvalue
);
2262 if (!isempty(state
))
2263 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2264 "Trailing garbage, ignoring.");
2269 int config_parse_documentation(const char *unit
,
2270 const char *filename
,
2272 const char *section
,
2273 unsigned section_line
,
2289 if (isempty(rvalue
)) {
2290 /* Empty assignment resets the list */
2291 u
->documentation
= strv_free(u
->documentation
);
2295 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2296 rvalue
, data
, userdata
);
2300 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2302 if (documentation_url_is_valid(*a
))
2305 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2306 "Invalid URL, ignoring: %s", *a
);
2317 int config_parse_syscall_filter(
2319 const char *filename
,
2321 const char *section
,
2322 unsigned section_line
,
2329 static const char default_syscalls
[] =
2336 ExecContext
*c
= data
;
2338 bool invert
= false;
2339 const char *word
, *state
;
2348 if (isempty(rvalue
)) {
2349 /* Empty assignment resets the list */
2350 c
->syscall_filter
= set_free(c
->syscall_filter
);
2351 c
->syscall_whitelist
= false;
2355 if (rvalue
[0] == '~') {
2360 if (!c
->syscall_filter
) {
2361 c
->syscall_filter
= set_new(NULL
);
2362 if (!c
->syscall_filter
)
2366 /* Allow everything but the ones listed */
2367 c
->syscall_whitelist
= false;
2371 /* Allow nothing but the ones listed */
2372 c
->syscall_whitelist
= true;
2374 /* Accept default syscalls if we are on a whitelist */
2375 NULSTR_FOREACH(i
, default_syscalls
) {
2378 id
= seccomp_syscall_resolve_name(i
);
2382 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2391 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2392 _cleanup_free_
char *t
= NULL
;
2395 t
= strndup(word
, l
);
2399 id
= seccomp_syscall_resolve_name(t
);
2401 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2402 "Failed to parse system call, ignoring: %s", t
);
2406 /* If we previously wanted to forbid a syscall and now
2407 * we want to allow it, then remove it from the list
2409 if (!invert
== c
->syscall_whitelist
) {
2410 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2416 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2418 if (!isempty(state
))
2419 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2420 "Trailing garbage, ignoring.");
2422 /* Turn on NNP, but only if it wasn't configured explicitly
2423 * before, and only if we are in user mode. */
2424 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2425 c
->no_new_privileges
= true;
2430 int config_parse_syscall_archs(
2432 const char *filename
,
2434 const char *section
,
2435 unsigned section_line
,
2443 const char *word
, *state
;
2447 if (isempty(rvalue
)) {
2448 *archs
= set_free(*archs
);
2452 r
= set_ensure_allocated(archs
, NULL
);
2456 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2457 _cleanup_free_
char *t
= NULL
;
2460 t
= strndup(word
, l
);
2464 r
= seccomp_arch_from_string(t
, &a
);
2466 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2467 "Failed to parse system call architecture, ignoring: %s", t
);
2471 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2477 if (!isempty(state
))
2478 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2479 "Trailing garbage, ignoring.");
2484 int config_parse_syscall_errno(
2486 const char *filename
,
2488 const char *section
,
2489 unsigned section_line
,
2496 ExecContext
*c
= data
;
2503 if (isempty(rvalue
)) {
2504 /* Empty assignment resets to KILL */
2505 c
->syscall_errno
= 0;
2509 e
= errno_from_name(rvalue
);
2511 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2512 "Failed to parse error number, ignoring: %s", rvalue
);
2516 c
->syscall_errno
= e
;
2520 int config_parse_address_families(
2522 const char *filename
,
2524 const char *section
,
2525 unsigned section_line
,
2532 ExecContext
*c
= data
;
2533 bool invert
= false;
2534 const char *word
, *state
;
2542 if (isempty(rvalue
)) {
2543 /* Empty assignment resets the list */
2544 c
->address_families
= set_free(c
->address_families
);
2545 c
->address_families_whitelist
= false;
2549 if (rvalue
[0] == '~') {
2554 if (!c
->address_families
) {
2555 c
->address_families
= set_new(NULL
);
2556 if (!c
->address_families
)
2559 c
->address_families_whitelist
= !invert
;
2562 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2563 _cleanup_free_
char *t
= NULL
;
2566 t
= strndup(word
, l
);
2570 af
= af_from_name(t
);
2572 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2573 "Failed to parse address family, ignoring: %s", t
);
2577 /* If we previously wanted to forbid an address family and now
2578 * we want to allow it, then remove it from the list
2580 if (!invert
== c
->address_families_whitelist
) {
2581 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2587 set_remove(c
->address_families
, INT_TO_PTR(af
));
2589 if (!isempty(state
))
2590 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2591 "Trailing garbage, ignoring.");
2597 int config_parse_unit_slice(
2599 const char *filename
,
2601 const char *section
,
2602 unsigned section_line
,
2609 _cleanup_free_
char *k
= NULL
;
2610 Unit
*u
= userdata
, *slice
= NULL
;
2618 r
= unit_name_printf(u
, rvalue
, &k
);
2620 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2624 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2626 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2630 r
= unit_set_slice(u
, slice
);
2632 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2639 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2641 int config_parse_cpu_shares(
2643 const char *filename
,
2645 const char *section
,
2646 unsigned section_line
,
2653 uint64_t *shares
= data
;
2660 r
= cg_cpu_shares_parse(rvalue
, shares
);
2662 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2669 int config_parse_cpu_quota(
2671 const char *filename
,
2673 const char *section
,
2674 unsigned section_line
,
2681 CGroupContext
*c
= data
;
2688 if (isempty(rvalue
)) {
2689 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2693 if (!endswith(rvalue
, "%")) {
2695 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2696 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2700 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2701 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2702 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2706 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2711 int config_parse_memory_limit(
2713 const char *filename
,
2715 const char *section
,
2716 unsigned section_line
,
2723 CGroupContext
*c
= data
;
2727 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2728 c
->memory_limit
= (uint64_t) -1;
2732 r
= parse_size(rvalue
, 1024, &bytes
);
2733 if (r
< 0 || bytes
< 1) {
2734 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2738 c
->memory_limit
= bytes
;
2742 int config_parse_tasks_max(
2744 const char *filename
,
2746 const char *section
,
2747 unsigned section_line
,
2754 CGroupContext
*c
= data
;
2758 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2759 c
->tasks_max
= (uint64_t) -1;
2763 r
= safe_atou64(rvalue
, &u
);
2764 if (r
< 0 || u
< 1) {
2765 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2772 int config_parse_device_allow(
2774 const char *filename
,
2776 const char *section
,
2777 unsigned section_line
,
2784 _cleanup_free_
char *path
= NULL
;
2785 CGroupContext
*c
= data
;
2786 CGroupDeviceAllow
*a
;
2790 if (isempty(rvalue
)) {
2791 while (c
->device_allow
)
2792 cgroup_context_free_device_allow(c
, c
->device_allow
);
2797 n
= strcspn(rvalue
, WHITESPACE
);
2798 path
= strndup(rvalue
, n
);
2802 if (!startswith(path
, "/dev/") &&
2803 !startswith(path
, "block-") &&
2804 !startswith(path
, "char-")) {
2805 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2806 "Invalid device node path '%s'. Ignoring.", path
);
2810 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2814 if (!in_charset(m
, "rwm")) {
2815 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2816 "Invalid device rights '%s'. Ignoring.", m
);
2820 a
= new0(CGroupDeviceAllow
, 1);
2826 a
->r
= !!strchr(m
, 'r');
2827 a
->w
= !!strchr(m
, 'w');
2828 a
->m
= !!strchr(m
, 'm');
2830 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2834 int config_parse_blockio_weight(
2836 const char *filename
,
2838 const char *section
,
2839 unsigned section_line
,
2846 uint64_t *weight
= data
;
2853 r
= cg_blkio_weight_parse(rvalue
, weight
);
2855 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2862 int config_parse_blockio_device_weight(
2864 const char *filename
,
2866 const char *section
,
2867 unsigned section_line
,
2874 _cleanup_free_
char *path
= NULL
;
2875 CGroupBlockIODeviceWeight
*w
;
2876 CGroupContext
*c
= data
;
2886 if (isempty(rvalue
)) {
2887 while (c
->blockio_device_weights
)
2888 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2893 n
= strcspn(rvalue
, WHITESPACE
);
2894 weight
= rvalue
+ n
;
2895 weight
+= strspn(weight
, WHITESPACE
);
2897 if (isempty(weight
)) {
2898 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Expected block device and device weight. Ignoring.");
2902 path
= strndup(rvalue
, n
);
2906 if (!path_startswith(path
, "/dev")) {
2907 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid device node path '%s'. Ignoring.", path
);
2911 r
= cg_blkio_weight_parse(weight
, &u
);
2913 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2917 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2919 w
= new0(CGroupBlockIODeviceWeight
, 1);
2928 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2932 int config_parse_blockio_bandwidth(
2934 const char *filename
,
2936 const char *section
,
2937 unsigned section_line
,
2944 _cleanup_free_
char *path
= NULL
;
2945 CGroupBlockIODeviceBandwidth
*b
;
2946 CGroupContext
*c
= data
;
2947 const char *bandwidth
;
2957 read
= streq("BlockIOReadBandwidth", lvalue
);
2959 if (isempty(rvalue
)) {
2960 CGroupBlockIODeviceBandwidth
*next
;
2962 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2963 if (b
->read
== read
)
2964 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2969 n
= strcspn(rvalue
, WHITESPACE
);
2970 bandwidth
= rvalue
+ n
;
2971 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2974 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2975 "Expected space separated pair of device node and bandwidth. Ignoring.");
2979 path
= strndup(rvalue
, n
);
2983 if (!path_startswith(path
, "/dev")) {
2984 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2985 "Invalid device node path '%s'. Ignoring.", path
);
2989 r
= parse_size(bandwidth
, 1000, &bytes
);
2990 if (r
< 0 || bytes
<= 0) {
2991 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2992 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2996 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3002 b
->bandwidth
= bytes
;
3005 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3010 int config_parse_netclass(
3012 const char *filename
,
3014 const char *section
,
3015 unsigned section_line
,
3022 CGroupContext
*c
= data
;
3030 if (streq(rvalue
, "auto")) {
3031 c
->netclass_type
= CGROUP_NETCLASS_TYPE_AUTO
;
3035 r
= safe_atou32(rvalue
, &v
);
3037 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3038 "Netclass '%s' invalid. Ignoring.", rvalue
);
3042 if (v
> CGROUP_NETCLASS_FIXED_MAX
)
3043 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3044 "Fixed netclass %" PRIu32
" out of allowed range (0-%d). Applying anyway.", v
, (uint32_t) CGROUP_NETCLASS_FIXED_MAX
);
3047 c
->netclass_type
= CGROUP_NETCLASS_TYPE_FIXED
;
3052 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3054 int config_parse_job_mode_isolate(
3056 const char *filename
,
3058 const char *section
,
3059 unsigned section_line
,
3073 r
= parse_boolean(rvalue
);
3075 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3076 "Failed to parse boolean, ignoring: %s", rvalue
);
3080 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3084 int config_parse_runtime_directory(
3086 const char *filename
,
3088 const char *section
,
3089 unsigned section_line
,
3098 const char *word
, *state
;
3107 if (isempty(rvalue
)) {
3108 /* Empty assignment resets the list */
3109 *rt
= strv_free(*rt
);
3113 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3114 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3116 t
= strndup(word
, l
);
3120 r
= unit_name_printf(u
, t
, &n
);
3122 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3123 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
3127 if (!filename_is_valid(n
)) {
3128 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3129 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3133 r
= strv_push(rt
, n
);
3139 if (!isempty(state
))
3140 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3141 "Trailing garbage, ignoring.");
3146 int config_parse_set_status(
3148 const char *filename
,
3150 const char *section
,
3151 unsigned section_line
,
3159 const char *word
, *state
;
3161 ExitStatusSet
*status_set
= data
;
3168 /* Empty assignment resets the list */
3169 if (isempty(rvalue
)) {
3170 exit_status_set_free(status_set
);
3174 FOREACH_WORD(word
, l
, rvalue
, state
) {
3175 _cleanup_free_
char *temp
;
3179 temp
= strndup(word
, l
);
3183 r
= safe_atoi(temp
, &val
);
3185 val
= signal_from_string_try_harder(temp
);
3188 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3189 "Failed to parse value, ignoring: %s", word
);
3192 set
= &status_set
->signal
;
3194 if (val
< 0 || val
> 255) {
3195 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3196 "Value %d is outside range 0-255, ignoring", val
);
3199 set
= &status_set
->status
;
3202 r
= set_ensure_allocated(set
, NULL
);
3206 r
= set_put(*set
, INT_TO_PTR(val
));
3208 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3209 "Unable to store: %s", word
);
3213 if (!isempty(state
))
3214 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3215 "Trailing garbage, ignoring.");
3220 int config_parse_namespace_path_strv(
3222 const char *filename
,
3224 const char *section
,
3225 unsigned section_line
,
3233 const char *word
, *state
;
3242 if (isempty(rvalue
)) {
3243 /* Empty assignment resets the list */
3244 *sv
= strv_free(*sv
);
3248 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3249 _cleanup_free_
char *n
;
3252 n
= strndup(word
, l
);
3256 if (!utf8_is_valid(n
)) {
3257 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3261 offset
= n
[0] == '-';
3262 if (!path_is_absolute(n
+ offset
)) {
3263 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3264 "Not an absolute path, ignoring: %s", rvalue
);
3268 path_kill_slashes(n
);
3270 r
= strv_push(sv
, n
);
3276 if (!isempty(state
))
3277 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3278 "Trailing garbage, ignoring.");
3283 int config_parse_no_new_privileges(
3285 const char *filename
,
3287 const char *section
,
3288 unsigned section_line
,
3295 ExecContext
*c
= data
;
3303 k
= parse_boolean(rvalue
);
3305 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3306 "Failed to parse boolean value, ignoring: %s", rvalue
);
3310 c
->no_new_privileges
= !!k
;
3311 c
->no_new_privileges_set
= true;
3316 int config_parse_protect_home(
3318 const char *filename
,
3320 const char *section
,
3321 unsigned section_line
,
3328 ExecContext
*c
= data
;
3336 /* Our enum shall be a superset of booleans, hence first try
3337 * to parse as as boolean, and then as enum */
3339 k
= parse_boolean(rvalue
);
3341 c
->protect_home
= PROTECT_HOME_YES
;
3343 c
->protect_home
= PROTECT_HOME_NO
;
3347 h
= protect_home_from_string(rvalue
);
3349 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3350 "Failed to parse protect home value, ignoring: %s", rvalue
);
3354 c
->protect_home
= h
;
3360 int config_parse_protect_system(
3362 const char *filename
,
3364 const char *section
,
3365 unsigned section_line
,
3372 ExecContext
*c
= data
;
3380 /* Our enum shall be a superset of booleans, hence first try
3381 * to parse as as boolean, and then as enum */
3383 k
= parse_boolean(rvalue
);
3385 c
->protect_system
= PROTECT_SYSTEM_YES
;
3387 c
->protect_system
= PROTECT_SYSTEM_NO
;
3391 s
= protect_system_from_string(rvalue
);
3393 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3394 "Failed to parse protect system value, ignoring: %s", rvalue
);
3398 c
->protect_system
= s
;
3404 #define FOLLOW_MAX 8
3406 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3417 /* This will update the filename pointer if the loaded file is
3418 * reached by a symlink. The old string will be freed. */
3421 char *target
, *name
;
3423 if (c
++ >= FOLLOW_MAX
)
3426 path_kill_slashes(*filename
);
3428 /* Add the file name we are currently looking at to
3429 * the names of this unit, but only if it is a valid
3431 name
= basename(*filename
);
3433 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3435 id
= set_get(names
, name
);
3441 r
= set_consume(names
, id
);
3447 /* Try to open the file name, but don't if its a symlink */
3448 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3455 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3456 r
= readlink_and_make_absolute(*filename
, &target
);
3464 f
= fdopen(fd
, "re");
3475 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3483 /* Let's try to add in all symlink names we found */
3484 while ((k
= set_steal_first(names
))) {
3486 /* First try to merge in the other name into our
3488 r
= unit_merge_by_name(*u
, k
);
3492 /* Hmm, we couldn't merge the other unit into
3493 * ours? Then let's try it the other way
3496 other
= manager_get_unit((*u
)->manager
, k
);
3500 r
= unit_merge(other
, *u
);
3503 return merge_by_names(u
, names
, NULL
);
3511 unit_choose_id(*u
, id
);
3519 static int load_from_path(Unit
*u
, const char *path
) {
3521 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3522 _cleanup_fclose_
FILE *f
= NULL
;
3523 _cleanup_free_
char *filename
= NULL
;
3531 symlink_names
= set_new(&string_hash_ops
);
3535 if (path_is_absolute(path
)) {
3537 filename
= strdup(path
);
3541 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3543 filename
= mfree(filename
);
3551 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3553 /* Instead of opening the path right away, we manually
3554 * follow all symlinks and add their name to our unit
3555 * name set while doing so */
3556 filename
= path_make_absolute(path
, *p
);
3560 if (u
->manager
->unit_path_cache
&&
3561 !set_get(u
->manager
->unit_path_cache
, filename
))
3564 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3567 filename
= mfree(filename
);
3571 /* Empty the symlink names for the next run */
3572 set_clear_free(symlink_names
);
3581 /* Hmm, no suitable file found? */
3585 r
= merge_by_names(&merged
, symlink_names
, id
);
3590 u
->load_state
= UNIT_MERGED
;
3594 if (fstat(fileno(f
), &st
) < 0)
3597 if (null_or_empty(&st
))
3598 u
->load_state
= UNIT_MASKED
;
3600 u
->load_state
= UNIT_LOADED
;
3602 /* Now, parse the file contents */
3603 r
= config_parse(u
->id
, filename
, f
,
3604 UNIT_VTABLE(u
)->sections
,
3605 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3606 false, true, false, u
);
3611 free(u
->fragment_path
);
3612 u
->fragment_path
= filename
;
3615 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3617 if (u
->source_path
) {
3618 if (stat(u
->source_path
, &st
) >= 0)
3619 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3621 u
->source_mtime
= 0;
3627 int unit_load_fragment(Unit
*u
) {
3633 assert(u
->load_state
== UNIT_STUB
);
3637 u
->load_state
= UNIT_LOADED
;
3641 /* First, try to find the unit under its id. We always look
3642 * for unit files in the default directories, to make it easy
3643 * to override things by placing things in /etc/systemd/system */
3644 r
= load_from_path(u
, u
->id
);
3648 /* Try to find an alias we can load this with */
3649 if (u
->load_state
== UNIT_STUB
) {
3650 SET_FOREACH(t
, u
->names
, i
) {
3655 r
= load_from_path(u
, t
);
3659 if (u
->load_state
!= UNIT_STUB
)
3664 /* And now, try looking for it under the suggested (originally linked) path */
3665 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3667 r
= load_from_path(u
, u
->fragment_path
);
3671 if (u
->load_state
== UNIT_STUB
)
3672 /* Hmm, this didn't work? Then let's get rid
3673 * of the fragment path stored for us, so that
3674 * we don't point to an invalid location. */
3675 u
->fragment_path
= mfree(u
->fragment_path
);
3678 /* Look for a template */
3679 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3680 _cleanup_free_
char *k
= NULL
;
3682 r
= unit_name_template(u
->id
, &k
);
3686 r
= load_from_path(u
, k
);
3690 if (u
->load_state
== UNIT_STUB
) {
3691 SET_FOREACH(t
, u
->names
, i
) {
3692 _cleanup_free_
char *z
= NULL
;
3697 r
= unit_name_template(t
, &z
);
3701 r
= load_from_path(u
, z
);
3705 if (u
->load_state
!= UNIT_STUB
)
3714 void unit_dump_config_items(FILE *f
) {
3715 static const struct {
3716 const ConfigParserCallback callback
;
3719 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3720 { config_parse_warn_compat
, "NOTSUPPORTED" },
3722 { config_parse_int
, "INTEGER" },
3723 { config_parse_unsigned
, "UNSIGNED" },
3724 { config_parse_iec_size
, "SIZE" },
3725 { config_parse_iec_uint64
, "SIZE" },
3726 { config_parse_si_size
, "SIZE" },
3727 { config_parse_bool
, "BOOLEAN" },
3728 { config_parse_string
, "STRING" },
3729 { config_parse_path
, "PATH" },
3730 { config_parse_unit_path_printf
, "PATH" },
3731 { config_parse_strv
, "STRING [...]" },
3732 { config_parse_exec_nice
, "NICE" },
3733 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3734 { config_parse_exec_io_class
, "IOCLASS" },
3735 { config_parse_exec_io_priority
, "IOPRIORITY" },
3736 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3737 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3738 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3739 { config_parse_mode
, "MODE" },
3740 { config_parse_unit_env_file
, "FILE" },
3741 { config_parse_output
, "OUTPUT" },
3742 { config_parse_input
, "INPUT" },
3743 { config_parse_log_facility
, "FACILITY" },
3744 { config_parse_log_level
, "LEVEL" },
3745 { config_parse_exec_capabilities
, "CAPABILITIES" },
3746 { config_parse_exec_secure_bits
, "SECUREBITS" },
3747 { config_parse_bounding_set
, "BOUNDINGSET" },
3748 { config_parse_limit
, "LIMIT" },
3749 { config_parse_unit_deps
, "UNIT [...]" },
3750 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3751 { config_parse_service_type
, "SERVICETYPE" },
3752 { config_parse_service_restart
, "SERVICERESTART" },
3753 #ifdef HAVE_SYSV_COMPAT
3754 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3756 { config_parse_kill_mode
, "KILLMODE" },
3757 { config_parse_signal
, "SIGNAL" },
3758 { config_parse_socket_listen
, "SOCKET [...]" },
3759 { config_parse_socket_bind
, "SOCKETBIND" },
3760 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3761 { config_parse_sec
, "SECONDS" },
3762 { config_parse_nsec
, "NANOSECONDS" },
3763 { config_parse_namespace_path_strv
, "PATH [...]" },
3764 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3765 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3766 { config_parse_unit_string_printf
, "STRING" },
3767 { config_parse_trigger_unit
, "UNIT" },
3768 { config_parse_timer
, "TIMER" },
3769 { config_parse_path_spec
, "PATH" },
3770 { config_parse_notify_access
, "ACCESS" },
3771 { config_parse_ip_tos
, "TOS" },
3772 { config_parse_unit_condition_path
, "CONDITION" },
3773 { config_parse_unit_condition_string
, "CONDITION" },
3774 { config_parse_unit_condition_null
, "CONDITION" },
3775 { config_parse_unit_slice
, "SLICE" },
3776 { config_parse_documentation
, "URL" },
3777 { config_parse_service_timeout
, "SECONDS" },
3778 { config_parse_failure_action
, "ACTION" },
3779 { config_parse_set_status
, "STATUS" },
3780 { config_parse_service_sockets
, "SOCKETS" },
3781 { config_parse_environ
, "ENVIRON" },
3783 { config_parse_syscall_filter
, "SYSCALLS" },
3784 { config_parse_syscall_archs
, "ARCHS" },
3785 { config_parse_syscall_errno
, "ERRNO" },
3786 { config_parse_address_families
, "FAMILIES" },
3788 { config_parse_cpu_shares
, "SHARES" },
3789 { config_parse_memory_limit
, "LIMIT" },
3790 { config_parse_device_allow
, "DEVICE" },
3791 { config_parse_device_policy
, "POLICY" },
3792 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3793 { config_parse_blockio_weight
, "WEIGHT" },
3794 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3795 { config_parse_long
, "LONG" },
3796 { config_parse_socket_service
, "SERVICE" },
3798 { config_parse_exec_selinux_context
, "LABEL" },
3800 { config_parse_job_mode
, "MODE" },
3801 { config_parse_job_mode_isolate
, "BOOLEAN" },
3802 { config_parse_personality
, "PERSONALITY" },
3805 const char *prev
= NULL
;
3810 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3811 const char *rvalue
= "OTHER", *lvalue
;
3815 const ConfigPerfItem
*p
;
3817 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3819 dot
= strchr(i
, '.');
3820 lvalue
= dot
? dot
+ 1 : i
;
3824 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3828 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3831 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3832 if (p
->parse
== table
[j
].callback
) {
3833 rvalue
= table
[j
].rvalue
;
3837 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);