1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
7 Copyright 2012 Holger Hans Peter Freyther
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <linux/oom.h>
32 #include <sys/resource.h>
36 #include "bus-error.h"
37 #include "bus-internal.h"
41 #include "conf-parser.h"
42 #include "cpu-set-util.h"
44 #include "errno-list.h"
48 #include "load-fragment.h"
51 #include "parse-util.h"
52 #include "path-util.h"
54 #include "seccomp-util.h"
56 #include "securebits.h"
57 #include "signal-util.h"
58 #include "string-util.h"
60 #include "unit-name.h"
61 #include "unit-printf.h"
65 int config_parse_warn_compat(
70 unsigned section_line
,
76 Disabled reason
= ltype
;
79 case DISABLED_CONFIGURATION
:
80 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
81 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
84 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
85 "Support for option %s= has been removed and it is ignored", lvalue
);
87 case DISABLED_EXPERIMENTAL
:
88 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
89 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
96 int config_parse_unit_deps(const char *unit
,
100 unsigned section_line
,
107 UnitDependency d
= ltype
;
109 const char *word
, *state
;
116 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
117 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
120 t
= strndup(word
, l
);
124 r
= unit_name_printf(u
, t
, &k
);
126 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
130 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
132 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
135 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring.");
140 int config_parse_unit_string_printf(
142 const char *filename
,
145 unsigned section_line
,
152 _cleanup_free_
char *k
= NULL
;
161 r
= unit_full_printf(u
, rvalue
, &k
);
163 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
167 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
170 int config_parse_unit_strv_printf(
172 const char *filename
,
175 unsigned section_line
,
183 _cleanup_free_
char *k
= NULL
;
191 r
= unit_full_printf(u
, rvalue
, &k
);
193 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
197 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
200 int config_parse_unit_path_printf(
202 const char *filename
,
205 unsigned section_line
,
212 _cleanup_free_
char *k
= NULL
;
221 r
= unit_full_printf(u
, rvalue
, &k
);
223 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
227 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
230 int config_parse_unit_path_strv_printf(
232 const char *filename
,
235 unsigned section_line
,
243 const char *word
, *state
;
253 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
254 _cleanup_free_
char *k
= NULL
;
260 r
= unit_full_printf(u
, t
, &k
);
262 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", t
);
266 if (!utf8_is_valid(k
)) {
267 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
271 if (!path_is_absolute(k
)) {
272 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Symlink path %s is not absolute, ignoring: %m", k
);
276 path_kill_slashes(k
);
285 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring.");
290 int config_parse_socket_listen(const char *unit
,
291 const char *filename
,
294 unsigned section_line
,
301 _cleanup_free_ SocketPort
*p
= NULL
;
313 if (isempty(rvalue
)) {
314 /* An empty assignment removes all ports */
315 socket_free_ports(s
);
319 p
= new0(SocketPort
, 1);
323 if (ltype
!= SOCKET_SOCKET
) {
326 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
328 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
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
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
344 r
= socket_address_parse_netlink(&p
->address
, k
);
346 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "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
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
360 r
= socket_address_parse_and_warn(&p
->address
, k
);
362 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "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
, 0, "Address family not supported, ignoring: %s", rvalue
);
382 p
->auxiliary_fds
= NULL
;
383 p
->n_auxiliary_fds
= 0;
387 LIST_FIND_TAIL(port
, s
->ports
, tail
);
388 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
390 LIST_PREPEND(port
, s
->ports
, p
);
396 int config_parse_socket_bind(const char *unit
,
397 const char *filename
,
400 unsigned section_line
,
408 SocketAddressBindIPv6Only b
;
417 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
421 r
= parse_boolean(rvalue
);
423 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
427 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
429 s
->bind_ipv6_only
= b
;
434 int config_parse_exec_nice(const char *unit
,
435 const char *filename
,
438 unsigned section_line
,
445 ExecContext
*c
= data
;
453 r
= safe_atoi(rvalue
, &priority
);
455 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
459 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
460 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Nice priority out of range, ignoring: %s", rvalue
);
470 int config_parse_exec_oom_score_adjust(const char* unit
,
471 const char *filename
,
474 unsigned section_line
,
481 ExecContext
*c
= data
;
489 r
= safe_atoi(rvalue
, &oa
);
491 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
495 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
496 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
500 c
->oom_score_adjust
= oa
;
501 c
->oom_score_adjust_set
= true;
506 int config_parse_exec(
508 const char *filename
,
511 unsigned section_line
,
518 ExecCommand
**e
= data
;
529 rvalue
+= strspn(rvalue
, WHITESPACE
);
531 if (isempty(rvalue
)) {
532 /* An empty assignment resets the list */
533 *e
= exec_command_free_list(*e
);
539 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
540 bool separate_argv0
= false, ignore
= false;
541 _cleanup_free_ ExecCommand
*nce
= NULL
;
542 _cleanup_strv_free_
char **n
= NULL
;
543 size_t nlen
= 0, nbufsize
= 0;
549 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
554 for (i
= 0; i
< 2; i
++) {
555 /* We accept an absolute path as first argument, or
556 * alternatively an absolute prefixed with @ to allow
557 * overriding of argv[0]. */
558 if (*f
== '-' && !ignore
)
560 else if (*f
== '@' && !separate_argv0
)
561 separate_argv0
= true;
568 /* First word is either "-" or "@" with no command. */
569 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty path in command line, ignoring: \"%s\"", rvalue
);
572 if (!string_is_safe(f
)) {
573 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path contains special characters, ignoring: %s", rvalue
);
576 if (!path_is_absolute(f
)) {
577 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path is not absolute, ignoring: %s", rvalue
);
580 if (endswith(f
, "/")) {
581 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path specifies a directory, ignoring: %s", rvalue
);
585 if (f
== firstword
) {
594 if (!separate_argv0
) {
595 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
604 path_kill_slashes(path
);
606 while (!isempty(p
)) {
607 _cleanup_free_
char *word
= NULL
;
609 /* Check explicitly for an unquoted semicolon as
610 * command separator token. */
611 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
613 p
+= strspn(p
, WHITESPACE
);
618 /* Check for \; explicitly, to not confuse it with \\;
619 * or "\;" or "\\;" etc. extract_first_word would
620 * return the same for all of those. */
621 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
623 p
+= strspn(p
, WHITESPACE
);
624 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
634 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
640 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
648 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
652 nce
= new0(ExecCommand
, 1);
658 nce
->ignore
= ignore
;
660 exec_command_append_list(e
, nce
);
662 /* Do not _cleanup_free_ these. */
673 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
674 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
676 int config_parse_socket_bindtodevice(const char* unit
,
677 const char *filename
,
680 unsigned section_line
,
695 if (rvalue
[0] && !streq(rvalue
, "*")) {
702 free(s
->bind_to_device
);
703 s
->bind_to_device
= n
;
708 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
709 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
711 int config_parse_exec_io_class(const char *unit
,
712 const char *filename
,
715 unsigned section_line
,
722 ExecContext
*c
= data
;
730 x
= ioprio_class_from_string(rvalue
);
732 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
736 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
737 c
->ioprio_set
= true;
742 int config_parse_exec_io_priority(const char *unit
,
743 const char *filename
,
746 unsigned section_line
,
753 ExecContext
*c
= data
;
761 r
= safe_atoi(rvalue
, &i
);
762 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
763 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
767 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
768 c
->ioprio_set
= true;
773 int config_parse_exec_cpu_sched_policy(const char *unit
,
774 const char *filename
,
777 unsigned section_line
,
785 ExecContext
*c
= data
;
793 x
= sched_policy_from_string(rvalue
);
795 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
799 c
->cpu_sched_policy
= x
;
800 /* Moving to or from real-time policy? We need to adjust the priority */
801 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
802 c
->cpu_sched_set
= true;
807 int config_parse_exec_cpu_sched_prio(const char *unit
,
808 const char *filename
,
811 unsigned section_line
,
818 ExecContext
*c
= data
;
826 r
= safe_atoi(rvalue
, &i
);
828 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
832 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
833 min
= sched_get_priority_min(c
->cpu_sched_policy
);
834 max
= sched_get_priority_max(c
->cpu_sched_policy
);
836 if (i
< min
|| i
> max
) {
837 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
841 c
->cpu_sched_priority
= i
;
842 c
->cpu_sched_set
= true;
847 int config_parse_exec_cpu_affinity(const char *unit
,
848 const char *filename
,
851 unsigned section_line
,
858 ExecContext
*c
= data
;
859 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
867 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
875 /* An empty assignment resets the CPU list */
881 c
->cpuset_ncpus
= ncpus
;
886 int config_parse_exec_capabilities(const char *unit
,
887 const char *filename
,
890 unsigned section_line
,
897 ExecContext
*c
= data
;
905 cap
= cap_from_text(rvalue
);
907 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
, "Failed to parse capabilities, ignoring: %s", rvalue
);
912 cap_free(c
->capabilities
);
913 c
->capabilities
= cap
;
918 int config_parse_exec_secure_bits(const char *unit
,
919 const char *filename
,
922 unsigned section_line
,
929 ExecContext
*c
= data
;
931 const char *word
, *state
;
938 if (isempty(rvalue
)) {
939 /* An empty assignment resets the field */
944 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
945 if (first_word(word
, "keep-caps"))
946 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
947 else if (first_word(word
, "keep-caps-locked"))
948 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
949 else if (first_word(word
, "no-setuid-fixup"))
950 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
951 else if (first_word(word
, "no-setuid-fixup-locked"))
952 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
953 else if (first_word(word
, "noroot"))
954 c
->secure_bits
|= 1<<SECURE_NOROOT
;
955 else if (first_word(word
, "noroot-locked"))
956 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
958 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse secure bits, ignoring: %s", rvalue
);
963 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, garbage at the end, ignoring.");
968 int config_parse_bounding_set(const char *unit
,
969 const char *filename
,
972 unsigned section_line
,
979 uint64_t *capability_bounding_set_drop
= data
;
980 const char *word
, *state
;
990 if (rvalue
[0] == '~') {
995 /* Note that we store this inverted internally, since the
996 * kernel wants it like this. But we actually expose it
997 * non-inverted everywhere to have a fully normalized
1000 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1001 _cleanup_free_
char *t
= NULL
;
1004 t
= strndup(word
, l
);
1008 cap
= capability_from_name(t
);
1010 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability in bounding set, ignoring: %s", t
);
1014 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1016 if (!isempty(state
))
1017 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1020 *capability_bounding_set_drop
|= sum
;
1022 *capability_bounding_set_drop
|= ~sum
;
1027 int config_parse_limit(const char *unit
,
1028 const char *filename
,
1030 const char *section
,
1031 unsigned section_line
,
1038 struct rlimit
**rl
= data
;
1039 unsigned long long u
;
1048 if (streq(rvalue
, "infinity"))
1049 u
= (unsigned long long) RLIM_INFINITY
;
1053 r
= safe_atollu(rvalue
, &u
);
1055 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1061 *rl
= new(struct rlimit
, 1);
1066 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1070 #ifdef HAVE_SYSV_COMPAT
1071 int config_parse_sysv_priority(const char *unit
,
1072 const char *filename
,
1074 const char *section
,
1075 unsigned section_line
,
1082 int *priority
= data
;
1090 r
= safe_atoi(rvalue
, &i
);
1091 if (r
< 0 || i
< 0) {
1092 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1096 *priority
= (int) i
;
1101 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1102 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1104 int config_parse_exec_mount_flags(const char *unit
,
1105 const char *filename
,
1107 const char *section
,
1108 unsigned section_line
,
1115 ExecContext
*c
= data
;
1116 const char *word
, *state
;
1118 unsigned long flags
= 0;
1125 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1126 _cleanup_free_
char *t
;
1128 t
= strndup(word
, l
);
1132 if (streq(t
, "shared"))
1134 else if (streq(t
, "slave"))
1136 else if (streq(t
, "private"))
1139 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1143 if (!isempty(state
))
1144 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1146 c
->mount_flags
= flags
;
1150 int config_parse_exec_selinux_context(
1152 const char *filename
,
1154 const char *section
,
1155 unsigned section_line
,
1162 ExecContext
*c
= data
;
1173 if (isempty(rvalue
)) {
1174 c
->selinux_context
= mfree(c
->selinux_context
);
1175 c
->selinux_context_ignore
= false;
1179 if (rvalue
[0] == '-') {
1185 r
= unit_name_printf(u
, rvalue
, &k
);
1187 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1191 free(c
->selinux_context
);
1192 c
->selinux_context
= k
;
1193 c
->selinux_context_ignore
= ignore
;
1198 int config_parse_exec_apparmor_profile(
1200 const char *filename
,
1202 const char *section
,
1203 unsigned section_line
,
1210 ExecContext
*c
= data
;
1221 if (isempty(rvalue
)) {
1222 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1223 c
->apparmor_profile_ignore
= false;
1227 if (rvalue
[0] == '-') {
1233 r
= unit_name_printf(u
, rvalue
, &k
);
1235 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1239 free(c
->apparmor_profile
);
1240 c
->apparmor_profile
= k
;
1241 c
->apparmor_profile_ignore
= ignore
;
1246 int config_parse_exec_smack_process_label(
1248 const char *filename
,
1250 const char *section
,
1251 unsigned section_line
,
1258 ExecContext
*c
= data
;
1269 if (isempty(rvalue
)) {
1270 c
->smack_process_label
= mfree(c
->smack_process_label
);
1271 c
->smack_process_label_ignore
= false;
1275 if (rvalue
[0] == '-') {
1281 r
= unit_name_printf(u
, rvalue
, &k
);
1283 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1287 free(c
->smack_process_label
);
1288 c
->smack_process_label
= k
;
1289 c
->smack_process_label_ignore
= ignore
;
1294 int config_parse_timer(const char *unit
,
1295 const char *filename
,
1297 const char *section
,
1298 unsigned section_line
,
1309 CalendarSpec
*c
= NULL
;
1316 if (isempty(rvalue
)) {
1317 /* Empty assignment resets list */
1318 timer_free_values(t
);
1322 b
= timer_base_from_string(lvalue
);
1324 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1328 if (b
== TIMER_CALENDAR
) {
1329 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1330 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", rvalue
);
1334 if (parse_sec(rvalue
, &u
) < 0) {
1335 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", rvalue
);
1340 v
= new0(TimerValue
, 1);
1342 calendar_spec_free(c
);
1348 v
->calendar_spec
= c
;
1350 LIST_PREPEND(value
, t
->values
, v
);
1355 int config_parse_trigger_unit(
1357 const char *filename
,
1359 const char *section
,
1360 unsigned section_line
,
1367 _cleanup_free_
char *p
= NULL
;
1377 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1378 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1382 r
= unit_name_printf(u
, rvalue
, &p
);
1384 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1388 type
= unit_name_to_type(p
);
1390 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1394 if (type
== u
->type
) {
1395 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1399 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1401 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1408 int config_parse_path_spec(const char *unit
,
1409 const char *filename
,
1411 const char *section
,
1412 unsigned section_line
,
1422 _cleanup_free_
char *k
= NULL
;
1430 if (isempty(rvalue
)) {
1431 /* Empty assignment clears list */
1436 b
= path_type_from_string(lvalue
);
1438 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1442 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1444 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1448 if (!path_is_absolute(k
)) {
1449 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1453 s
= new0(PathSpec
, 1);
1458 s
->path
= path_kill_slashes(k
);
1463 LIST_PREPEND(spec
, p
->specs
, s
);
1468 int config_parse_socket_service(
1470 const char *filename
,
1472 const char *section
,
1473 unsigned section_line
,
1480 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1481 _cleanup_free_
char *p
= NULL
;
1491 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1493 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1497 if (!endswith(p
, ".service")) {
1498 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1502 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1504 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1508 unit_ref_set(&s
->service
, x
);
1513 int config_parse_fdname(
1515 const char *filename
,
1517 const char *section
,
1518 unsigned section_line
,
1525 _cleanup_free_
char *p
= NULL
;
1534 if (isempty(rvalue
)) {
1535 s
->fdname
= mfree(s
->fdname
);
1539 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1541 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1545 if (!fdname_is_valid(p
)) {
1546 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1557 int config_parse_service_sockets(
1559 const char *filename
,
1561 const char *section
,
1562 unsigned section_line
,
1570 const char *word
, *state
;
1579 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1580 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1582 t
= strndup(word
, l
);
1586 r
= unit_name_printf(UNIT(s
), t
, &k
);
1588 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1592 if (!endswith(k
, ".socket")) {
1593 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1597 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1599 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1601 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1603 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1605 if (!isempty(state
))
1606 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1611 int config_parse_bus_name(
1613 const char *filename
,
1615 const char *section
,
1616 unsigned section_line
,
1623 _cleanup_free_
char *k
= NULL
;
1632 r
= unit_full_printf(u
, rvalue
, &k
);
1634 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1638 if (!service_name_is_valid(k
)) {
1639 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1643 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1646 int config_parse_service_timeout(const char *unit
,
1647 const char *filename
,
1649 const char *section
,
1650 unsigned section_line
,
1657 Service
*s
= userdata
;
1665 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1666 rvalue
, data
, userdata
);
1670 if (streq(lvalue
, "TimeoutSec")) {
1671 s
->start_timeout_defined
= true;
1672 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1673 } else if (streq(lvalue
, "TimeoutStartSec"))
1674 s
->start_timeout_defined
= true;
1679 int config_parse_busname_service(
1681 const char *filename
,
1683 const char *section
,
1684 unsigned section_line
,
1691 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1695 _cleanup_free_
char *p
= NULL
;
1702 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1704 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1708 if (!endswith(p
, ".service")) {
1709 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1713 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1715 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1719 unit_ref_set(&n
->service
, x
);
1724 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1726 int config_parse_bus_policy(
1728 const char *filename
,
1730 const char *section
,
1731 unsigned section_line
,
1738 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1739 _cleanup_free_
char *id_str
= NULL
;
1740 BusName
*busname
= data
;
1748 p
= new0(BusNamePolicy
, 1);
1752 if (streq(lvalue
, "AllowUser"))
1753 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1754 else if (streq(lvalue
, "AllowGroup"))
1755 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1757 assert_not_reached("Unknown lvalue");
1759 id_str
= strdup(rvalue
);
1763 access_str
= strpbrk(id_str
, WHITESPACE
);
1765 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy value '%s'", rvalue
);
1771 access_str
+= strspn(access_str
, WHITESPACE
);
1773 p
->access
= bus_policy_access_from_string(access_str
);
1774 if (p
->access
< 0) {
1775 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy access type '%s'", access_str
);
1782 LIST_PREPEND(policy
, busname
->policy
, p
);
1788 int config_parse_bus_endpoint_policy(
1790 const char *filename
,
1792 const char *section
,
1793 unsigned section_line
,
1800 _cleanup_free_
char *name
= NULL
;
1801 BusPolicyAccess access
;
1802 ExecContext
*c
= data
;
1811 name
= strdup(rvalue
);
1815 access_str
= strpbrk(name
, WHITESPACE
);
1817 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy value '%s'", rvalue
);
1823 access_str
+= strspn(access_str
, WHITESPACE
);
1825 access
= bus_policy_access_from_string(access_str
);
1826 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1827 access
>= _BUS_POLICY_ACCESS_MAX
) {
1828 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy access type '%s'", access_str
);
1832 if (!c
->bus_endpoint
) {
1833 r
= bus_endpoint_new(&c
->bus_endpoint
);
1835 return log_error_errno(r
, "Failed to create bus endpoint object: %m");
1838 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1841 int config_parse_working_directory(
1843 const char *filename
,
1845 const char *section
,
1846 unsigned section_line
,
1853 ExecContext
*c
= data
;
1864 if (rvalue
[0] == '-') {
1870 if (streq(rvalue
, "~")) {
1871 c
->working_directory_home
= true;
1872 c
->working_directory
= mfree(c
->working_directory
);
1874 _cleanup_free_
char *k
= NULL
;
1876 r
= unit_full_printf(u
, rvalue
, &k
);
1878 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
1882 path_kill_slashes(k
);
1884 if (!utf8_is_valid(k
)) {
1885 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
1889 if (!path_is_absolute(k
)) {
1890 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
1894 free(c
->working_directory
);
1895 c
->working_directory
= k
;
1898 c
->working_directory_home
= false;
1901 c
->working_directory_missing_ok
= missing_ok
;
1905 int config_parse_unit_env_file(const char *unit
,
1906 const char *filename
,
1908 const char *section
,
1909 unsigned section_line
,
1918 _cleanup_free_
char *n
= NULL
;
1926 if (isempty(rvalue
)) {
1927 /* Empty assignment frees the list */
1928 *env
= strv_free(*env
);
1932 r
= unit_full_printf(u
, rvalue
, &n
);
1934 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1938 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
1939 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
1943 r
= strv_extend(env
, n
);
1950 int config_parse_environ(const char *unit
,
1951 const char *filename
,
1953 const char *section
,
1954 unsigned section_line
,
1963 const char *word
, *state
;
1965 _cleanup_free_
char *k
= NULL
;
1973 if (isempty(rvalue
)) {
1974 /* Empty assignment resets the list */
1975 *env
= strv_free(*env
);
1980 r
= unit_full_printf(u
, rvalue
, &k
);
1982 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1993 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1994 _cleanup_free_
char *n
= NULL
;
1997 r
= cunescape_length(word
, l
, 0, &n
);
1999 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
2003 if (!env_assignment_is_valid(n
)) {
2004 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid environment assignment, ignoring: %s", rvalue
);
2008 x
= strv_env_set(*env
, n
);
2015 if (!isempty(state
))
2016 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2021 int config_parse_ip_tos(const char *unit
,
2022 const char *filename
,
2024 const char *section
,
2025 unsigned section_line
,
2032 int *ip_tos
= data
, x
;
2039 x
= ip_tos_from_string(rvalue
);
2041 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2049 int config_parse_unit_condition_path(
2051 const char *filename
,
2053 const char *section
,
2054 unsigned section_line
,
2061 _cleanup_free_
char *p
= NULL
;
2062 Condition
**list
= data
, *c
;
2063 ConditionType t
= ltype
;
2064 bool trigger
, negate
;
2073 if (isempty(rvalue
)) {
2074 /* Empty assignment resets the list */
2075 *list
= condition_free_list(*list
);
2079 trigger
= rvalue
[0] == '|';
2083 negate
= rvalue
[0] == '!';
2087 r
= unit_full_printf(u
, rvalue
, &p
);
2089 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2093 if (!path_is_absolute(p
)) {
2094 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2098 c
= condition_new(t
, p
, trigger
, negate
);
2102 LIST_PREPEND(conditions
, *list
, c
);
2106 int config_parse_unit_condition_string(
2108 const char *filename
,
2110 const char *section
,
2111 unsigned section_line
,
2118 _cleanup_free_
char *s
= NULL
;
2119 Condition
**list
= data
, *c
;
2120 ConditionType t
= ltype
;
2121 bool trigger
, negate
;
2130 if (isempty(rvalue
)) {
2131 /* Empty assignment resets the list */
2132 *list
= condition_free_list(*list
);
2136 trigger
= rvalue
[0] == '|';
2140 negate
= rvalue
[0] == '!';
2144 r
= unit_full_printf(u
, rvalue
, &s
);
2146 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2150 c
= condition_new(t
, s
, trigger
, negate
);
2154 LIST_PREPEND(conditions
, *list
, c
);
2158 int config_parse_unit_condition_null(
2160 const char *filename
,
2162 const char *section
,
2163 unsigned section_line
,
2170 Condition
**list
= data
, *c
;
2171 bool trigger
, negate
;
2179 if (isempty(rvalue
)) {
2180 /* Empty assignment resets the list */
2181 *list
= condition_free_list(*list
);
2185 trigger
= rvalue
[0] == '|';
2189 negate
= rvalue
[0] == '!';
2193 b
= parse_boolean(rvalue
);
2195 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2202 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2206 LIST_PREPEND(conditions
, *list
, c
);
2210 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2211 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2213 int config_parse_unit_requires_mounts_for(
2215 const char *filename
,
2217 const char *section
,
2218 unsigned section_line
,
2226 const char *word
, *state
;
2234 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2236 _cleanup_free_
char *n
;
2238 n
= strndup(word
, l
);
2242 if (!utf8_is_valid(n
)) {
2243 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2247 r
= unit_require_mounts_for(u
, n
);
2249 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount for, ignoring: %s", rvalue
);
2253 if (!isempty(state
))
2254 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2259 int config_parse_documentation(const char *unit
,
2260 const char *filename
,
2262 const char *section
,
2263 unsigned section_line
,
2279 if (isempty(rvalue
)) {
2280 /* Empty assignment resets the list */
2281 u
->documentation
= strv_free(u
->documentation
);
2285 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2286 rvalue
, data
, userdata
);
2290 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2292 if (documentation_url_is_valid(*a
))
2295 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2306 int config_parse_syscall_filter(
2308 const char *filename
,
2310 const char *section
,
2311 unsigned section_line
,
2318 static const char default_syscalls
[] =
2325 ExecContext
*c
= data
;
2327 bool invert
= false;
2328 const char *word
, *state
;
2337 if (isempty(rvalue
)) {
2338 /* Empty assignment resets the list */
2339 c
->syscall_filter
= set_free(c
->syscall_filter
);
2340 c
->syscall_whitelist
= false;
2344 if (rvalue
[0] == '~') {
2349 if (!c
->syscall_filter
) {
2350 c
->syscall_filter
= set_new(NULL
);
2351 if (!c
->syscall_filter
)
2355 /* Allow everything but the ones listed */
2356 c
->syscall_whitelist
= false;
2360 /* Allow nothing but the ones listed */
2361 c
->syscall_whitelist
= true;
2363 /* Accept default syscalls if we are on a whitelist */
2364 NULSTR_FOREACH(i
, default_syscalls
) {
2367 id
= seccomp_syscall_resolve_name(i
);
2371 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2380 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2381 _cleanup_free_
char *t
= NULL
;
2384 t
= strndup(word
, l
);
2388 id
= seccomp_syscall_resolve_name(t
);
2390 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2394 /* If we previously wanted to forbid a syscall and now
2395 * we want to allow it, then remove it from the list
2397 if (!invert
== c
->syscall_whitelist
) {
2398 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2404 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2406 if (!isempty(state
))
2407 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2409 /* Turn on NNP, but only if it wasn't configured explicitly
2410 * before, and only if we are in user mode. */
2411 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2412 c
->no_new_privileges
= true;
2417 int config_parse_syscall_archs(
2419 const char *filename
,
2421 const char *section
,
2422 unsigned section_line
,
2430 const char *word
, *state
;
2434 if (isempty(rvalue
)) {
2435 *archs
= set_free(*archs
);
2439 r
= set_ensure_allocated(archs
, NULL
);
2443 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2444 _cleanup_free_
char *t
= NULL
;
2447 t
= strndup(word
, l
);
2451 r
= seccomp_arch_from_string(t
, &a
);
2453 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call architecture, ignoring: %s", t
);
2457 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2463 if (!isempty(state
))
2464 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2469 int config_parse_syscall_errno(
2471 const char *filename
,
2473 const char *section
,
2474 unsigned section_line
,
2481 ExecContext
*c
= data
;
2488 if (isempty(rvalue
)) {
2489 /* Empty assignment resets to KILL */
2490 c
->syscall_errno
= 0;
2494 e
= errno_from_name(rvalue
);
2496 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2500 c
->syscall_errno
= e
;
2504 int config_parse_address_families(
2506 const char *filename
,
2508 const char *section
,
2509 unsigned section_line
,
2516 ExecContext
*c
= data
;
2517 bool invert
= false;
2518 const char *word
, *state
;
2526 if (isempty(rvalue
)) {
2527 /* Empty assignment resets the list */
2528 c
->address_families
= set_free(c
->address_families
);
2529 c
->address_families_whitelist
= false;
2533 if (rvalue
[0] == '~') {
2538 if (!c
->address_families
) {
2539 c
->address_families
= set_new(NULL
);
2540 if (!c
->address_families
)
2543 c
->address_families_whitelist
= !invert
;
2546 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2547 _cleanup_free_
char *t
= NULL
;
2550 t
= strndup(word
, l
);
2554 af
= af_from_name(t
);
2556 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse address family, ignoring: %s", t
);
2560 /* If we previously wanted to forbid an address family and now
2561 * we want to allow it, then remove it from the list
2563 if (!invert
== c
->address_families_whitelist
) {
2564 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2570 set_remove(c
->address_families
, INT_TO_PTR(af
));
2572 if (!isempty(state
))
2573 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2579 int config_parse_unit_slice(
2581 const char *filename
,
2583 const char *section
,
2584 unsigned section_line
,
2591 _cleanup_free_
char *k
= NULL
;
2592 Unit
*u
= userdata
, *slice
= NULL
;
2600 r
= unit_name_printf(u
, rvalue
, &k
);
2602 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2606 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2608 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2612 r
= unit_set_slice(u
, slice
);
2614 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2621 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2623 int config_parse_cpu_shares(
2625 const char *filename
,
2627 const char *section
,
2628 unsigned section_line
,
2635 uint64_t *shares
= data
;
2642 r
= cg_cpu_shares_parse(rvalue
, shares
);
2644 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2651 int config_parse_cpu_quota(
2653 const char *filename
,
2655 const char *section
,
2656 unsigned section_line
,
2663 CGroupContext
*c
= data
;
2670 if (isempty(rvalue
)) {
2671 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2675 if (!endswith(rvalue
, "%")) {
2676 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2680 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2681 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' invalid. Ignoring.", rvalue
);
2685 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2690 int config_parse_memory_limit(
2692 const char *filename
,
2694 const char *section
,
2695 unsigned section_line
,
2702 CGroupContext
*c
= data
;
2706 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2707 c
->memory_limit
= (uint64_t) -1;
2711 r
= parse_size(rvalue
, 1024, &bytes
);
2712 if (r
< 0 || bytes
< 1) {
2713 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2717 c
->memory_limit
= bytes
;
2721 int config_parse_tasks_max(
2723 const char *filename
,
2725 const char *section
,
2726 unsigned section_line
,
2733 CGroupContext
*c
= data
;
2737 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2738 c
->tasks_max
= (uint64_t) -1;
2742 r
= safe_atou64(rvalue
, &u
);
2743 if (r
< 0 || u
< 1) {
2744 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2752 int config_parse_device_allow(
2754 const char *filename
,
2756 const char *section
,
2757 unsigned section_line
,
2764 _cleanup_free_
char *path
= NULL
;
2765 CGroupContext
*c
= data
;
2766 CGroupDeviceAllow
*a
;
2770 if (isempty(rvalue
)) {
2771 while (c
->device_allow
)
2772 cgroup_context_free_device_allow(c
, c
->device_allow
);
2777 n
= strcspn(rvalue
, WHITESPACE
);
2778 path
= strndup(rvalue
, n
);
2782 if (!startswith(path
, "/dev/") &&
2783 !startswith(path
, "block-") &&
2784 !startswith(path
, "char-")) {
2785 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2789 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2793 if (!in_charset(m
, "rwm")) {
2794 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
2798 a
= new0(CGroupDeviceAllow
, 1);
2804 a
->r
= !!strchr(m
, 'r');
2805 a
->w
= !!strchr(m
, 'w');
2806 a
->m
= !!strchr(m
, 'm');
2808 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2812 int config_parse_blockio_weight(
2814 const char *filename
,
2816 const char *section
,
2817 unsigned section_line
,
2824 uint64_t *weight
= data
;
2831 r
= cg_blkio_weight_parse(rvalue
, weight
);
2833 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2840 int config_parse_blockio_device_weight(
2842 const char *filename
,
2844 const char *section
,
2845 unsigned section_line
,
2852 _cleanup_free_
char *path
= NULL
;
2853 CGroupBlockIODeviceWeight
*w
;
2854 CGroupContext
*c
= data
;
2864 if (isempty(rvalue
)) {
2865 while (c
->blockio_device_weights
)
2866 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2871 n
= strcspn(rvalue
, WHITESPACE
);
2872 weight
= rvalue
+ n
;
2873 weight
+= strspn(weight
, WHITESPACE
);
2875 if (isempty(weight
)) {
2876 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
2880 path
= strndup(rvalue
, n
);
2884 if (!path_startswith(path
, "/dev")) {
2885 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2889 r
= cg_blkio_weight_parse(weight
, &u
);
2891 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2895 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2897 w
= new0(CGroupBlockIODeviceWeight
, 1);
2906 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2910 int config_parse_blockio_bandwidth(
2912 const char *filename
,
2914 const char *section
,
2915 unsigned section_line
,
2922 _cleanup_free_
char *path
= NULL
;
2923 CGroupBlockIODeviceBandwidth
*b
;
2924 CGroupContext
*c
= data
;
2925 const char *bandwidth
;
2935 read
= streq("BlockIOReadBandwidth", lvalue
);
2937 if (isempty(rvalue
)) {
2938 CGroupBlockIODeviceBandwidth
*next
;
2940 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2941 if (b
->read
== read
)
2942 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2947 n
= strcspn(rvalue
, WHITESPACE
);
2948 bandwidth
= rvalue
+ n
;
2949 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2952 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
2956 path
= strndup(rvalue
, n
);
2960 if (!path_startswith(path
, "/dev")) {
2961 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2965 r
= parse_size(bandwidth
, 1000, &bytes
);
2966 if (r
< 0 || bytes
<= 0) {
2967 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2971 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2977 b
->bandwidth
= bytes
;
2980 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2985 int config_parse_netclass(
2987 const char *filename
,
2989 const char *section
,
2990 unsigned section_line
,
2997 CGroupContext
*c
= data
;
3005 if (streq(rvalue
, "auto")) {
3006 c
->netclass_type
= CGROUP_NETCLASS_TYPE_AUTO
;
3010 r
= safe_atou32(rvalue
, &v
);
3012 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Netclass '%s' invalid. Ignoring.", rvalue
);
3016 if (v
> CGROUP_NETCLASS_FIXED_MAX
)
3017 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3018 "Fixed netclass %" PRIu32
" out of allowed range (0-%d). Applying anyway.", v
, (uint32_t) CGROUP_NETCLASS_FIXED_MAX
);
3021 c
->netclass_type
= CGROUP_NETCLASS_TYPE_FIXED
;
3026 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3028 int config_parse_job_mode_isolate(
3030 const char *filename
,
3032 const char *section
,
3033 unsigned section_line
,
3047 r
= parse_boolean(rvalue
);
3049 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3053 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3057 int config_parse_runtime_directory(
3059 const char *filename
,
3061 const char *section
,
3062 unsigned section_line
,
3071 const char *word
, *state
;
3080 if (isempty(rvalue
)) {
3081 /* Empty assignment resets the list */
3082 *rt
= strv_free(*rt
);
3086 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3087 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3089 t
= strndup(word
, l
);
3093 r
= unit_name_printf(u
, t
, &n
);
3095 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
3099 if (!filename_is_valid(n
)) {
3100 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3104 r
= strv_push(rt
, n
);
3110 if (!isempty(state
))
3111 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3116 int config_parse_set_status(
3118 const char *filename
,
3120 const char *section
,
3121 unsigned section_line
,
3129 const char *word
, *state
;
3131 ExitStatusSet
*status_set
= data
;
3138 /* Empty assignment resets the list */
3139 if (isempty(rvalue
)) {
3140 exit_status_set_free(status_set
);
3144 FOREACH_WORD(word
, l
, rvalue
, state
) {
3145 _cleanup_free_
char *temp
;
3149 temp
= strndup(word
, l
);
3153 r
= safe_atoi(temp
, &val
);
3155 val
= signal_from_string_try_harder(temp
);
3158 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3161 set
= &status_set
->signal
;
3163 if (val
< 0 || val
> 255) {
3164 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3167 set
= &status_set
->status
;
3170 r
= set_ensure_allocated(set
, NULL
);
3174 r
= set_put(*set
, INT_TO_PTR(val
));
3176 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3180 if (!isempty(state
))
3181 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3186 int config_parse_namespace_path_strv(
3188 const char *filename
,
3190 const char *section
,
3191 unsigned section_line
,
3208 if (isempty(rvalue
)) {
3209 /* Empty assignment resets the list */
3210 *sv
= strv_free(*sv
);
3214 prev
= cur
= rvalue
;
3216 _cleanup_free_
char *word
= NULL
;
3219 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3221 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring: %s", prev
);
3227 if (!utf8_is_valid(word
)) {
3228 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3233 offset
= word
[0] == '-';
3234 if (!path_is_absolute(word
+ offset
)) {
3235 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", word
);
3240 path_kill_slashes(word
+ offset
);
3242 r
= strv_push(sv
, word
);
3253 int config_parse_no_new_privileges(
3255 const char *filename
,
3257 const char *section
,
3258 unsigned section_line
,
3265 ExecContext
*c
= data
;
3273 k
= parse_boolean(rvalue
);
3275 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3279 c
->no_new_privileges
= !!k
;
3280 c
->no_new_privileges_set
= true;
3285 int config_parse_protect_home(
3287 const char *filename
,
3289 const char *section
,
3290 unsigned section_line
,
3297 ExecContext
*c
= data
;
3305 /* Our enum shall be a superset of booleans, hence first try
3306 * to parse as as boolean, and then as enum */
3308 k
= parse_boolean(rvalue
);
3310 c
->protect_home
= PROTECT_HOME_YES
;
3312 c
->protect_home
= PROTECT_HOME_NO
;
3316 h
= protect_home_from_string(rvalue
);
3318 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
3322 c
->protect_home
= h
;
3328 int config_parse_protect_system(
3330 const char *filename
,
3332 const char *section
,
3333 unsigned section_line
,
3340 ExecContext
*c
= data
;
3348 /* Our enum shall be a superset of booleans, hence first try
3349 * to parse as as boolean, and then as enum */
3351 k
= parse_boolean(rvalue
);
3353 c
->protect_system
= PROTECT_SYSTEM_YES
;
3355 c
->protect_system
= PROTECT_SYSTEM_NO
;
3359 s
= protect_system_from_string(rvalue
);
3361 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
3365 c
->protect_system
= s
;
3371 #define FOLLOW_MAX 8
3373 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3384 /* This will update the filename pointer if the loaded file is
3385 * reached by a symlink. The old string will be freed. */
3388 char *target
, *name
;
3390 if (c
++ >= FOLLOW_MAX
)
3393 path_kill_slashes(*filename
);
3395 /* Add the file name we are currently looking at to
3396 * the names of this unit, but only if it is a valid
3398 name
= basename(*filename
);
3400 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3402 id
= set_get(names
, name
);
3408 r
= set_consume(names
, id
);
3414 /* Try to open the file name, but don't if its a symlink */
3415 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3422 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3423 r
= readlink_and_make_absolute(*filename
, &target
);
3431 f
= fdopen(fd
, "re");
3442 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3450 /* Let's try to add in all symlink names we found */
3451 while ((k
= set_steal_first(names
))) {
3453 /* First try to merge in the other name into our
3455 r
= unit_merge_by_name(*u
, k
);
3459 /* Hmm, we couldn't merge the other unit into
3460 * ours? Then let's try it the other way
3463 other
= manager_get_unit((*u
)->manager
, k
);
3467 r
= unit_merge(other
, *u
);
3470 return merge_by_names(u
, names
, NULL
);
3478 unit_choose_id(*u
, id
);
3486 static int load_from_path(Unit
*u
, const char *path
) {
3488 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3489 _cleanup_fclose_
FILE *f
= NULL
;
3490 _cleanup_free_
char *filename
= NULL
;
3498 symlink_names
= set_new(&string_hash_ops
);
3502 if (path_is_absolute(path
)) {
3504 filename
= strdup(path
);
3508 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3510 filename
= mfree(filename
);
3518 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3520 /* Instead of opening the path right away, we manually
3521 * follow all symlinks and add their name to our unit
3522 * name set while doing so */
3523 filename
= path_make_absolute(path
, *p
);
3527 if (u
->manager
->unit_path_cache
&&
3528 !set_get(u
->manager
->unit_path_cache
, filename
))
3531 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3534 filename
= mfree(filename
);
3538 /* Empty the symlink names for the next run */
3539 set_clear_free(symlink_names
);
3548 /* Hmm, no suitable file found? */
3552 r
= merge_by_names(&merged
, symlink_names
, id
);
3557 u
->load_state
= UNIT_MERGED
;
3561 if (fstat(fileno(f
), &st
) < 0)
3564 if (null_or_empty(&st
))
3565 u
->load_state
= UNIT_MASKED
;
3567 u
->load_state
= UNIT_LOADED
;
3569 /* Now, parse the file contents */
3570 r
= config_parse(u
->id
, filename
, f
,
3571 UNIT_VTABLE(u
)->sections
,
3572 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3573 false, true, false, u
);
3578 free(u
->fragment_path
);
3579 u
->fragment_path
= filename
;
3582 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3584 if (u
->source_path
) {
3585 if (stat(u
->source_path
, &st
) >= 0)
3586 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3588 u
->source_mtime
= 0;
3594 int unit_load_fragment(Unit
*u
) {
3600 assert(u
->load_state
== UNIT_STUB
);
3604 u
->load_state
= UNIT_LOADED
;
3608 /* First, try to find the unit under its id. We always look
3609 * for unit files in the default directories, to make it easy
3610 * to override things by placing things in /etc/systemd/system */
3611 r
= load_from_path(u
, u
->id
);
3615 /* Try to find an alias we can load this with */
3616 if (u
->load_state
== UNIT_STUB
) {
3617 SET_FOREACH(t
, u
->names
, i
) {
3622 r
= load_from_path(u
, t
);
3626 if (u
->load_state
!= UNIT_STUB
)
3631 /* And now, try looking for it under the suggested (originally linked) path */
3632 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3634 r
= load_from_path(u
, u
->fragment_path
);
3638 if (u
->load_state
== UNIT_STUB
)
3639 /* Hmm, this didn't work? Then let's get rid
3640 * of the fragment path stored for us, so that
3641 * we don't point to an invalid location. */
3642 u
->fragment_path
= mfree(u
->fragment_path
);
3645 /* Look for a template */
3646 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3647 _cleanup_free_
char *k
= NULL
;
3649 r
= unit_name_template(u
->id
, &k
);
3653 r
= load_from_path(u
, k
);
3657 if (u
->load_state
== UNIT_STUB
) {
3658 SET_FOREACH(t
, u
->names
, i
) {
3659 _cleanup_free_
char *z
= NULL
;
3664 r
= unit_name_template(t
, &z
);
3668 r
= load_from_path(u
, z
);
3672 if (u
->load_state
!= UNIT_STUB
)
3681 void unit_dump_config_items(FILE *f
) {
3682 static const struct {
3683 const ConfigParserCallback callback
;
3686 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3687 { config_parse_warn_compat
, "NOTSUPPORTED" },
3689 { config_parse_int
, "INTEGER" },
3690 { config_parse_unsigned
, "UNSIGNED" },
3691 { config_parse_iec_size
, "SIZE" },
3692 { config_parse_iec_uint64
, "SIZE" },
3693 { config_parse_si_size
, "SIZE" },
3694 { config_parse_bool
, "BOOLEAN" },
3695 { config_parse_string
, "STRING" },
3696 { config_parse_path
, "PATH" },
3697 { config_parse_unit_path_printf
, "PATH" },
3698 { config_parse_strv
, "STRING [...]" },
3699 { config_parse_exec_nice
, "NICE" },
3700 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3701 { config_parse_exec_io_class
, "IOCLASS" },
3702 { config_parse_exec_io_priority
, "IOPRIORITY" },
3703 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3704 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3705 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3706 { config_parse_mode
, "MODE" },
3707 { config_parse_unit_env_file
, "FILE" },
3708 { config_parse_output
, "OUTPUT" },
3709 { config_parse_input
, "INPUT" },
3710 { config_parse_log_facility
, "FACILITY" },
3711 { config_parse_log_level
, "LEVEL" },
3712 { config_parse_exec_capabilities
, "CAPABILITIES" },
3713 { config_parse_exec_secure_bits
, "SECUREBITS" },
3714 { config_parse_bounding_set
, "BOUNDINGSET" },
3715 { config_parse_limit
, "LIMIT" },
3716 { config_parse_unit_deps
, "UNIT [...]" },
3717 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3718 { config_parse_service_type
, "SERVICETYPE" },
3719 { config_parse_service_restart
, "SERVICERESTART" },
3720 #ifdef HAVE_SYSV_COMPAT
3721 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3723 { config_parse_kill_mode
, "KILLMODE" },
3724 { config_parse_signal
, "SIGNAL" },
3725 { config_parse_socket_listen
, "SOCKET [...]" },
3726 { config_parse_socket_bind
, "SOCKETBIND" },
3727 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3728 { config_parse_sec
, "SECONDS" },
3729 { config_parse_nsec
, "NANOSECONDS" },
3730 { config_parse_namespace_path_strv
, "PATH [...]" },
3731 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3732 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3733 { config_parse_unit_string_printf
, "STRING" },
3734 { config_parse_trigger_unit
, "UNIT" },
3735 { config_parse_timer
, "TIMER" },
3736 { config_parse_path_spec
, "PATH" },
3737 { config_parse_notify_access
, "ACCESS" },
3738 { config_parse_ip_tos
, "TOS" },
3739 { config_parse_unit_condition_path
, "CONDITION" },
3740 { config_parse_unit_condition_string
, "CONDITION" },
3741 { config_parse_unit_condition_null
, "CONDITION" },
3742 { config_parse_unit_slice
, "SLICE" },
3743 { config_parse_documentation
, "URL" },
3744 { config_parse_service_timeout
, "SECONDS" },
3745 { config_parse_failure_action
, "ACTION" },
3746 { config_parse_set_status
, "STATUS" },
3747 { config_parse_service_sockets
, "SOCKETS" },
3748 { config_parse_environ
, "ENVIRON" },
3750 { config_parse_syscall_filter
, "SYSCALLS" },
3751 { config_parse_syscall_archs
, "ARCHS" },
3752 { config_parse_syscall_errno
, "ERRNO" },
3753 { config_parse_address_families
, "FAMILIES" },
3755 { config_parse_cpu_shares
, "SHARES" },
3756 { config_parse_memory_limit
, "LIMIT" },
3757 { config_parse_device_allow
, "DEVICE" },
3758 { config_parse_device_policy
, "POLICY" },
3759 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3760 { config_parse_blockio_weight
, "WEIGHT" },
3761 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3762 { config_parse_long
, "LONG" },
3763 { config_parse_socket_service
, "SERVICE" },
3765 { config_parse_exec_selinux_context
, "LABEL" },
3767 { config_parse_job_mode
, "MODE" },
3768 { config_parse_job_mode_isolate
, "BOOLEAN" },
3769 { config_parse_personality
, "PERSONALITY" },
3772 const char *prev
= NULL
;
3777 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3778 const char *rvalue
= "OTHER", *lvalue
;
3782 const ConfigPerfItem
*p
;
3784 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3786 dot
= strchr(i
, '.');
3787 lvalue
= dot
? dot
+ 1 : i
;
3791 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3795 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3798 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3799 if (p
->parse
== table
[j
].callback
) {
3800 rvalue
= table
[j
].rvalue
;
3804 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);