2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
5 Copyright 2012 Holger Hans Peter Freyther
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <linux/oom.h>
30 #include <sys/resource.h>
34 #include "alloc-util.h"
35 #include "bus-error.h"
36 #include "bus-internal.h"
39 #include "capability-util.h"
41 #include "conf-parser.h"
42 #include "cpu-set-util.h"
44 #include "errno-list.h"
49 #include "load-fragment.h"
52 #include "parse-util.h"
53 #include "path-util.h"
54 #include "process-util.h"
55 #include "rlimit-util.h"
57 #include "seccomp-util.h"
59 #include "securebits.h"
60 #include "signal-util.h"
61 #include "stat-util.h"
62 #include "string-util.h"
64 #include "unit-name.h"
65 #include "unit-printf.h"
70 int config_parse_warn_compat(
75 unsigned section_line
,
81 Disabled reason
= ltype
;
84 case DISABLED_CONFIGURATION
:
85 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
86 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
89 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
90 "Support for option %s= has been removed and it is ignored", lvalue
);
92 case DISABLED_EXPERIMENTAL
:
93 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
94 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
101 int config_parse_unit_deps(
103 const char *filename
,
106 unsigned section_line
,
113 UnitDependency d
= ltype
;
123 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
126 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_RETAIN_ESCAPE
);
132 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
136 r
= unit_name_printf(u
, word
, &k
);
138 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
142 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
144 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
150 int config_parse_obsolete_unit_deps(
152 const char *filename
,
155 unsigned section_line
,
162 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
163 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue
, unit_dependency_to_string(ltype
));
165 return config_parse_unit_deps(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, data
, userdata
);
168 int config_parse_unit_string_printf(
170 const char *filename
,
173 unsigned section_line
,
180 _cleanup_free_
char *k
= NULL
;
189 r
= unit_full_printf(u
, rvalue
, &k
);
191 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
195 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
198 int config_parse_unit_strv_printf(
200 const char *filename
,
203 unsigned section_line
,
211 _cleanup_free_
char *k
= NULL
;
219 r
= unit_full_printf(u
, rvalue
, &k
);
221 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
225 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
228 int config_parse_unit_path_printf(
230 const char *filename
,
233 unsigned section_line
,
240 _cleanup_free_
char *k
= NULL
;
249 r
= unit_full_printf(u
, rvalue
, &k
);
251 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
255 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
258 int config_parse_unit_path_strv_printf(
260 const char *filename
,
263 unsigned section_line
,
271 const char *word
, *state
;
281 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
282 _cleanup_free_
char *k
= NULL
;
288 r
= unit_full_printf(u
, t
, &k
);
290 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", t
);
294 if (!utf8_is_valid(k
)) {
295 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
299 if (!path_is_absolute(k
)) {
300 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Symlink path %s is not absolute, ignoring: %m", k
);
304 path_kill_slashes(k
);
313 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring.");
318 int config_parse_socket_listen(const char *unit
,
319 const char *filename
,
322 unsigned section_line
,
329 _cleanup_free_ SocketPort
*p
= NULL
;
341 if (isempty(rvalue
)) {
342 /* An empty assignment removes all ports */
343 socket_free_ports(s
);
347 p
= new0(SocketPort
, 1);
351 if (ltype
!= SOCKET_SOCKET
) {
354 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
356 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
360 path_kill_slashes(p
->path
);
362 } else if (streq(lvalue
, "ListenNetlink")) {
363 _cleanup_free_
char *k
= NULL
;
365 p
->type
= SOCKET_SOCKET
;
366 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
368 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
372 r
= socket_address_parse_netlink(&p
->address
, k
);
374 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
379 _cleanup_free_
char *k
= NULL
;
381 p
->type
= SOCKET_SOCKET
;
382 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
384 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
388 r
= socket_address_parse_and_warn(&p
->address
, k
);
390 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
394 if (streq(lvalue
, "ListenStream"))
395 p
->address
.type
= SOCK_STREAM
;
396 else if (streq(lvalue
, "ListenDatagram"))
397 p
->address
.type
= SOCK_DGRAM
;
399 assert(streq(lvalue
, "ListenSequentialPacket"));
400 p
->address
.type
= SOCK_SEQPACKET
;
403 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
404 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
410 p
->auxiliary_fds
= NULL
;
411 p
->n_auxiliary_fds
= 0;
415 LIST_FIND_TAIL(port
, s
->ports
, tail
);
416 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
418 LIST_PREPEND(port
, s
->ports
, p
);
424 int config_parse_socket_protocol(const char *unit
,
425 const char *filename
,
428 unsigned section_line
,
443 if (streq(rvalue
, "udplite"))
444 s
->socket_protocol
= IPPROTO_UDPLITE
;
445 else if (streq(rvalue
, "sctp"))
446 s
->socket_protocol
= IPPROTO_SCTP
;
448 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
455 int config_parse_socket_bind(const char *unit
,
456 const char *filename
,
459 unsigned section_line
,
467 SocketAddressBindIPv6Only b
;
476 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
480 r
= parse_boolean(rvalue
);
482 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
486 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
488 s
->bind_ipv6_only
= b
;
493 int config_parse_exec_nice(const char *unit
,
494 const char *filename
,
497 unsigned section_line
,
504 ExecContext
*c
= data
;
512 r
= safe_atoi(rvalue
, &priority
);
514 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
518 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
519 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Nice priority out of range, ignoring: %s", rvalue
);
529 int config_parse_exec_oom_score_adjust(const char* unit
,
530 const char *filename
,
533 unsigned section_line
,
540 ExecContext
*c
= data
;
548 r
= safe_atoi(rvalue
, &oa
);
550 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
554 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
555 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
559 c
->oom_score_adjust
= oa
;
560 c
->oom_score_adjust_set
= true;
565 int config_parse_exec(
567 const char *filename
,
570 unsigned section_line
,
577 _cleanup_free_
char *cmd
= NULL
;
578 ExecCommand
**e
= data
;
591 rvalue
+= strspn(rvalue
, WHITESPACE
);
593 if (isempty(rvalue
)) {
594 /* An empty assignment resets the list */
595 *e
= exec_command_free_list(*e
);
599 r
= unit_full_printf(u
, rvalue
, &cmd
);
601 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
607 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
608 bool separate_argv0
= false, ignore
= false;
609 _cleanup_free_ ExecCommand
*nce
= NULL
;
610 _cleanup_strv_free_
char **n
= NULL
;
611 size_t nlen
= 0, nbufsize
= 0;
617 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
622 for (i
= 0; i
< 2; i
++) {
623 /* We accept an absolute path as first argument, or
624 * alternatively an absolute prefixed with @ to allow
625 * overriding of argv[0]. */
626 if (*f
== '-' && !ignore
)
628 else if (*f
== '@' && !separate_argv0
)
629 separate_argv0
= true;
636 /* First word is either "-" or "@" with no command. */
637 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty path in command line, ignoring: \"%s\"", rvalue
);
640 if (!string_is_safe(f
)) {
641 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path contains special characters, ignoring: %s", rvalue
);
644 if (!path_is_absolute(f
)) {
645 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path is not absolute, ignoring: %s", rvalue
);
648 if (endswith(f
, "/")) {
649 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path specifies a directory, ignoring: %s", rvalue
);
653 if (f
== firstword
) {
662 if (!separate_argv0
) {
663 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
672 path_kill_slashes(path
);
674 while (!isempty(p
)) {
675 _cleanup_free_
char *word
= NULL
;
677 /* Check explicitly for an unquoted semicolon as
678 * command separator token. */
679 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
681 p
+= strspn(p
, WHITESPACE
);
686 /* Check for \; explicitly, to not confuse it with \\;
687 * or "\;" or "\\;" etc. extract_first_word would
688 * return the same for all of those. */
689 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
691 p
+= strspn(p
, WHITESPACE
);
692 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
702 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
708 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
716 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
720 nce
= new0(ExecCommand
, 1);
726 nce
->ignore
= ignore
;
728 exec_command_append_list(e
, nce
);
730 /* Do not _cleanup_free_ these. */
741 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
742 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
744 int config_parse_socket_bindtodevice(const char* unit
,
745 const char *filename
,
748 unsigned section_line
,
763 if (rvalue
[0] && !streq(rvalue
, "*")) {
770 free(s
->bind_to_device
);
771 s
->bind_to_device
= n
;
776 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
777 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
779 int config_parse_exec_io_class(const char *unit
,
780 const char *filename
,
783 unsigned section_line
,
790 ExecContext
*c
= data
;
798 x
= ioprio_class_from_string(rvalue
);
800 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
804 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
805 c
->ioprio_set
= true;
810 int config_parse_exec_io_priority(const char *unit
,
811 const char *filename
,
814 unsigned section_line
,
821 ExecContext
*c
= data
;
829 r
= safe_atoi(rvalue
, &i
);
830 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
831 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
835 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
836 c
->ioprio_set
= true;
841 int config_parse_exec_cpu_sched_policy(const char *unit
,
842 const char *filename
,
845 unsigned section_line
,
853 ExecContext
*c
= data
;
861 x
= sched_policy_from_string(rvalue
);
863 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
867 c
->cpu_sched_policy
= x
;
868 /* Moving to or from real-time policy? We need to adjust the priority */
869 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
870 c
->cpu_sched_set
= true;
875 int config_parse_exec_cpu_sched_prio(const char *unit
,
876 const char *filename
,
879 unsigned section_line
,
886 ExecContext
*c
= data
;
894 r
= safe_atoi(rvalue
, &i
);
896 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
900 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
901 min
= sched_get_priority_min(c
->cpu_sched_policy
);
902 max
= sched_get_priority_max(c
->cpu_sched_policy
);
904 if (i
< min
|| i
> max
) {
905 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
909 c
->cpu_sched_priority
= i
;
910 c
->cpu_sched_set
= true;
915 int config_parse_exec_cpu_affinity(const char *unit
,
916 const char *filename
,
919 unsigned section_line
,
926 ExecContext
*c
= data
;
927 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
935 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
943 /* An empty assignment resets the CPU list */
949 c
->cpuset_ncpus
= ncpus
;
954 int config_parse_exec_secure_bits(const char *unit
,
955 const char *filename
,
958 unsigned section_line
,
965 ExecContext
*c
= data
;
967 const char *word
, *state
;
974 if (isempty(rvalue
)) {
975 /* An empty assignment resets the field */
980 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
981 if (first_word(word
, "keep-caps"))
982 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
983 else if (first_word(word
, "keep-caps-locked"))
984 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
985 else if (first_word(word
, "no-setuid-fixup"))
986 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
987 else if (first_word(word
, "no-setuid-fixup-locked"))
988 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
989 else if (first_word(word
, "noroot"))
990 c
->secure_bits
|= 1<<SECURE_NOROOT
;
991 else if (first_word(word
, "noroot-locked"))
992 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
994 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse secure bits, ignoring: %s", rvalue
);
999 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, garbage at the end, ignoring.");
1004 int config_parse_capability_set(
1006 const char *filename
,
1008 const char *section
,
1009 unsigned section_line
,
1016 uint64_t *capability_set
= data
;
1017 uint64_t sum
= 0, initial
= 0;
1018 bool invert
= false;
1026 if (rvalue
[0] == '~') {
1031 if (strcmp(lvalue
, "CapabilityBoundingSet") == 0)
1032 initial
= CAP_ALL
; /* initialized to all bits on */
1033 /* else "AmbientCapabilities" initialized to all bits off */
1037 _cleanup_free_
char *word
= NULL
;
1040 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1046 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word, ignoring: %s", rvalue
);
1050 cap
= capability_from_name(word
);
1052 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word
);
1056 sum
|= ((uint64_t) UINT64_C(1)) << (uint64_t) cap
;
1059 sum
= invert
? ~sum
: sum
;
1061 if (sum
== 0 || *capability_set
== initial
)
1062 /* "" or uninitialized data -> replace */
1063 *capability_set
= sum
;
1065 /* previous data -> merge */
1066 *capability_set
|= sum
;
1071 int config_parse_limit(
1073 const char *filename
,
1075 const char *section
,
1076 unsigned section_line
,
1083 struct rlimit
**rl
= data
, d
= {};
1091 r
= rlimit_parse(ltype
, rvalue
, &d
);
1093 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1097 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1104 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1112 #ifdef HAVE_SYSV_COMPAT
1113 int config_parse_sysv_priority(const char *unit
,
1114 const char *filename
,
1116 const char *section
,
1117 unsigned section_line
,
1124 int *priority
= data
;
1132 r
= safe_atoi(rvalue
, &i
);
1133 if (r
< 0 || i
< 0) {
1134 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1138 *priority
= (int) i
;
1143 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1144 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1146 int config_parse_exec_mount_flags(const char *unit
,
1147 const char *filename
,
1149 const char *section
,
1150 unsigned section_line
,
1158 unsigned long flags
= 0;
1159 ExecContext
*c
= data
;
1166 if (streq(rvalue
, "shared"))
1168 else if (streq(rvalue
, "slave"))
1170 else if (streq(rvalue
, "private"))
1173 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1177 c
->mount_flags
= flags
;
1182 int config_parse_exec_selinux_context(
1184 const char *filename
,
1186 const char *section
,
1187 unsigned section_line
,
1194 ExecContext
*c
= data
;
1205 if (isempty(rvalue
)) {
1206 c
->selinux_context
= mfree(c
->selinux_context
);
1207 c
->selinux_context_ignore
= false;
1211 if (rvalue
[0] == '-') {
1217 r
= unit_name_printf(u
, rvalue
, &k
);
1219 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1223 free(c
->selinux_context
);
1224 c
->selinux_context
= k
;
1225 c
->selinux_context_ignore
= ignore
;
1230 int config_parse_exec_apparmor_profile(
1232 const char *filename
,
1234 const char *section
,
1235 unsigned section_line
,
1242 ExecContext
*c
= data
;
1253 if (isempty(rvalue
)) {
1254 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1255 c
->apparmor_profile_ignore
= false;
1259 if (rvalue
[0] == '-') {
1265 r
= unit_name_printf(u
, rvalue
, &k
);
1267 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1271 free(c
->apparmor_profile
);
1272 c
->apparmor_profile
= k
;
1273 c
->apparmor_profile_ignore
= ignore
;
1278 int config_parse_exec_smack_process_label(
1280 const char *filename
,
1282 const char *section
,
1283 unsigned section_line
,
1290 ExecContext
*c
= data
;
1301 if (isempty(rvalue
)) {
1302 c
->smack_process_label
= mfree(c
->smack_process_label
);
1303 c
->smack_process_label_ignore
= false;
1307 if (rvalue
[0] == '-') {
1313 r
= unit_name_printf(u
, rvalue
, &k
);
1315 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1319 free(c
->smack_process_label
);
1320 c
->smack_process_label
= k
;
1321 c
->smack_process_label_ignore
= ignore
;
1326 int config_parse_timer(const char *unit
,
1327 const char *filename
,
1329 const char *section
,
1330 unsigned section_line
,
1341 CalendarSpec
*c
= NULL
;
1348 if (isempty(rvalue
)) {
1349 /* Empty assignment resets list */
1350 timer_free_values(t
);
1354 b
= timer_base_from_string(lvalue
);
1356 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1360 if (b
== TIMER_CALENDAR
) {
1361 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1362 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", rvalue
);
1366 if (parse_sec(rvalue
, &u
) < 0) {
1367 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", rvalue
);
1372 v
= new0(TimerValue
, 1);
1374 calendar_spec_free(c
);
1380 v
->calendar_spec
= c
;
1382 LIST_PREPEND(value
, t
->values
, v
);
1387 int config_parse_trigger_unit(
1389 const char *filename
,
1391 const char *section
,
1392 unsigned section_line
,
1399 _cleanup_free_
char *p
= NULL
;
1409 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1410 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1414 r
= unit_name_printf(u
, rvalue
, &p
);
1416 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1420 type
= unit_name_to_type(p
);
1422 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1426 if (type
== u
->type
) {
1427 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1431 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1433 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1440 int config_parse_path_spec(const char *unit
,
1441 const char *filename
,
1443 const char *section
,
1444 unsigned section_line
,
1454 _cleanup_free_
char *k
= NULL
;
1462 if (isempty(rvalue
)) {
1463 /* Empty assignment clears list */
1468 b
= path_type_from_string(lvalue
);
1470 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1474 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1476 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1480 if (!path_is_absolute(k
)) {
1481 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1485 s
= new0(PathSpec
, 1);
1490 s
->path
= path_kill_slashes(k
);
1495 LIST_PREPEND(spec
, p
->specs
, s
);
1500 int config_parse_socket_service(
1502 const char *filename
,
1504 const char *section
,
1505 unsigned section_line
,
1512 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1513 _cleanup_free_
char *p
= NULL
;
1523 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1525 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1529 if (!endswith(p
, ".service")) {
1530 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1534 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1536 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1540 unit_ref_set(&s
->service
, x
);
1545 int config_parse_fdname(
1547 const char *filename
,
1549 const char *section
,
1550 unsigned section_line
,
1557 _cleanup_free_
char *p
= NULL
;
1566 if (isempty(rvalue
)) {
1567 s
->fdname
= mfree(s
->fdname
);
1571 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1573 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1577 if (!fdname_is_valid(p
)) {
1578 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1589 int config_parse_service_sockets(
1591 const char *filename
,
1593 const char *section
,
1594 unsigned section_line
,
1612 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1614 r
= extract_first_word(&p
, &word
, NULL
, 0);
1620 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1624 r
= unit_name_printf(UNIT(s
), word
, &k
);
1626 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1630 if (!endswith(k
, ".socket")) {
1631 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1635 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1637 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1639 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1641 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1647 int config_parse_bus_name(
1649 const char *filename
,
1651 const char *section
,
1652 unsigned section_line
,
1659 _cleanup_free_
char *k
= NULL
;
1668 r
= unit_full_printf(u
, rvalue
, &k
);
1670 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1674 if (!service_name_is_valid(k
)) {
1675 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1679 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1682 int config_parse_service_timeout(
1684 const char *filename
,
1686 const char *section
,
1687 unsigned section_line
,
1694 Service
*s
= userdata
;
1703 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1705 r
= parse_sec(rvalue
, &usec
);
1707 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1711 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1712 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1713 * all other timeouts. */
1715 usec
= USEC_INFINITY
;
1717 if (!streq(lvalue
, "TimeoutStopSec")) {
1718 s
->start_timeout_defined
= true;
1719 s
->timeout_start_usec
= usec
;
1722 if (!streq(lvalue
, "TimeoutStartSec"))
1723 s
->timeout_stop_usec
= usec
;
1728 int config_parse_sec_fix_0(
1730 const char *filename
,
1732 const char *section
,
1733 unsigned section_line
,
1740 usec_t
*usec
= data
;
1748 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1749 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1752 r
= parse_sec(rvalue
, usec
);
1754 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1759 *usec
= USEC_INFINITY
;
1764 int config_parse_busname_service(
1766 const char *filename
,
1768 const char *section
,
1769 unsigned section_line
,
1776 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1780 _cleanup_free_
char *p
= NULL
;
1787 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1789 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1793 if (!endswith(p
, ".service")) {
1794 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1798 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1800 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1804 unit_ref_set(&n
->service
, x
);
1809 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1811 int config_parse_bus_policy(
1813 const char *filename
,
1815 const char *section
,
1816 unsigned section_line
,
1823 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1824 _cleanup_free_
char *id_str
= NULL
;
1825 BusName
*busname
= data
;
1833 p
= new0(BusNamePolicy
, 1);
1837 if (streq(lvalue
, "AllowUser"))
1838 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1839 else if (streq(lvalue
, "AllowGroup"))
1840 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1842 assert_not_reached("Unknown lvalue");
1844 id_str
= strdup(rvalue
);
1848 access_str
= strpbrk(id_str
, WHITESPACE
);
1850 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy value '%s'", rvalue
);
1856 access_str
+= strspn(access_str
, WHITESPACE
);
1858 p
->access
= bus_policy_access_from_string(access_str
);
1859 if (p
->access
< 0) {
1860 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy access type '%s'", access_str
);
1867 LIST_PREPEND(policy
, busname
->policy
, p
);
1873 int config_parse_working_directory(
1875 const char *filename
,
1877 const char *section
,
1878 unsigned section_line
,
1885 ExecContext
*c
= data
;
1896 if (rvalue
[0] == '-') {
1902 if (streq(rvalue
, "~")) {
1903 c
->working_directory_home
= true;
1904 c
->working_directory
= mfree(c
->working_directory
);
1906 _cleanup_free_
char *k
= NULL
;
1908 r
= unit_full_printf(u
, rvalue
, &k
);
1910 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
1914 path_kill_slashes(k
);
1916 if (!utf8_is_valid(k
)) {
1917 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
1921 if (!path_is_absolute(k
)) {
1922 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
1926 free(c
->working_directory
);
1927 c
->working_directory
= k
;
1930 c
->working_directory_home
= false;
1933 c
->working_directory_missing_ok
= missing_ok
;
1937 int config_parse_unit_env_file(const char *unit
,
1938 const char *filename
,
1940 const char *section
,
1941 unsigned section_line
,
1950 _cleanup_free_
char *n
= NULL
;
1958 if (isempty(rvalue
)) {
1959 /* Empty assignment frees the list */
1960 *env
= strv_free(*env
);
1964 r
= unit_full_printf(u
, rvalue
, &n
);
1966 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1970 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
1971 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
1975 r
= strv_extend(env
, n
);
1982 int config_parse_environ(const char *unit
,
1983 const char *filename
,
1985 const char *section
,
1986 unsigned section_line
,
1995 const char *word
, *state
;
1997 _cleanup_free_
char *k
= NULL
;
2005 if (isempty(rvalue
)) {
2006 /* Empty assignment resets the list */
2007 *env
= strv_free(*env
);
2012 r
= unit_full_printf(u
, rvalue
, &k
);
2014 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2025 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
2026 _cleanup_free_
char *n
= NULL
;
2029 r
= cunescape_length(word
, l
, 0, &n
);
2031 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
2035 if (!env_assignment_is_valid(n
)) {
2036 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid environment assignment, ignoring: %s", rvalue
);
2040 x
= strv_env_set(*env
, n
);
2047 if (!isempty(state
))
2048 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2053 int config_parse_pass_environ(const char *unit
,
2054 const char *filename
,
2056 const char *section
,
2057 unsigned section_line
,
2064 const char *whole_rvalue
= rvalue
;
2065 char*** passenv
= data
;
2066 _cleanup_strv_free_
char **n
= NULL
;
2067 size_t nlen
= 0, nbufsize
= 0;
2075 if (isempty(rvalue
)) {
2076 /* Empty assignment resets the list */
2077 *passenv
= strv_free(*passenv
);
2082 _cleanup_free_
char *word
= NULL
;
2084 r
= extract_first_word(&rvalue
, &word
, WHITESPACE
, EXTRACT_QUOTES
);
2090 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2091 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2095 if (!env_name_is_valid(word
)) {
2096 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2097 "Invalid environment name for %s, ignoring: %s", lvalue
, word
);
2101 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2109 r
= strv_extend_strv(passenv
, n
, true);
2117 int config_parse_ip_tos(const char *unit
,
2118 const char *filename
,
2120 const char *section
,
2121 unsigned section_line
,
2128 int *ip_tos
= data
, x
;
2135 x
= ip_tos_from_string(rvalue
);
2137 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2145 int config_parse_unit_condition_path(
2147 const char *filename
,
2149 const char *section
,
2150 unsigned section_line
,
2157 _cleanup_free_
char *p
= NULL
;
2158 Condition
**list
= data
, *c
;
2159 ConditionType t
= ltype
;
2160 bool trigger
, negate
;
2169 if (isempty(rvalue
)) {
2170 /* Empty assignment resets the list */
2171 *list
= condition_free_list(*list
);
2175 trigger
= rvalue
[0] == '|';
2179 negate
= rvalue
[0] == '!';
2183 r
= unit_full_printf(u
, rvalue
, &p
);
2185 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2189 if (!path_is_absolute(p
)) {
2190 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2194 c
= condition_new(t
, p
, trigger
, negate
);
2198 LIST_PREPEND(conditions
, *list
, c
);
2202 int config_parse_unit_condition_string(
2204 const char *filename
,
2206 const char *section
,
2207 unsigned section_line
,
2214 _cleanup_free_
char *s
= NULL
;
2215 Condition
**list
= data
, *c
;
2216 ConditionType t
= ltype
;
2217 bool trigger
, negate
;
2226 if (isempty(rvalue
)) {
2227 /* Empty assignment resets the list */
2228 *list
= condition_free_list(*list
);
2232 trigger
= rvalue
[0] == '|';
2236 negate
= rvalue
[0] == '!';
2240 r
= unit_full_printf(u
, rvalue
, &s
);
2242 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2246 c
= condition_new(t
, s
, trigger
, negate
);
2250 LIST_PREPEND(conditions
, *list
, c
);
2254 int config_parse_unit_condition_null(
2256 const char *filename
,
2258 const char *section
,
2259 unsigned section_line
,
2266 Condition
**list
= data
, *c
;
2267 bool trigger
, negate
;
2275 if (isempty(rvalue
)) {
2276 /* Empty assignment resets the list */
2277 *list
= condition_free_list(*list
);
2281 trigger
= rvalue
[0] == '|';
2285 negate
= rvalue
[0] == '!';
2289 b
= parse_boolean(rvalue
);
2291 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2298 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2302 LIST_PREPEND(conditions
, *list
, c
);
2306 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2307 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2309 int config_parse_unit_requires_mounts_for(
2311 const char *filename
,
2313 const char *section
,
2314 unsigned section_line
,
2322 const char *word
, *state
;
2330 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2332 _cleanup_free_
char *n
;
2334 n
= strndup(word
, l
);
2338 if (!utf8_is_valid(n
)) {
2339 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2343 r
= unit_require_mounts_for(u
, n
);
2345 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount for, ignoring: %s", rvalue
);
2349 if (!isempty(state
))
2350 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2355 int config_parse_documentation(const char *unit
,
2356 const char *filename
,
2358 const char *section
,
2359 unsigned section_line
,
2375 if (isempty(rvalue
)) {
2376 /* Empty assignment resets the list */
2377 u
->documentation
= strv_free(u
->documentation
);
2381 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2382 rvalue
, data
, userdata
);
2386 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2388 if (documentation_url_is_valid(*a
))
2391 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2402 int config_parse_syscall_filter(
2404 const char *filename
,
2406 const char *section
,
2407 unsigned section_line
,
2414 static const char default_syscalls
[] =
2421 ExecContext
*c
= data
;
2423 bool invert
= false;
2424 const char *word
, *state
;
2433 if (isempty(rvalue
)) {
2434 /* Empty assignment resets the list */
2435 c
->syscall_filter
= set_free(c
->syscall_filter
);
2436 c
->syscall_whitelist
= false;
2440 if (rvalue
[0] == '~') {
2445 if (!c
->syscall_filter
) {
2446 c
->syscall_filter
= set_new(NULL
);
2447 if (!c
->syscall_filter
)
2451 /* Allow everything but the ones listed */
2452 c
->syscall_whitelist
= false;
2456 /* Allow nothing but the ones listed */
2457 c
->syscall_whitelist
= true;
2459 /* Accept default syscalls if we are on a whitelist */
2460 NULSTR_FOREACH(i
, default_syscalls
) {
2463 id
= seccomp_syscall_resolve_name(i
);
2467 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2476 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2477 _cleanup_free_
char *t
= NULL
;
2480 t
= strndup(word
, l
);
2484 id
= seccomp_syscall_resolve_name(t
);
2486 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2490 /* If we previously wanted to forbid a syscall and now
2491 * we want to allow it, then remove it from the list
2493 if (!invert
== c
->syscall_whitelist
) {
2494 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2500 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2502 if (!isempty(state
))
2503 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2505 /* Turn on NNP, but only if it wasn't configured explicitly
2506 * before, and only if we are in user mode. */
2507 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2508 c
->no_new_privileges
= true;
2513 int config_parse_syscall_archs(
2515 const char *filename
,
2517 const char *section
,
2518 unsigned section_line
,
2526 const char *word
, *state
;
2530 if (isempty(rvalue
)) {
2531 *archs
= set_free(*archs
);
2535 r
= set_ensure_allocated(archs
, NULL
);
2539 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2540 _cleanup_free_
char *t
= NULL
;
2543 t
= strndup(word
, l
);
2547 r
= seccomp_arch_from_string(t
, &a
);
2549 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call architecture, ignoring: %s", t
);
2553 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2559 if (!isempty(state
))
2560 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2565 int config_parse_syscall_errno(
2567 const char *filename
,
2569 const char *section
,
2570 unsigned section_line
,
2577 ExecContext
*c
= data
;
2584 if (isempty(rvalue
)) {
2585 /* Empty assignment resets to KILL */
2586 c
->syscall_errno
= 0;
2590 e
= errno_from_name(rvalue
);
2592 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2596 c
->syscall_errno
= e
;
2600 int config_parse_address_families(
2602 const char *filename
,
2604 const char *section
,
2605 unsigned section_line
,
2612 ExecContext
*c
= data
;
2613 bool invert
= false;
2614 const char *word
, *state
;
2622 if (isempty(rvalue
)) {
2623 /* Empty assignment resets the list */
2624 c
->address_families
= set_free(c
->address_families
);
2625 c
->address_families_whitelist
= false;
2629 if (rvalue
[0] == '~') {
2634 if (!c
->address_families
) {
2635 c
->address_families
= set_new(NULL
);
2636 if (!c
->address_families
)
2639 c
->address_families_whitelist
= !invert
;
2642 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2643 _cleanup_free_
char *t
= NULL
;
2646 t
= strndup(word
, l
);
2650 af
= af_from_name(t
);
2652 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse address family, ignoring: %s", t
);
2656 /* If we previously wanted to forbid an address family and now
2657 * we want to allow it, then remove it from the list
2659 if (!invert
== c
->address_families_whitelist
) {
2660 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2666 set_remove(c
->address_families
, INT_TO_PTR(af
));
2668 if (!isempty(state
))
2669 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2675 int config_parse_unit_slice(
2677 const char *filename
,
2679 const char *section
,
2680 unsigned section_line
,
2687 _cleanup_free_
char *k
= NULL
;
2688 Unit
*u
= userdata
, *slice
= NULL
;
2696 r
= unit_name_printf(u
, rvalue
, &k
);
2698 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2702 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2704 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2708 r
= unit_set_slice(u
, slice
);
2710 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2717 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2719 int config_parse_cpu_shares(
2721 const char *filename
,
2723 const char *section
,
2724 unsigned section_line
,
2731 uint64_t *shares
= data
;
2738 r
= cg_cpu_shares_parse(rvalue
, shares
);
2740 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2747 int config_parse_cpu_quota(
2749 const char *filename
,
2751 const char *section
,
2752 unsigned section_line
,
2759 CGroupContext
*c
= data
;
2766 if (isempty(rvalue
)) {
2767 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2771 if (!endswith(rvalue
, "%")) {
2772 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2776 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2777 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' invalid. Ignoring.", rvalue
);
2781 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2786 int config_parse_memory_limit(
2788 const char *filename
,
2790 const char *section
,
2791 unsigned section_line
,
2798 CGroupContext
*c
= data
;
2802 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2803 c
->memory_limit
= (uint64_t) -1;
2807 r
= parse_size(rvalue
, 1024, &bytes
);
2808 if (r
< 0 || bytes
< 1) {
2809 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2813 c
->memory_limit
= bytes
;
2817 int config_parse_tasks_max(
2819 const char *filename
,
2821 const char *section
,
2822 unsigned section_line
,
2829 uint64_t *tasks_max
= data
, u
;
2832 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2833 *tasks_max
= (uint64_t) -1;
2837 r
= safe_atou64(rvalue
, &u
);
2838 if (r
< 0 || u
< 1) {
2839 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2847 int config_parse_device_allow(
2849 const char *filename
,
2851 const char *section
,
2852 unsigned section_line
,
2859 _cleanup_free_
char *path
= NULL
;
2860 CGroupContext
*c
= data
;
2861 CGroupDeviceAllow
*a
;
2865 if (isempty(rvalue
)) {
2866 while (c
->device_allow
)
2867 cgroup_context_free_device_allow(c
, c
->device_allow
);
2872 n
= strcspn(rvalue
, WHITESPACE
);
2873 path
= strndup(rvalue
, n
);
2877 if (!startswith(path
, "/dev/") &&
2878 !startswith(path
, "block-") &&
2879 !startswith(path
, "char-")) {
2880 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2884 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2888 if (!in_charset(m
, "rwm")) {
2889 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
2893 a
= new0(CGroupDeviceAllow
, 1);
2899 a
->r
= !!strchr(m
, 'r');
2900 a
->w
= !!strchr(m
, 'w');
2901 a
->m
= !!strchr(m
, 'm');
2903 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2907 int config_parse_blockio_weight(
2909 const char *filename
,
2911 const char *section
,
2912 unsigned section_line
,
2919 uint64_t *weight
= data
;
2926 r
= cg_blkio_weight_parse(rvalue
, weight
);
2928 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2935 int config_parse_blockio_device_weight(
2937 const char *filename
,
2939 const char *section
,
2940 unsigned section_line
,
2947 _cleanup_free_
char *path
= NULL
;
2948 CGroupBlockIODeviceWeight
*w
;
2949 CGroupContext
*c
= data
;
2959 if (isempty(rvalue
)) {
2960 while (c
->blockio_device_weights
)
2961 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2966 n
= strcspn(rvalue
, WHITESPACE
);
2967 weight
= rvalue
+ n
;
2968 weight
+= strspn(weight
, WHITESPACE
);
2970 if (isempty(weight
)) {
2971 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
2975 path
= strndup(rvalue
, n
);
2979 if (!path_startswith(path
, "/dev")) {
2980 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2984 r
= cg_blkio_weight_parse(weight
, &u
);
2986 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2990 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2992 w
= new0(CGroupBlockIODeviceWeight
, 1);
3001 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3005 int config_parse_blockio_bandwidth(
3007 const char *filename
,
3009 const char *section
,
3010 unsigned section_line
,
3017 _cleanup_free_
char *path
= NULL
;
3018 CGroupBlockIODeviceBandwidth
*b
;
3019 CGroupContext
*c
= data
;
3020 const char *bandwidth
;
3030 read
= streq("BlockIOReadBandwidth", lvalue
);
3032 if (isempty(rvalue
)) {
3033 CGroupBlockIODeviceBandwidth
*next
;
3035 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
3036 if (b
->read
== read
)
3037 cgroup_context_free_blockio_device_bandwidth(c
, b
);
3042 n
= strcspn(rvalue
, WHITESPACE
);
3043 bandwidth
= rvalue
+ n
;
3044 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3047 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3051 path
= strndup(rvalue
, n
);
3055 if (!path_startswith(path
, "/dev")) {
3056 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3060 r
= parse_size(bandwidth
, 1000, &bytes
);
3061 if (r
< 0 || bytes
<= 0) {
3062 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3066 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3072 b
->bandwidth
= bytes
;
3075 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3080 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3082 int config_parse_job_mode_isolate(
3084 const char *filename
,
3086 const char *section
,
3087 unsigned section_line
,
3101 r
= parse_boolean(rvalue
);
3103 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3107 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3111 int config_parse_runtime_directory(
3113 const char *filename
,
3115 const char *section
,
3116 unsigned section_line
,
3125 const char *word
, *state
;
3134 if (isempty(rvalue
)) {
3135 /* Empty assignment resets the list */
3136 *rt
= strv_free(*rt
);
3140 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3141 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3143 t
= strndup(word
, l
);
3147 r
= unit_name_printf(u
, t
, &n
);
3149 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
3153 if (!filename_is_valid(n
)) {
3154 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3158 r
= strv_push(rt
, n
);
3164 if (!isempty(state
))
3165 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3170 int config_parse_set_status(
3172 const char *filename
,
3174 const char *section
,
3175 unsigned section_line
,
3183 const char *word
, *state
;
3185 ExitStatusSet
*status_set
= data
;
3192 /* Empty assignment resets the list */
3193 if (isempty(rvalue
)) {
3194 exit_status_set_free(status_set
);
3198 FOREACH_WORD(word
, l
, rvalue
, state
) {
3199 _cleanup_free_
char *temp
;
3203 temp
= strndup(word
, l
);
3207 r
= safe_atoi(temp
, &val
);
3209 val
= signal_from_string_try_harder(temp
);
3212 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3215 set
= &status_set
->signal
;
3217 if (val
< 0 || val
> 255) {
3218 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3221 set
= &status_set
->status
;
3224 r
= set_ensure_allocated(set
, NULL
);
3228 r
= set_put(*set
, INT_TO_PTR(val
));
3230 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3234 if (!isempty(state
))
3235 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3240 int config_parse_namespace_path_strv(
3242 const char *filename
,
3244 const char *section
,
3245 unsigned section_line
,
3262 if (isempty(rvalue
)) {
3263 /* Empty assignment resets the list */
3264 *sv
= strv_free(*sv
);
3268 prev
= cur
= rvalue
;
3270 _cleanup_free_
char *word
= NULL
;
3273 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3279 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage, ignoring: %s", prev
);
3283 if (!utf8_is_valid(word
)) {
3284 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3289 offset
= word
[0] == '-';
3290 if (!path_is_absolute(word
+ offset
)) {
3291 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", word
);
3296 path_kill_slashes(word
+ offset
);
3298 r
= strv_push(sv
, word
);
3309 int config_parse_no_new_privileges(
3311 const char *filename
,
3313 const char *section
,
3314 unsigned section_line
,
3321 ExecContext
*c
= data
;
3329 k
= parse_boolean(rvalue
);
3331 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3335 c
->no_new_privileges
= !!k
;
3336 c
->no_new_privileges_set
= true;
3341 int config_parse_protect_home(
3343 const char *filename
,
3345 const char *section
,
3346 unsigned section_line
,
3353 ExecContext
*c
= data
;
3361 /* Our enum shall be a superset of booleans, hence first try
3362 * to parse as as boolean, and then as enum */
3364 k
= parse_boolean(rvalue
);
3366 c
->protect_home
= PROTECT_HOME_YES
;
3368 c
->protect_home
= PROTECT_HOME_NO
;
3372 h
= protect_home_from_string(rvalue
);
3374 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
3378 c
->protect_home
= h
;
3384 int config_parse_protect_system(
3386 const char *filename
,
3388 const char *section
,
3389 unsigned section_line
,
3396 ExecContext
*c
= data
;
3404 /* Our enum shall be a superset of booleans, hence first try
3405 * to parse as as boolean, and then as enum */
3407 k
= parse_boolean(rvalue
);
3409 c
->protect_system
= PROTECT_SYSTEM_YES
;
3411 c
->protect_system
= PROTECT_SYSTEM_NO
;
3415 s
= protect_system_from_string(rvalue
);
3417 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
3421 c
->protect_system
= s
;
3427 #define FOLLOW_MAX 8
3429 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3440 /* This will update the filename pointer if the loaded file is
3441 * reached by a symlink. The old string will be freed. */
3444 char *target
, *name
;
3446 if (c
++ >= FOLLOW_MAX
)
3449 path_kill_slashes(*filename
);
3451 /* Add the file name we are currently looking at to
3452 * the names of this unit, but only if it is a valid
3454 name
= basename(*filename
);
3456 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3458 id
= set_get(names
, name
);
3464 r
= set_consume(names
, id
);
3470 /* Try to open the file name, but don't if its a symlink */
3471 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3478 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3479 r
= readlink_and_make_absolute(*filename
, &target
);
3487 f
= fdopen(fd
, "re");
3498 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3506 /* Let's try to add in all symlink names we found */
3507 while ((k
= set_steal_first(names
))) {
3509 /* First try to merge in the other name into our
3511 r
= unit_merge_by_name(*u
, k
);
3515 /* Hmm, we couldn't merge the other unit into
3516 * ours? Then let's try it the other way
3519 other
= manager_get_unit((*u
)->manager
, k
);
3523 r
= unit_merge(other
, *u
);
3526 return merge_by_names(u
, names
, NULL
);
3534 unit_choose_id(*u
, id
);
3542 static int load_from_path(Unit
*u
, const char *path
) {
3544 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3545 _cleanup_fclose_
FILE *f
= NULL
;
3546 _cleanup_free_
char *filename
= NULL
;
3554 symlink_names
= set_new(&string_hash_ops
);
3558 if (path_is_absolute(path
)) {
3560 filename
= strdup(path
);
3564 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3566 filename
= mfree(filename
);
3574 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3576 /* Instead of opening the path right away, we manually
3577 * follow all symlinks and add their name to our unit
3578 * name set while doing so */
3579 filename
= path_make_absolute(path
, *p
);
3583 if (u
->manager
->unit_path_cache
&&
3584 !set_get(u
->manager
->unit_path_cache
, filename
))
3587 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3590 filename
= mfree(filename
);
3594 /* Empty the symlink names for the next run */
3595 set_clear_free(symlink_names
);
3604 /* Hmm, no suitable file found? */
3608 r
= merge_by_names(&merged
, symlink_names
, id
);
3613 u
->load_state
= UNIT_MERGED
;
3617 if (fstat(fileno(f
), &st
) < 0)
3620 if (null_or_empty(&st
))
3621 u
->load_state
= UNIT_MASKED
;
3623 u
->load_state
= UNIT_LOADED
;
3625 /* Now, parse the file contents */
3626 r
= config_parse(u
->id
, filename
, f
,
3627 UNIT_VTABLE(u
)->sections
,
3628 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3629 false, true, false, u
);
3634 free(u
->fragment_path
);
3635 u
->fragment_path
= filename
;
3638 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3640 if (u
->source_path
) {
3641 if (stat(u
->source_path
, &st
) >= 0)
3642 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3644 u
->source_mtime
= 0;
3650 int unit_load_fragment(Unit
*u
) {
3656 assert(u
->load_state
== UNIT_STUB
);
3660 u
->load_state
= UNIT_LOADED
;
3664 /* First, try to find the unit under its id. We always look
3665 * for unit files in the default directories, to make it easy
3666 * to override things by placing things in /etc/systemd/system */
3667 r
= load_from_path(u
, u
->id
);
3671 /* Try to find an alias we can load this with */
3672 if (u
->load_state
== UNIT_STUB
) {
3673 SET_FOREACH(t
, u
->names
, i
) {
3678 r
= load_from_path(u
, t
);
3682 if (u
->load_state
!= UNIT_STUB
)
3687 /* And now, try looking for it under the suggested (originally linked) path */
3688 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3690 r
= load_from_path(u
, u
->fragment_path
);
3694 if (u
->load_state
== UNIT_STUB
)
3695 /* Hmm, this didn't work? Then let's get rid
3696 * of the fragment path stored for us, so that
3697 * we don't point to an invalid location. */
3698 u
->fragment_path
= mfree(u
->fragment_path
);
3701 /* Look for a template */
3702 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3703 _cleanup_free_
char *k
= NULL
;
3705 r
= unit_name_template(u
->id
, &k
);
3709 r
= load_from_path(u
, k
);
3713 if (u
->load_state
== UNIT_STUB
) {
3714 SET_FOREACH(t
, u
->names
, i
) {
3715 _cleanup_free_
char *z
= NULL
;
3720 r
= unit_name_template(t
, &z
);
3724 r
= load_from_path(u
, z
);
3728 if (u
->load_state
!= UNIT_STUB
)
3737 void unit_dump_config_items(FILE *f
) {
3738 static const struct {
3739 const ConfigParserCallback callback
;
3742 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3743 { config_parse_warn_compat
, "NOTSUPPORTED" },
3745 { config_parse_int
, "INTEGER" },
3746 { config_parse_unsigned
, "UNSIGNED" },
3747 { config_parse_iec_size
, "SIZE" },
3748 { config_parse_iec_uint64
, "SIZE" },
3749 { config_parse_si_size
, "SIZE" },
3750 { config_parse_bool
, "BOOLEAN" },
3751 { config_parse_string
, "STRING" },
3752 { config_parse_path
, "PATH" },
3753 { config_parse_unit_path_printf
, "PATH" },
3754 { config_parse_strv
, "STRING [...]" },
3755 { config_parse_exec_nice
, "NICE" },
3756 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3757 { config_parse_exec_io_class
, "IOCLASS" },
3758 { config_parse_exec_io_priority
, "IOPRIORITY" },
3759 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3760 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3761 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3762 { config_parse_mode
, "MODE" },
3763 { config_parse_unit_env_file
, "FILE" },
3764 { config_parse_output
, "OUTPUT" },
3765 { config_parse_input
, "INPUT" },
3766 { config_parse_log_facility
, "FACILITY" },
3767 { config_parse_log_level
, "LEVEL" },
3768 { config_parse_exec_secure_bits
, "SECUREBITS" },
3769 { config_parse_capability_set
, "BOUNDINGSET" },
3770 { config_parse_limit
, "LIMIT" },
3771 { config_parse_unit_deps
, "UNIT [...]" },
3772 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3773 { config_parse_service_type
, "SERVICETYPE" },
3774 { config_parse_service_restart
, "SERVICERESTART" },
3775 #ifdef HAVE_SYSV_COMPAT
3776 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3778 { config_parse_kill_mode
, "KILLMODE" },
3779 { config_parse_signal
, "SIGNAL" },
3780 { config_parse_socket_listen
, "SOCKET [...]" },
3781 { config_parse_socket_bind
, "SOCKETBIND" },
3782 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3783 { config_parse_sec
, "SECONDS" },
3784 { config_parse_nsec
, "NANOSECONDS" },
3785 { config_parse_namespace_path_strv
, "PATH [...]" },
3786 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3787 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3788 { config_parse_unit_string_printf
, "STRING" },
3789 { config_parse_trigger_unit
, "UNIT" },
3790 { config_parse_timer
, "TIMER" },
3791 { config_parse_path_spec
, "PATH" },
3792 { config_parse_notify_access
, "ACCESS" },
3793 { config_parse_ip_tos
, "TOS" },
3794 { config_parse_unit_condition_path
, "CONDITION" },
3795 { config_parse_unit_condition_string
, "CONDITION" },
3796 { config_parse_unit_condition_null
, "CONDITION" },
3797 { config_parse_unit_slice
, "SLICE" },
3798 { config_parse_documentation
, "URL" },
3799 { config_parse_service_timeout
, "SECONDS" },
3800 { config_parse_failure_action
, "ACTION" },
3801 { config_parse_set_status
, "STATUS" },
3802 { config_parse_service_sockets
, "SOCKETS" },
3803 { config_parse_environ
, "ENVIRON" },
3805 { config_parse_syscall_filter
, "SYSCALLS" },
3806 { config_parse_syscall_archs
, "ARCHS" },
3807 { config_parse_syscall_errno
, "ERRNO" },
3808 { config_parse_address_families
, "FAMILIES" },
3810 { config_parse_cpu_shares
, "SHARES" },
3811 { config_parse_memory_limit
, "LIMIT" },
3812 { config_parse_device_allow
, "DEVICE" },
3813 { config_parse_device_policy
, "POLICY" },
3814 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3815 { config_parse_blockio_weight
, "WEIGHT" },
3816 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3817 { config_parse_long
, "LONG" },
3818 { config_parse_socket_service
, "SERVICE" },
3820 { config_parse_exec_selinux_context
, "LABEL" },
3822 { config_parse_job_mode
, "MODE" },
3823 { config_parse_job_mode_isolate
, "BOOLEAN" },
3824 { config_parse_personality
, "PERSONALITY" },
3827 const char *prev
= NULL
;
3832 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3833 const char *rvalue
= "OTHER", *lvalue
;
3837 const ConfigPerfItem
*p
;
3839 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3841 dot
= strchr(i
, '.');
3842 lvalue
= dot
? dot
+ 1 : i
;
3846 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3850 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3853 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3854 if (p
->parse
== table
[j
].callback
) {
3855 rvalue
= table
[j
].rvalue
;
3859 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);