1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
7 Copyright 2012 Holger Hans Peter Freyther
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <linux/oom.h>
32 #include <sys/resource.h>
36 #include "bus-error.h"
37 #include "bus-internal.h"
41 #include "conf-parser.h"
42 #include "cpu-set-util.h"
44 #include "errno-list.h"
48 #include "path-util.h"
50 #include "seccomp-util.h"
52 #include "securebits.h"
53 #include "signal-util.h"
55 #include "unit-name.h"
56 #include "unit-printf.h"
59 #include "load-fragment.h"
61 int config_parse_warn_compat(
66 unsigned section_line
,
72 Disabled reason
= ltype
;
75 case DISABLED_CONFIGURATION
:
76 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
77 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
80 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
81 "Support for option %s= has been removed and it is ignored", lvalue
);
83 case DISABLED_EXPERIMENTAL
:
84 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
85 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
92 int config_parse_unit_deps(const char *unit
,
96 unsigned section_line
,
103 UnitDependency d
= ltype
;
105 const char *word
, *state
;
112 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
113 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
116 t
= strndup(word
, l
);
120 r
= unit_name_printf(u
, t
, &k
);
122 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
126 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
128 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
131 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring.");
136 int config_parse_unit_string_printf(
138 const char *filename
,
141 unsigned section_line
,
148 _cleanup_free_
char *k
= NULL
;
157 r
= unit_full_printf(u
, rvalue
, &k
);
159 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
163 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
166 int config_parse_unit_strv_printf(
168 const char *filename
,
171 unsigned section_line
,
179 _cleanup_free_
char *k
= NULL
;
187 r
= unit_full_printf(u
, rvalue
, &k
);
189 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
193 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
196 int config_parse_unit_path_printf(
198 const char *filename
,
201 unsigned section_line
,
208 _cleanup_free_
char *k
= NULL
;
217 r
= unit_full_printf(u
, rvalue
, &k
);
219 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
223 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
226 int config_parse_unit_path_strv_printf(
228 const char *filename
,
231 unsigned section_line
,
239 const char *word
, *state
;
249 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
250 _cleanup_free_
char *k
= NULL
;
256 r
= unit_full_printf(u
, t
, &k
);
258 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", t
);
262 if (!utf8_is_valid(k
)) {
263 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
267 if (!path_is_absolute(k
)) {
268 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Symlink path %s is not absolute, ignoring: %m", k
);
272 path_kill_slashes(k
);
281 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring.");
286 int config_parse_socket_listen(const char *unit
,
287 const char *filename
,
290 unsigned section_line
,
297 _cleanup_free_ SocketPort
*p
= NULL
;
309 if (isempty(rvalue
)) {
310 /* An empty assignment removes all ports */
311 socket_free_ports(s
);
315 p
= new0(SocketPort
, 1);
319 if (ltype
!= SOCKET_SOCKET
) {
322 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
324 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
328 path_kill_slashes(p
->path
);
330 } else if (streq(lvalue
, "ListenNetlink")) {
331 _cleanup_free_
char *k
= NULL
;
333 p
->type
= SOCKET_SOCKET
;
334 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
336 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
340 r
= socket_address_parse_netlink(&p
->address
, k
);
342 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
347 _cleanup_free_
char *k
= NULL
;
349 p
->type
= SOCKET_SOCKET
;
350 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
352 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
356 r
= socket_address_parse_and_warn(&p
->address
, k
);
358 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
362 if (streq(lvalue
, "ListenStream"))
363 p
->address
.type
= SOCK_STREAM
;
364 else if (streq(lvalue
, "ListenDatagram"))
365 p
->address
.type
= SOCK_DGRAM
;
367 assert(streq(lvalue
, "ListenSequentialPacket"));
368 p
->address
.type
= SOCK_SEQPACKET
;
371 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
372 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
378 p
->auxiliary_fds
= NULL
;
379 p
->n_auxiliary_fds
= 0;
383 LIST_FIND_TAIL(port
, s
->ports
, tail
);
384 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
386 LIST_PREPEND(port
, s
->ports
, p
);
392 int config_parse_socket_bind(const char *unit
,
393 const char *filename
,
396 unsigned section_line
,
404 SocketAddressBindIPv6Only b
;
413 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
417 r
= parse_boolean(rvalue
);
419 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
423 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
425 s
->bind_ipv6_only
= b
;
430 int config_parse_exec_nice(const char *unit
,
431 const char *filename
,
434 unsigned section_line
,
441 ExecContext
*c
= data
;
449 r
= safe_atoi(rvalue
, &priority
);
451 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
455 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
456 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Nice priority out of range, ignoring: %s", rvalue
);
466 int config_parse_exec_oom_score_adjust(const char* unit
,
467 const char *filename
,
470 unsigned section_line
,
477 ExecContext
*c
= data
;
485 r
= safe_atoi(rvalue
, &oa
);
487 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
491 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
492 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
496 c
->oom_score_adjust
= oa
;
497 c
->oom_score_adjust_set
= true;
502 int config_parse_exec(
504 const char *filename
,
507 unsigned section_line
,
514 ExecCommand
**e
= data
;
526 rvalue
+= strspn(rvalue
, WHITESPACE
);
529 if (isempty(rvalue
)) {
530 /* An empty assignment resets the list */
531 *e
= exec_command_free_list(*e
);
537 _cleanup_strv_free_
char **n
= NULL
;
538 size_t nlen
= 0, nbufsize
= 0;
539 _cleanup_free_ ExecCommand
*nce
= NULL
;
540 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
542 bool separate_argv0
= false, ignore
= false;
546 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
551 for (i
= 0; i
< 2; i
++) {
552 /* We accept an absolute path as first argument, or
553 * alternatively an absolute prefixed with @ to allow
554 * overriding of argv[0]. */
555 if (*f
== '-' && !ignore
)
557 else if (*f
== '@' && !separate_argv0
)
558 separate_argv0
= true;
565 /* First word is either "-" or "@" with no command. */
566 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty path in command line, ignoring: \"%s\"", rvalue
);
569 if (!string_is_safe(f
)) {
570 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path contains special characters, ignoring: %s", rvalue
);
573 if (!path_is_absolute(f
)) {
574 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path is not absolute, ignoring: %s", rvalue
);
577 if (endswith(f
, "/")) {
578 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path specifies a directory, ignoring: %s", rvalue
);
582 if (f
== firstword
) {
591 if (!separate_argv0
) {
592 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
601 path_kill_slashes(path
);
603 while (!isempty(p
)) {
604 _cleanup_free_
char *word
= NULL
;
606 /* Check explicitly for an unquoted semicolon as
607 * command separator token. */
608 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
610 p
+= strspn(p
, WHITESPACE
);
615 /* Check for \; explicitly, to not confuse it with \\;
616 * or "\;" or "\\;" etc. extract_first_word would
617 * return the same for all of those. */
618 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
620 p
+= strspn(p
, WHITESPACE
);
621 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
631 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
637 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
645 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
649 nce
= new0(ExecCommand
, 1);
655 nce
->ignore
= ignore
;
657 exec_command_append_list(e
, nce
);
659 /* Do not _cleanup_free_ these. */
670 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
671 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
673 int config_parse_socket_bindtodevice(const char* unit
,
674 const char *filename
,
677 unsigned section_line
,
692 if (rvalue
[0] && !streq(rvalue
, "*")) {
699 free(s
->bind_to_device
);
700 s
->bind_to_device
= n
;
705 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
706 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
708 int config_parse_exec_io_class(const char *unit
,
709 const char *filename
,
712 unsigned section_line
,
719 ExecContext
*c
= data
;
727 x
= ioprio_class_from_string(rvalue
);
729 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
733 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
734 c
->ioprio_set
= true;
739 int config_parse_exec_io_priority(const char *unit
,
740 const char *filename
,
743 unsigned section_line
,
750 ExecContext
*c
= data
;
758 r
= safe_atoi(rvalue
, &i
);
759 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
760 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
764 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
765 c
->ioprio_set
= true;
770 int config_parse_exec_cpu_sched_policy(const char *unit
,
771 const char *filename
,
774 unsigned section_line
,
782 ExecContext
*c
= data
;
790 x
= sched_policy_from_string(rvalue
);
792 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
796 c
->cpu_sched_policy
= x
;
797 /* Moving to or from real-time policy? We need to adjust the priority */
798 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
799 c
->cpu_sched_set
= true;
804 int config_parse_exec_cpu_sched_prio(const char *unit
,
805 const char *filename
,
808 unsigned section_line
,
815 ExecContext
*c
= data
;
823 r
= safe_atoi(rvalue
, &i
);
825 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
829 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
830 min
= sched_get_priority_min(c
->cpu_sched_policy
);
831 max
= sched_get_priority_max(c
->cpu_sched_policy
);
833 if (i
< min
|| i
> max
) {
834 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
838 c
->cpu_sched_priority
= i
;
839 c
->cpu_sched_set
= true;
844 int config_parse_exec_cpu_affinity(const char *unit
,
845 const char *filename
,
848 unsigned section_line
,
855 ExecContext
*c
= data
;
856 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
864 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
872 /* An empty assignment resets the CPU list */
878 c
->cpuset_ncpus
= ncpus
;
883 int config_parse_exec_capabilities(const char *unit
,
884 const char *filename
,
887 unsigned section_line
,
894 ExecContext
*c
= data
;
902 cap
= cap_from_text(rvalue
);
904 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
, "Failed to parse capabilities, ignoring: %s", rvalue
);
909 cap_free(c
->capabilities
);
910 c
->capabilities
= cap
;
915 int config_parse_exec_secure_bits(const char *unit
,
916 const char *filename
,
919 unsigned section_line
,
926 ExecContext
*c
= data
;
928 const char *word
, *state
;
935 if (isempty(rvalue
)) {
936 /* An empty assignment resets the field */
941 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
942 if (first_word(word
, "keep-caps"))
943 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
944 else if (first_word(word
, "keep-caps-locked"))
945 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
946 else if (first_word(word
, "no-setuid-fixup"))
947 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
948 else if (first_word(word
, "no-setuid-fixup-locked"))
949 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
950 else if (first_word(word
, "noroot"))
951 c
->secure_bits
|= 1<<SECURE_NOROOT
;
952 else if (first_word(word
, "noroot-locked"))
953 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
955 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse secure bits, ignoring: %s", rvalue
);
960 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, garbage at the end, ignoring.");
965 int config_parse_bounding_set(const char *unit
,
966 const char *filename
,
969 unsigned section_line
,
976 uint64_t *capability_bounding_set_drop
= data
;
977 const char *word
, *state
;
987 if (rvalue
[0] == '~') {
992 /* Note that we store this inverted internally, since the
993 * kernel wants it like this. But we actually expose it
994 * non-inverted everywhere to have a fully normalized
997 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
998 _cleanup_free_
char *t
= NULL
;
1001 t
= strndup(word
, l
);
1005 cap
= capability_from_name(t
);
1007 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability in bounding set, ignoring: %s", t
);
1011 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1013 if (!isempty(state
))
1014 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1017 *capability_bounding_set_drop
|= sum
;
1019 *capability_bounding_set_drop
|= ~sum
;
1024 int config_parse_limit(const char *unit
,
1025 const char *filename
,
1027 const char *section
,
1028 unsigned section_line
,
1035 struct rlimit
**rl
= data
;
1036 unsigned long long u
;
1045 if (streq(rvalue
, "infinity"))
1046 u
= (unsigned long long) RLIM_INFINITY
;
1050 r
= safe_atollu(rvalue
, &u
);
1052 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1058 *rl
= new(struct rlimit
, 1);
1063 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1067 #ifdef HAVE_SYSV_COMPAT
1068 int config_parse_sysv_priority(const char *unit
,
1069 const char *filename
,
1071 const char *section
,
1072 unsigned section_line
,
1079 int *priority
= data
;
1087 r
= safe_atoi(rvalue
, &i
);
1088 if (r
< 0 || i
< 0) {
1089 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1093 *priority
= (int) i
;
1098 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1099 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1101 int config_parse_exec_mount_flags(const char *unit
,
1102 const char *filename
,
1104 const char *section
,
1105 unsigned section_line
,
1112 ExecContext
*c
= data
;
1113 const char *word
, *state
;
1115 unsigned long flags
= 0;
1122 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1123 _cleanup_free_
char *t
;
1125 t
= strndup(word
, l
);
1129 if (streq(t
, "shared"))
1131 else if (streq(t
, "slave"))
1133 else if (streq(t
, "private"))
1136 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1140 if (!isempty(state
))
1141 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1143 c
->mount_flags
= flags
;
1147 int config_parse_exec_selinux_context(
1149 const char *filename
,
1151 const char *section
,
1152 unsigned section_line
,
1159 ExecContext
*c
= data
;
1170 if (isempty(rvalue
)) {
1171 c
->selinux_context
= mfree(c
->selinux_context
);
1172 c
->selinux_context_ignore
= false;
1176 if (rvalue
[0] == '-') {
1182 r
= unit_name_printf(u
, rvalue
, &k
);
1184 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1188 free(c
->selinux_context
);
1189 c
->selinux_context
= k
;
1190 c
->selinux_context_ignore
= ignore
;
1195 int config_parse_exec_apparmor_profile(
1197 const char *filename
,
1199 const char *section
,
1200 unsigned section_line
,
1207 ExecContext
*c
= data
;
1218 if (isempty(rvalue
)) {
1219 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1220 c
->apparmor_profile_ignore
= false;
1224 if (rvalue
[0] == '-') {
1230 r
= unit_name_printf(u
, rvalue
, &k
);
1232 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1236 free(c
->apparmor_profile
);
1237 c
->apparmor_profile
= k
;
1238 c
->apparmor_profile_ignore
= ignore
;
1243 int config_parse_exec_smack_process_label(
1245 const char *filename
,
1247 const char *section
,
1248 unsigned section_line
,
1255 ExecContext
*c
= data
;
1266 if (isempty(rvalue
)) {
1267 c
->smack_process_label
= mfree(c
->smack_process_label
);
1268 c
->smack_process_label_ignore
= false;
1272 if (rvalue
[0] == '-') {
1278 r
= unit_name_printf(u
, rvalue
, &k
);
1280 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1284 free(c
->smack_process_label
);
1285 c
->smack_process_label
= k
;
1286 c
->smack_process_label_ignore
= ignore
;
1291 int config_parse_timer(const char *unit
,
1292 const char *filename
,
1294 const char *section
,
1295 unsigned section_line
,
1306 CalendarSpec
*c
= NULL
;
1313 if (isempty(rvalue
)) {
1314 /* Empty assignment resets list */
1315 timer_free_values(t
);
1319 b
= timer_base_from_string(lvalue
);
1321 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1325 if (b
== TIMER_CALENDAR
) {
1326 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1327 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", rvalue
);
1331 if (parse_sec(rvalue
, &u
) < 0) {
1332 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", rvalue
);
1337 v
= new0(TimerValue
, 1);
1339 calendar_spec_free(c
);
1345 v
->calendar_spec
= c
;
1347 LIST_PREPEND(value
, t
->values
, v
);
1352 int config_parse_trigger_unit(
1354 const char *filename
,
1356 const char *section
,
1357 unsigned section_line
,
1364 _cleanup_free_
char *p
= NULL
;
1374 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1375 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1379 r
= unit_name_printf(u
, rvalue
, &p
);
1381 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1385 type
= unit_name_to_type(p
);
1387 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1391 if (type
== u
->type
) {
1392 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1396 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1398 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1405 int config_parse_path_spec(const char *unit
,
1406 const char *filename
,
1408 const char *section
,
1409 unsigned section_line
,
1419 _cleanup_free_
char *k
= NULL
;
1427 if (isempty(rvalue
)) {
1428 /* Empty assignment clears list */
1433 b
= path_type_from_string(lvalue
);
1435 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1439 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1441 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1445 if (!path_is_absolute(k
)) {
1446 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1450 s
= new0(PathSpec
, 1);
1455 s
->path
= path_kill_slashes(k
);
1460 LIST_PREPEND(spec
, p
->specs
, s
);
1465 int config_parse_socket_service(
1467 const char *filename
,
1469 const char *section
,
1470 unsigned section_line
,
1477 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1478 _cleanup_free_
char *p
= NULL
;
1488 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1490 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1494 if (!endswith(p
, ".service")) {
1495 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1499 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1501 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1505 unit_ref_set(&s
->service
, x
);
1510 int config_parse_fdname(
1512 const char *filename
,
1514 const char *section
,
1515 unsigned section_line
,
1522 _cleanup_free_
char *p
= NULL
;
1531 if (isempty(rvalue
)) {
1532 s
->fdname
= mfree(s
->fdname
);
1536 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1538 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1542 if (!fdname_is_valid(p
)) {
1543 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1554 int config_parse_service_sockets(
1556 const char *filename
,
1558 const char *section
,
1559 unsigned section_line
,
1567 const char *word
, *state
;
1576 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1577 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1579 t
= strndup(word
, l
);
1583 r
= unit_name_printf(UNIT(s
), t
, &k
);
1585 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1589 if (!endswith(k
, ".socket")) {
1590 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1594 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1596 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1598 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1600 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1602 if (!isempty(state
))
1603 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1608 int config_parse_bus_name(
1610 const char *filename
,
1612 const char *section
,
1613 unsigned section_line
,
1620 _cleanup_free_
char *k
= NULL
;
1629 r
= unit_full_printf(u
, rvalue
, &k
);
1631 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1635 if (!service_name_is_valid(k
)) {
1636 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1640 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1643 int config_parse_service_timeout(const char *unit
,
1644 const char *filename
,
1646 const char *section
,
1647 unsigned section_line
,
1654 Service
*s
= userdata
;
1662 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1663 rvalue
, data
, userdata
);
1667 if (streq(lvalue
, "TimeoutSec")) {
1668 s
->start_timeout_defined
= true;
1669 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1670 } else if (streq(lvalue
, "TimeoutStartSec"))
1671 s
->start_timeout_defined
= true;
1676 int config_parse_busname_service(
1678 const char *filename
,
1680 const char *section
,
1681 unsigned section_line
,
1688 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1692 _cleanup_free_
char *p
= NULL
;
1699 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1701 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1705 if (!endswith(p
, ".service")) {
1706 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1710 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1712 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1716 unit_ref_set(&n
->service
, x
);
1721 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1723 int config_parse_bus_policy(
1725 const char *filename
,
1727 const char *section
,
1728 unsigned section_line
,
1735 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1736 _cleanup_free_
char *id_str
= NULL
;
1737 BusName
*busname
= data
;
1745 p
= new0(BusNamePolicy
, 1);
1749 if (streq(lvalue
, "AllowUser"))
1750 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1751 else if (streq(lvalue
, "AllowGroup"))
1752 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1754 assert_not_reached("Unknown lvalue");
1756 id_str
= strdup(rvalue
);
1760 access_str
= strpbrk(id_str
, WHITESPACE
);
1762 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy value '%s'", rvalue
);
1768 access_str
+= strspn(access_str
, WHITESPACE
);
1770 p
->access
= bus_policy_access_from_string(access_str
);
1771 if (p
->access
< 0) {
1772 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy access type '%s'", access_str
);
1779 LIST_PREPEND(policy
, busname
->policy
, p
);
1785 int config_parse_bus_endpoint_policy(
1787 const char *filename
,
1789 const char *section
,
1790 unsigned section_line
,
1797 _cleanup_free_
char *name
= NULL
;
1798 BusPolicyAccess access
;
1799 ExecContext
*c
= data
;
1808 name
= strdup(rvalue
);
1812 access_str
= strpbrk(name
, WHITESPACE
);
1814 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy value '%s'", rvalue
);
1820 access_str
+= strspn(access_str
, WHITESPACE
);
1822 access
= bus_policy_access_from_string(access_str
);
1823 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1824 access
>= _BUS_POLICY_ACCESS_MAX
) {
1825 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy access type '%s'", access_str
);
1829 if (!c
->bus_endpoint
) {
1830 r
= bus_endpoint_new(&c
->bus_endpoint
);
1832 return log_error_errno(r
, "Failed to create bus endpoint object: %m");
1835 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1838 int config_parse_working_directory(
1840 const char *filename
,
1842 const char *section
,
1843 unsigned section_line
,
1850 ExecContext
*c
= data
;
1861 if (rvalue
[0] == '-') {
1867 if (streq(rvalue
, "~")) {
1868 c
->working_directory_home
= true;
1869 c
->working_directory
= mfree(c
->working_directory
);
1871 _cleanup_free_
char *k
= NULL
;
1873 r
= unit_full_printf(u
, rvalue
, &k
);
1875 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
1879 path_kill_slashes(k
);
1881 if (!utf8_is_valid(k
)) {
1882 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
1886 if (!path_is_absolute(k
)) {
1887 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
1891 free(c
->working_directory
);
1892 c
->working_directory
= k
;
1895 c
->working_directory_home
= false;
1898 c
->working_directory_missing_ok
= missing_ok
;
1902 int config_parse_unit_env_file(const char *unit
,
1903 const char *filename
,
1905 const char *section
,
1906 unsigned section_line
,
1915 _cleanup_free_
char *n
= NULL
;
1923 if (isempty(rvalue
)) {
1924 /* Empty assignment frees the list */
1925 *env
= strv_free(*env
);
1929 r
= unit_full_printf(u
, rvalue
, &n
);
1931 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1935 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
1936 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
1940 r
= strv_extend(env
, n
);
1947 int config_parse_environ(const char *unit
,
1948 const char *filename
,
1950 const char *section
,
1951 unsigned section_line
,
1960 const char *word
, *state
;
1962 _cleanup_free_
char *k
= NULL
;
1970 if (isempty(rvalue
)) {
1971 /* Empty assignment resets the list */
1972 *env
= strv_free(*env
);
1977 r
= unit_full_printf(u
, rvalue
, &k
);
1979 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1990 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1991 _cleanup_free_
char *n
= NULL
;
1994 r
= cunescape_length(word
, l
, 0, &n
);
1996 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
2000 if (!env_assignment_is_valid(n
)) {
2001 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid environment assignment, ignoring: %s", rvalue
);
2005 x
= strv_env_set(*env
, n
);
2012 if (!isempty(state
))
2013 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2018 int config_parse_ip_tos(const char *unit
,
2019 const char *filename
,
2021 const char *section
,
2022 unsigned section_line
,
2029 int *ip_tos
= data
, x
;
2036 x
= ip_tos_from_string(rvalue
);
2038 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2046 int config_parse_unit_condition_path(
2048 const char *filename
,
2050 const char *section
,
2051 unsigned section_line
,
2058 _cleanup_free_
char *p
= NULL
;
2059 Condition
**list
= data
, *c
;
2060 ConditionType t
= ltype
;
2061 bool trigger
, negate
;
2070 if (isempty(rvalue
)) {
2071 /* Empty assignment resets the list */
2072 *list
= condition_free_list(*list
);
2076 trigger
= rvalue
[0] == '|';
2080 negate
= rvalue
[0] == '!';
2084 r
= unit_full_printf(u
, rvalue
, &p
);
2086 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2090 if (!path_is_absolute(p
)) {
2091 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2095 c
= condition_new(t
, p
, trigger
, negate
);
2099 LIST_PREPEND(conditions
, *list
, c
);
2103 int config_parse_unit_condition_string(
2105 const char *filename
,
2107 const char *section
,
2108 unsigned section_line
,
2115 _cleanup_free_
char *s
= NULL
;
2116 Condition
**list
= data
, *c
;
2117 ConditionType t
= ltype
;
2118 bool trigger
, negate
;
2127 if (isempty(rvalue
)) {
2128 /* Empty assignment resets the list */
2129 *list
= condition_free_list(*list
);
2133 trigger
= rvalue
[0] == '|';
2137 negate
= rvalue
[0] == '!';
2141 r
= unit_full_printf(u
, rvalue
, &s
);
2143 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2147 c
= condition_new(t
, s
, trigger
, negate
);
2151 LIST_PREPEND(conditions
, *list
, c
);
2155 int config_parse_unit_condition_null(
2157 const char *filename
,
2159 const char *section
,
2160 unsigned section_line
,
2167 Condition
**list
= data
, *c
;
2168 bool trigger
, negate
;
2176 if (isempty(rvalue
)) {
2177 /* Empty assignment resets the list */
2178 *list
= condition_free_list(*list
);
2182 trigger
= rvalue
[0] == '|';
2186 negate
= rvalue
[0] == '!';
2190 b
= parse_boolean(rvalue
);
2192 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2199 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2203 LIST_PREPEND(conditions
, *list
, c
);
2207 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2208 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2210 int config_parse_unit_requires_mounts_for(
2212 const char *filename
,
2214 const char *section
,
2215 unsigned section_line
,
2223 const char *word
, *state
;
2231 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2233 _cleanup_free_
char *n
;
2235 n
= strndup(word
, l
);
2239 if (!utf8_is_valid(n
)) {
2240 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2244 r
= unit_require_mounts_for(u
, n
);
2246 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount for, ignoring: %s", rvalue
);
2250 if (!isempty(state
))
2251 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2256 int config_parse_documentation(const char *unit
,
2257 const char *filename
,
2259 const char *section
,
2260 unsigned section_line
,
2276 if (isempty(rvalue
)) {
2277 /* Empty assignment resets the list */
2278 u
->documentation
= strv_free(u
->documentation
);
2282 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2283 rvalue
, data
, userdata
);
2287 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2289 if (documentation_url_is_valid(*a
))
2292 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2303 int config_parse_syscall_filter(
2305 const char *filename
,
2307 const char *section
,
2308 unsigned section_line
,
2315 static const char default_syscalls
[] =
2322 ExecContext
*c
= data
;
2324 bool invert
= false;
2325 const char *word
, *state
;
2334 if (isempty(rvalue
)) {
2335 /* Empty assignment resets the list */
2336 c
->syscall_filter
= set_free(c
->syscall_filter
);
2337 c
->syscall_whitelist
= false;
2341 if (rvalue
[0] == '~') {
2346 if (!c
->syscall_filter
) {
2347 c
->syscall_filter
= set_new(NULL
);
2348 if (!c
->syscall_filter
)
2352 /* Allow everything but the ones listed */
2353 c
->syscall_whitelist
= false;
2357 /* Allow nothing but the ones listed */
2358 c
->syscall_whitelist
= true;
2360 /* Accept default syscalls if we are on a whitelist */
2361 NULSTR_FOREACH(i
, default_syscalls
) {
2364 id
= seccomp_syscall_resolve_name(i
);
2368 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2377 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2378 _cleanup_free_
char *t
= NULL
;
2381 t
= strndup(word
, l
);
2385 id
= seccomp_syscall_resolve_name(t
);
2387 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2391 /* If we previously wanted to forbid a syscall and now
2392 * we want to allow it, then remove it from the list
2394 if (!invert
== c
->syscall_whitelist
) {
2395 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2401 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2403 if (!isempty(state
))
2404 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2406 /* Turn on NNP, but only if it wasn't configured explicitly
2407 * before, and only if we are in user mode. */
2408 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2409 c
->no_new_privileges
= true;
2414 int config_parse_syscall_archs(
2416 const char *filename
,
2418 const char *section
,
2419 unsigned section_line
,
2427 const char *word
, *state
;
2431 if (isempty(rvalue
)) {
2432 *archs
= set_free(*archs
);
2436 r
= set_ensure_allocated(archs
, NULL
);
2440 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2441 _cleanup_free_
char *t
= NULL
;
2444 t
= strndup(word
, l
);
2448 r
= seccomp_arch_from_string(t
, &a
);
2450 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call architecture, ignoring: %s", t
);
2454 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2460 if (!isempty(state
))
2461 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2466 int config_parse_syscall_errno(
2468 const char *filename
,
2470 const char *section
,
2471 unsigned section_line
,
2478 ExecContext
*c
= data
;
2485 if (isempty(rvalue
)) {
2486 /* Empty assignment resets to KILL */
2487 c
->syscall_errno
= 0;
2491 e
= errno_from_name(rvalue
);
2493 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2497 c
->syscall_errno
= e
;
2501 int config_parse_address_families(
2503 const char *filename
,
2505 const char *section
,
2506 unsigned section_line
,
2513 ExecContext
*c
= data
;
2514 bool invert
= false;
2515 const char *word
, *state
;
2523 if (isempty(rvalue
)) {
2524 /* Empty assignment resets the list */
2525 c
->address_families
= set_free(c
->address_families
);
2526 c
->address_families_whitelist
= false;
2530 if (rvalue
[0] == '~') {
2535 if (!c
->address_families
) {
2536 c
->address_families
= set_new(NULL
);
2537 if (!c
->address_families
)
2540 c
->address_families_whitelist
= !invert
;
2543 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2544 _cleanup_free_
char *t
= NULL
;
2547 t
= strndup(word
, l
);
2551 af
= af_from_name(t
);
2553 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse address family, ignoring: %s", t
);
2557 /* If we previously wanted to forbid an address family and now
2558 * we want to allow it, then remove it from the list
2560 if (!invert
== c
->address_families_whitelist
) {
2561 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2567 set_remove(c
->address_families
, INT_TO_PTR(af
));
2569 if (!isempty(state
))
2570 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2576 int config_parse_unit_slice(
2578 const char *filename
,
2580 const char *section
,
2581 unsigned section_line
,
2588 _cleanup_free_
char *k
= NULL
;
2589 Unit
*u
= userdata
, *slice
= NULL
;
2597 r
= unit_name_printf(u
, rvalue
, &k
);
2599 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2603 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2605 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2609 r
= unit_set_slice(u
, slice
);
2611 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2618 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2620 int config_parse_cpu_shares(
2622 const char *filename
,
2624 const char *section
,
2625 unsigned section_line
,
2632 uint64_t *shares
= data
;
2639 r
= cg_cpu_shares_parse(rvalue
, shares
);
2641 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2648 int config_parse_cpu_quota(
2650 const char *filename
,
2652 const char *section
,
2653 unsigned section_line
,
2660 CGroupContext
*c
= data
;
2667 if (isempty(rvalue
)) {
2668 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2672 if (!endswith(rvalue
, "%")) {
2673 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2677 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2678 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' invalid. Ignoring.", rvalue
);
2682 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2687 int config_parse_memory_limit(
2689 const char *filename
,
2691 const char *section
,
2692 unsigned section_line
,
2699 CGroupContext
*c
= data
;
2703 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2704 c
->memory_limit
= (uint64_t) -1;
2708 r
= parse_size(rvalue
, 1024, &bytes
);
2709 if (r
< 0 || bytes
< 1) {
2710 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2714 c
->memory_limit
= bytes
;
2718 int config_parse_tasks_max(
2720 const char *filename
,
2722 const char *section
,
2723 unsigned section_line
,
2730 CGroupContext
*c
= data
;
2734 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2735 c
->tasks_max
= (uint64_t) -1;
2739 r
= safe_atou64(rvalue
, &u
);
2740 if (r
< 0 || u
< 1) {
2741 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2749 int config_parse_device_allow(
2751 const char *filename
,
2753 const char *section
,
2754 unsigned section_line
,
2761 _cleanup_free_
char *path
= NULL
;
2762 CGroupContext
*c
= data
;
2763 CGroupDeviceAllow
*a
;
2767 if (isempty(rvalue
)) {
2768 while (c
->device_allow
)
2769 cgroup_context_free_device_allow(c
, c
->device_allow
);
2774 n
= strcspn(rvalue
, WHITESPACE
);
2775 path
= strndup(rvalue
, n
);
2779 if (!startswith(path
, "/dev/") &&
2780 !startswith(path
, "block-") &&
2781 !startswith(path
, "char-")) {
2782 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2786 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2790 if (!in_charset(m
, "rwm")) {
2791 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
2795 a
= new0(CGroupDeviceAllow
, 1);
2801 a
->r
= !!strchr(m
, 'r');
2802 a
->w
= !!strchr(m
, 'w');
2803 a
->m
= !!strchr(m
, 'm');
2805 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2809 int config_parse_blockio_weight(
2811 const char *filename
,
2813 const char *section
,
2814 unsigned section_line
,
2821 uint64_t *weight
= data
;
2828 r
= cg_blkio_weight_parse(rvalue
, weight
);
2830 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2837 int config_parse_blockio_device_weight(
2839 const char *filename
,
2841 const char *section
,
2842 unsigned section_line
,
2849 _cleanup_free_
char *path
= NULL
;
2850 CGroupBlockIODeviceWeight
*w
;
2851 CGroupContext
*c
= data
;
2861 if (isempty(rvalue
)) {
2862 while (c
->blockio_device_weights
)
2863 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2868 n
= strcspn(rvalue
, WHITESPACE
);
2869 weight
= rvalue
+ n
;
2870 weight
+= strspn(weight
, WHITESPACE
);
2872 if (isempty(weight
)) {
2873 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
2877 path
= strndup(rvalue
, n
);
2881 if (!path_startswith(path
, "/dev")) {
2882 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2886 r
= cg_blkio_weight_parse(weight
, &u
);
2888 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2892 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2894 w
= new0(CGroupBlockIODeviceWeight
, 1);
2903 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2907 int config_parse_blockio_bandwidth(
2909 const char *filename
,
2911 const char *section
,
2912 unsigned section_line
,
2919 _cleanup_free_
char *path
= NULL
;
2920 CGroupBlockIODeviceBandwidth
*b
;
2921 CGroupContext
*c
= data
;
2922 const char *bandwidth
;
2932 read
= streq("BlockIOReadBandwidth", lvalue
);
2934 if (isempty(rvalue
)) {
2935 CGroupBlockIODeviceBandwidth
*next
;
2937 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2938 if (b
->read
== read
)
2939 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2944 n
= strcspn(rvalue
, WHITESPACE
);
2945 bandwidth
= rvalue
+ n
;
2946 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2949 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
2953 path
= strndup(rvalue
, n
);
2957 if (!path_startswith(path
, "/dev")) {
2958 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2962 r
= parse_size(bandwidth
, 1000, &bytes
);
2963 if (r
< 0 || bytes
<= 0) {
2964 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2968 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2974 b
->bandwidth
= bytes
;
2977 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2982 int config_parse_netclass(
2984 const char *filename
,
2986 const char *section
,
2987 unsigned section_line
,
2994 CGroupContext
*c
= data
;
3002 if (streq(rvalue
, "auto")) {
3003 c
->netclass_type
= CGROUP_NETCLASS_TYPE_AUTO
;
3007 r
= safe_atou32(rvalue
, &v
);
3009 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Netclass '%s' invalid. Ignoring.", rvalue
);
3013 if (v
> CGROUP_NETCLASS_FIXED_MAX
)
3014 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3015 "Fixed netclass %" PRIu32
" out of allowed range (0-%d). Applying anyway.", v
, (uint32_t) CGROUP_NETCLASS_FIXED_MAX
);
3018 c
->netclass_type
= CGROUP_NETCLASS_TYPE_FIXED
;
3023 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3025 int config_parse_job_mode_isolate(
3027 const char *filename
,
3029 const char *section
,
3030 unsigned section_line
,
3044 r
= parse_boolean(rvalue
);
3046 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3050 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3054 int config_parse_runtime_directory(
3056 const char *filename
,
3058 const char *section
,
3059 unsigned section_line
,
3068 const char *word
, *state
;
3077 if (isempty(rvalue
)) {
3078 /* Empty assignment resets the list */
3079 *rt
= strv_free(*rt
);
3083 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3084 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3086 t
= strndup(word
, l
);
3090 r
= unit_name_printf(u
, t
, &n
);
3092 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
3096 if (!filename_is_valid(n
)) {
3097 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3101 r
= strv_push(rt
, n
);
3107 if (!isempty(state
))
3108 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3113 int config_parse_set_status(
3115 const char *filename
,
3117 const char *section
,
3118 unsigned section_line
,
3126 const char *word
, *state
;
3128 ExitStatusSet
*status_set
= data
;
3135 /* Empty assignment resets the list */
3136 if (isempty(rvalue
)) {
3137 exit_status_set_free(status_set
);
3141 FOREACH_WORD(word
, l
, rvalue
, state
) {
3142 _cleanup_free_
char *temp
;
3146 temp
= strndup(word
, l
);
3150 r
= safe_atoi(temp
, &val
);
3152 val
= signal_from_string_try_harder(temp
);
3155 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3158 set
= &status_set
->signal
;
3160 if (val
< 0 || val
> 255) {
3161 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3164 set
= &status_set
->status
;
3167 r
= set_ensure_allocated(set
, NULL
);
3171 r
= set_put(*set
, INT_TO_PTR(val
));
3173 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3177 if (!isempty(state
))
3178 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3183 int config_parse_namespace_path_strv(
3185 const char *filename
,
3187 const char *section
,
3188 unsigned section_line
,
3205 if (isempty(rvalue
)) {
3206 /* Empty assignment resets the list */
3207 *sv
= strv_free(*sv
);
3211 prev
= cur
= rvalue
;
3213 _cleanup_free_
char *word
= NULL
;
3216 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3218 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring: %s", prev
);
3224 if (!utf8_is_valid(word
)) {
3225 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3230 offset
= word
[0] == '-';
3231 if (!path_is_absolute(word
+ offset
)) {
3232 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", word
);
3237 path_kill_slashes(word
+ offset
);
3239 r
= strv_push(sv
, word
);
3250 int config_parse_no_new_privileges(
3252 const char *filename
,
3254 const char *section
,
3255 unsigned section_line
,
3262 ExecContext
*c
= data
;
3270 k
= parse_boolean(rvalue
);
3272 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3276 c
->no_new_privileges
= !!k
;
3277 c
->no_new_privileges_set
= true;
3282 int config_parse_protect_home(
3284 const char *filename
,
3286 const char *section
,
3287 unsigned section_line
,
3294 ExecContext
*c
= data
;
3302 /* Our enum shall be a superset of booleans, hence first try
3303 * to parse as as boolean, and then as enum */
3305 k
= parse_boolean(rvalue
);
3307 c
->protect_home
= PROTECT_HOME_YES
;
3309 c
->protect_home
= PROTECT_HOME_NO
;
3313 h
= protect_home_from_string(rvalue
);
3315 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
3319 c
->protect_home
= h
;
3325 int config_parse_protect_system(
3327 const char *filename
,
3329 const char *section
,
3330 unsigned section_line
,
3337 ExecContext
*c
= data
;
3345 /* Our enum shall be a superset of booleans, hence first try
3346 * to parse as as boolean, and then as enum */
3348 k
= parse_boolean(rvalue
);
3350 c
->protect_system
= PROTECT_SYSTEM_YES
;
3352 c
->protect_system
= PROTECT_SYSTEM_NO
;
3356 s
= protect_system_from_string(rvalue
);
3358 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
3362 c
->protect_system
= s
;
3368 #define FOLLOW_MAX 8
3370 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3381 /* This will update the filename pointer if the loaded file is
3382 * reached by a symlink. The old string will be freed. */
3385 char *target
, *name
;
3387 if (c
++ >= FOLLOW_MAX
)
3390 path_kill_slashes(*filename
);
3392 /* Add the file name we are currently looking at to
3393 * the names of this unit, but only if it is a valid
3395 name
= basename(*filename
);
3397 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3399 id
= set_get(names
, name
);
3405 r
= set_consume(names
, id
);
3411 /* Try to open the file name, but don't if its a symlink */
3412 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3419 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3420 r
= readlink_and_make_absolute(*filename
, &target
);
3428 f
= fdopen(fd
, "re");
3439 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3447 /* Let's try to add in all symlink names we found */
3448 while ((k
= set_steal_first(names
))) {
3450 /* First try to merge in the other name into our
3452 r
= unit_merge_by_name(*u
, k
);
3456 /* Hmm, we couldn't merge the other unit into
3457 * ours? Then let's try it the other way
3460 other
= manager_get_unit((*u
)->manager
, k
);
3464 r
= unit_merge(other
, *u
);
3467 return merge_by_names(u
, names
, NULL
);
3475 unit_choose_id(*u
, id
);
3483 static int load_from_path(Unit
*u
, const char *path
) {
3485 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3486 _cleanup_fclose_
FILE *f
= NULL
;
3487 _cleanup_free_
char *filename
= NULL
;
3495 symlink_names
= set_new(&string_hash_ops
);
3499 if (path_is_absolute(path
)) {
3501 filename
= strdup(path
);
3505 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3507 filename
= mfree(filename
);
3515 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3517 /* Instead of opening the path right away, we manually
3518 * follow all symlinks and add their name to our unit
3519 * name set while doing so */
3520 filename
= path_make_absolute(path
, *p
);
3524 if (u
->manager
->unit_path_cache
&&
3525 !set_get(u
->manager
->unit_path_cache
, filename
))
3528 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3531 filename
= mfree(filename
);
3535 /* Empty the symlink names for the next run */
3536 set_clear_free(symlink_names
);
3545 /* Hmm, no suitable file found? */
3549 r
= merge_by_names(&merged
, symlink_names
, id
);
3554 u
->load_state
= UNIT_MERGED
;
3558 if (fstat(fileno(f
), &st
) < 0)
3561 if (null_or_empty(&st
))
3562 u
->load_state
= UNIT_MASKED
;
3564 u
->load_state
= UNIT_LOADED
;
3566 /* Now, parse the file contents */
3567 r
= config_parse(u
->id
, filename
, f
,
3568 UNIT_VTABLE(u
)->sections
,
3569 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3570 false, true, false, u
);
3575 free(u
->fragment_path
);
3576 u
->fragment_path
= filename
;
3579 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3581 if (u
->source_path
) {
3582 if (stat(u
->source_path
, &st
) >= 0)
3583 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3585 u
->source_mtime
= 0;
3591 int unit_load_fragment(Unit
*u
) {
3597 assert(u
->load_state
== UNIT_STUB
);
3601 u
->load_state
= UNIT_LOADED
;
3605 /* First, try to find the unit under its id. We always look
3606 * for unit files in the default directories, to make it easy
3607 * to override things by placing things in /etc/systemd/system */
3608 r
= load_from_path(u
, u
->id
);
3612 /* Try to find an alias we can load this with */
3613 if (u
->load_state
== UNIT_STUB
) {
3614 SET_FOREACH(t
, u
->names
, i
) {
3619 r
= load_from_path(u
, t
);
3623 if (u
->load_state
!= UNIT_STUB
)
3628 /* And now, try looking for it under the suggested (originally linked) path */
3629 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3631 r
= load_from_path(u
, u
->fragment_path
);
3635 if (u
->load_state
== UNIT_STUB
)
3636 /* Hmm, this didn't work? Then let's get rid
3637 * of the fragment path stored for us, so that
3638 * we don't point to an invalid location. */
3639 u
->fragment_path
= mfree(u
->fragment_path
);
3642 /* Look for a template */
3643 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3644 _cleanup_free_
char *k
= NULL
;
3646 r
= unit_name_template(u
->id
, &k
);
3650 r
= load_from_path(u
, k
);
3654 if (u
->load_state
== UNIT_STUB
) {
3655 SET_FOREACH(t
, u
->names
, i
) {
3656 _cleanup_free_
char *z
= NULL
;
3661 r
= unit_name_template(t
, &z
);
3665 r
= load_from_path(u
, z
);
3669 if (u
->load_state
!= UNIT_STUB
)
3678 void unit_dump_config_items(FILE *f
) {
3679 static const struct {
3680 const ConfigParserCallback callback
;
3683 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3684 { config_parse_warn_compat
, "NOTSUPPORTED" },
3686 { config_parse_int
, "INTEGER" },
3687 { config_parse_unsigned
, "UNSIGNED" },
3688 { config_parse_iec_size
, "SIZE" },
3689 { config_parse_iec_uint64
, "SIZE" },
3690 { config_parse_si_size
, "SIZE" },
3691 { config_parse_bool
, "BOOLEAN" },
3692 { config_parse_string
, "STRING" },
3693 { config_parse_path
, "PATH" },
3694 { config_parse_unit_path_printf
, "PATH" },
3695 { config_parse_strv
, "STRING [...]" },
3696 { config_parse_exec_nice
, "NICE" },
3697 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3698 { config_parse_exec_io_class
, "IOCLASS" },
3699 { config_parse_exec_io_priority
, "IOPRIORITY" },
3700 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3701 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3702 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3703 { config_parse_mode
, "MODE" },
3704 { config_parse_unit_env_file
, "FILE" },
3705 { config_parse_output
, "OUTPUT" },
3706 { config_parse_input
, "INPUT" },
3707 { config_parse_log_facility
, "FACILITY" },
3708 { config_parse_log_level
, "LEVEL" },
3709 { config_parse_exec_capabilities
, "CAPABILITIES" },
3710 { config_parse_exec_secure_bits
, "SECUREBITS" },
3711 { config_parse_bounding_set
, "BOUNDINGSET" },
3712 { config_parse_limit
, "LIMIT" },
3713 { config_parse_unit_deps
, "UNIT [...]" },
3714 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3715 { config_parse_service_type
, "SERVICETYPE" },
3716 { config_parse_service_restart
, "SERVICERESTART" },
3717 #ifdef HAVE_SYSV_COMPAT
3718 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3720 { config_parse_kill_mode
, "KILLMODE" },
3721 { config_parse_signal
, "SIGNAL" },
3722 { config_parse_socket_listen
, "SOCKET [...]" },
3723 { config_parse_socket_bind
, "SOCKETBIND" },
3724 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3725 { config_parse_sec
, "SECONDS" },
3726 { config_parse_nsec
, "NANOSECONDS" },
3727 { config_parse_namespace_path_strv
, "PATH [...]" },
3728 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3729 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3730 { config_parse_unit_string_printf
, "STRING" },
3731 { config_parse_trigger_unit
, "UNIT" },
3732 { config_parse_timer
, "TIMER" },
3733 { config_parse_path_spec
, "PATH" },
3734 { config_parse_notify_access
, "ACCESS" },
3735 { config_parse_ip_tos
, "TOS" },
3736 { config_parse_unit_condition_path
, "CONDITION" },
3737 { config_parse_unit_condition_string
, "CONDITION" },
3738 { config_parse_unit_condition_null
, "CONDITION" },
3739 { config_parse_unit_slice
, "SLICE" },
3740 { config_parse_documentation
, "URL" },
3741 { config_parse_service_timeout
, "SECONDS" },
3742 { config_parse_failure_action
, "ACTION" },
3743 { config_parse_set_status
, "STATUS" },
3744 { config_parse_service_sockets
, "SOCKETS" },
3745 { config_parse_environ
, "ENVIRON" },
3747 { config_parse_syscall_filter
, "SYSCALLS" },
3748 { config_parse_syscall_archs
, "ARCHS" },
3749 { config_parse_syscall_errno
, "ERRNO" },
3750 { config_parse_address_families
, "FAMILIES" },
3752 { config_parse_cpu_shares
, "SHARES" },
3753 { config_parse_memory_limit
, "LIMIT" },
3754 { config_parse_device_allow
, "DEVICE" },
3755 { config_parse_device_policy
, "POLICY" },
3756 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3757 { config_parse_blockio_weight
, "WEIGHT" },
3758 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3759 { config_parse_long
, "LONG" },
3760 { config_parse_socket_service
, "SERVICE" },
3762 { config_parse_exec_selinux_context
, "LABEL" },
3764 { config_parse_job_mode
, "MODE" },
3765 { config_parse_job_mode_isolate
, "BOOLEAN" },
3766 { config_parse_personality
, "PERSONALITY" },
3769 const char *prev
= NULL
;
3774 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3775 const char *rvalue
= "OTHER", *lvalue
;
3779 const ConfigPerfItem
*p
;
3781 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3783 dot
= strchr(i
, '.');
3784 lvalue
= dot
? dot
+ 1 : i
;
3788 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3792 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3795 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3796 if (p
->parse
== table
[j
].callback
) {
3797 rvalue
= table
[j
].rvalue
;
3801 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);