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 ExecCommand
**e
= data
;
588 rvalue
+= strspn(rvalue
, WHITESPACE
);
590 if (isempty(rvalue
)) {
591 /* An empty assignment resets the list */
592 *e
= exec_command_free_list(*e
);
598 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
599 bool separate_argv0
= false, ignore
= false;
600 _cleanup_free_ ExecCommand
*nce
= NULL
;
601 _cleanup_strv_free_
char **n
= NULL
;
602 size_t nlen
= 0, nbufsize
= 0;
608 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
613 for (i
= 0; i
< 2; i
++) {
614 /* We accept an absolute path as first argument, or
615 * alternatively an absolute prefixed with @ to allow
616 * overriding of argv[0]. */
617 if (*f
== '-' && !ignore
)
619 else if (*f
== '@' && !separate_argv0
)
620 separate_argv0
= true;
627 /* First word is either "-" or "@" with no command. */
628 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty path in command line, ignoring: \"%s\"", rvalue
);
631 if (!string_is_safe(f
)) {
632 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path contains special characters, ignoring: %s", rvalue
);
635 if (!path_is_absolute(f
)) {
636 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path is not absolute, ignoring: %s", rvalue
);
639 if (endswith(f
, "/")) {
640 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path specifies a directory, ignoring: %s", rvalue
);
644 if (f
== firstword
) {
653 if (!separate_argv0
) {
654 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
663 path_kill_slashes(path
);
665 while (!isempty(p
)) {
666 _cleanup_free_
char *word
= NULL
;
668 /* Check explicitly for an unquoted semicolon as
669 * command separator token. */
670 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
672 p
+= strspn(p
, WHITESPACE
);
677 /* Check for \; explicitly, to not confuse it with \\;
678 * or "\;" or "\\;" etc. extract_first_word would
679 * return the same for all of those. */
680 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
682 p
+= strspn(p
, WHITESPACE
);
683 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
693 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
699 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
707 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
711 nce
= new0(ExecCommand
, 1);
717 nce
->ignore
= ignore
;
719 exec_command_append_list(e
, nce
);
721 /* Do not _cleanup_free_ these. */
732 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
733 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
735 int config_parse_socket_bindtodevice(const char* unit
,
736 const char *filename
,
739 unsigned section_line
,
754 if (rvalue
[0] && !streq(rvalue
, "*")) {
761 free(s
->bind_to_device
);
762 s
->bind_to_device
= n
;
767 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
768 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
770 int config_parse_exec_io_class(const char *unit
,
771 const char *filename
,
774 unsigned section_line
,
781 ExecContext
*c
= data
;
789 x
= ioprio_class_from_string(rvalue
);
791 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
795 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
796 c
->ioprio_set
= true;
801 int config_parse_exec_io_priority(const char *unit
,
802 const char *filename
,
805 unsigned section_line
,
812 ExecContext
*c
= data
;
820 r
= safe_atoi(rvalue
, &i
);
821 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
822 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
826 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
827 c
->ioprio_set
= true;
832 int config_parse_exec_cpu_sched_policy(const char *unit
,
833 const char *filename
,
836 unsigned section_line
,
844 ExecContext
*c
= data
;
852 x
= sched_policy_from_string(rvalue
);
854 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
858 c
->cpu_sched_policy
= x
;
859 /* Moving to or from real-time policy? We need to adjust the priority */
860 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
861 c
->cpu_sched_set
= true;
866 int config_parse_exec_cpu_sched_prio(const char *unit
,
867 const char *filename
,
870 unsigned section_line
,
877 ExecContext
*c
= data
;
885 r
= safe_atoi(rvalue
, &i
);
887 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
891 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
892 min
= sched_get_priority_min(c
->cpu_sched_policy
);
893 max
= sched_get_priority_max(c
->cpu_sched_policy
);
895 if (i
< min
|| i
> max
) {
896 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
900 c
->cpu_sched_priority
= i
;
901 c
->cpu_sched_set
= true;
906 int config_parse_exec_cpu_affinity(const char *unit
,
907 const char *filename
,
910 unsigned section_line
,
917 ExecContext
*c
= data
;
918 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
926 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
934 /* An empty assignment resets the CPU list */
940 c
->cpuset_ncpus
= ncpus
;
945 int config_parse_exec_secure_bits(const char *unit
,
946 const char *filename
,
949 unsigned section_line
,
956 ExecContext
*c
= data
;
958 const char *word
, *state
;
965 if (isempty(rvalue
)) {
966 /* An empty assignment resets the field */
971 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
972 if (first_word(word
, "keep-caps"))
973 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
974 else if (first_word(word
, "keep-caps-locked"))
975 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
976 else if (first_word(word
, "no-setuid-fixup"))
977 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
978 else if (first_word(word
, "no-setuid-fixup-locked"))
979 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
980 else if (first_word(word
, "noroot"))
981 c
->secure_bits
|= 1<<SECURE_NOROOT
;
982 else if (first_word(word
, "noroot-locked"))
983 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
985 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse secure bits, ignoring: %s", rvalue
);
990 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, garbage at the end, ignoring.");
995 int config_parse_capability_set(
997 const char *filename
,
1000 unsigned section_line
,
1007 uint64_t *capability_set
= data
;
1008 uint64_t sum
= 0, initial
= 0;
1009 bool invert
= false;
1017 if (rvalue
[0] == '~') {
1022 if (strcmp(lvalue
, "CapabilityBoundingSet") == 0)
1023 initial
= CAP_ALL
; /* initialized to all bits on */
1024 /* else "AmbientCapabilities" initialized to all bits off */
1028 _cleanup_free_
char *word
= NULL
;
1031 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1037 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word, ignoring: %s", rvalue
);
1041 cap
= capability_from_name(word
);
1043 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word
);
1047 sum
|= ((uint64_t) UINT64_C(1)) << (uint64_t) cap
;
1050 sum
= invert
? ~sum
: sum
;
1052 if (sum
== 0 || *capability_set
== initial
)
1053 /* "" or uninitialized data -> replace */
1054 *capability_set
= sum
;
1056 /* previous data -> merge */
1057 *capability_set
|= sum
;
1062 int config_parse_limit(
1064 const char *filename
,
1066 const char *section
,
1067 unsigned section_line
,
1074 struct rlimit
**rl
= data
, d
= {};
1082 r
= rlimit_parse(ltype
, rvalue
, &d
);
1084 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1088 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1095 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1103 #ifdef HAVE_SYSV_COMPAT
1104 int config_parse_sysv_priority(const char *unit
,
1105 const char *filename
,
1107 const char *section
,
1108 unsigned section_line
,
1115 int *priority
= data
;
1123 r
= safe_atoi(rvalue
, &i
);
1124 if (r
< 0 || i
< 0) {
1125 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1129 *priority
= (int) i
;
1134 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1135 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1137 int config_parse_exec_mount_flags(const char *unit
,
1138 const char *filename
,
1140 const char *section
,
1141 unsigned section_line
,
1149 unsigned long flags
= 0;
1150 ExecContext
*c
= data
;
1157 if (streq(rvalue
, "shared"))
1159 else if (streq(rvalue
, "slave"))
1161 else if (streq(rvalue
, "private"))
1164 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1168 c
->mount_flags
= flags
;
1173 int config_parse_exec_selinux_context(
1175 const char *filename
,
1177 const char *section
,
1178 unsigned section_line
,
1185 ExecContext
*c
= data
;
1196 if (isempty(rvalue
)) {
1197 c
->selinux_context
= mfree(c
->selinux_context
);
1198 c
->selinux_context_ignore
= false;
1202 if (rvalue
[0] == '-') {
1208 r
= unit_name_printf(u
, rvalue
, &k
);
1210 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1214 free(c
->selinux_context
);
1215 c
->selinux_context
= k
;
1216 c
->selinux_context_ignore
= ignore
;
1221 int config_parse_exec_apparmor_profile(
1223 const char *filename
,
1225 const char *section
,
1226 unsigned section_line
,
1233 ExecContext
*c
= data
;
1244 if (isempty(rvalue
)) {
1245 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1246 c
->apparmor_profile_ignore
= false;
1250 if (rvalue
[0] == '-') {
1256 r
= unit_name_printf(u
, rvalue
, &k
);
1258 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1262 free(c
->apparmor_profile
);
1263 c
->apparmor_profile
= k
;
1264 c
->apparmor_profile_ignore
= ignore
;
1269 int config_parse_exec_smack_process_label(
1271 const char *filename
,
1273 const char *section
,
1274 unsigned section_line
,
1281 ExecContext
*c
= data
;
1292 if (isempty(rvalue
)) {
1293 c
->smack_process_label
= mfree(c
->smack_process_label
);
1294 c
->smack_process_label_ignore
= false;
1298 if (rvalue
[0] == '-') {
1304 r
= unit_name_printf(u
, rvalue
, &k
);
1306 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1310 free(c
->smack_process_label
);
1311 c
->smack_process_label
= k
;
1312 c
->smack_process_label_ignore
= ignore
;
1317 int config_parse_timer(const char *unit
,
1318 const char *filename
,
1320 const char *section
,
1321 unsigned section_line
,
1332 CalendarSpec
*c
= NULL
;
1339 if (isempty(rvalue
)) {
1340 /* Empty assignment resets list */
1341 timer_free_values(t
);
1345 b
= timer_base_from_string(lvalue
);
1347 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1351 if (b
== TIMER_CALENDAR
) {
1352 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1353 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", rvalue
);
1357 if (parse_sec(rvalue
, &u
) < 0) {
1358 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", rvalue
);
1363 v
= new0(TimerValue
, 1);
1365 calendar_spec_free(c
);
1371 v
->calendar_spec
= c
;
1373 LIST_PREPEND(value
, t
->values
, v
);
1378 int config_parse_trigger_unit(
1380 const char *filename
,
1382 const char *section
,
1383 unsigned section_line
,
1390 _cleanup_free_
char *p
= NULL
;
1400 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1401 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1405 r
= unit_name_printf(u
, rvalue
, &p
);
1407 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1411 type
= unit_name_to_type(p
);
1413 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1417 if (type
== u
->type
) {
1418 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1422 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1424 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1431 int config_parse_path_spec(const char *unit
,
1432 const char *filename
,
1434 const char *section
,
1435 unsigned section_line
,
1445 _cleanup_free_
char *k
= NULL
;
1453 if (isempty(rvalue
)) {
1454 /* Empty assignment clears list */
1459 b
= path_type_from_string(lvalue
);
1461 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1465 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1467 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1471 if (!path_is_absolute(k
)) {
1472 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1476 s
= new0(PathSpec
, 1);
1481 s
->path
= path_kill_slashes(k
);
1486 LIST_PREPEND(spec
, p
->specs
, s
);
1491 int config_parse_socket_service(
1493 const char *filename
,
1495 const char *section
,
1496 unsigned section_line
,
1503 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1504 _cleanup_free_
char *p
= NULL
;
1514 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1516 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1520 if (!endswith(p
, ".service")) {
1521 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1525 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1527 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1531 unit_ref_set(&s
->service
, x
);
1536 int config_parse_fdname(
1538 const char *filename
,
1540 const char *section
,
1541 unsigned section_line
,
1548 _cleanup_free_
char *p
= NULL
;
1557 if (isempty(rvalue
)) {
1558 s
->fdname
= mfree(s
->fdname
);
1562 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1564 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1568 if (!fdname_is_valid(p
)) {
1569 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1580 int config_parse_service_sockets(
1582 const char *filename
,
1584 const char *section
,
1585 unsigned section_line
,
1603 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1605 r
= extract_first_word(&p
, &word
, NULL
, 0);
1611 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1615 r
= unit_name_printf(UNIT(s
), word
, &k
);
1617 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1621 if (!endswith(k
, ".socket")) {
1622 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1626 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1628 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1630 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1632 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1638 int config_parse_bus_name(
1640 const char *filename
,
1642 const char *section
,
1643 unsigned section_line
,
1650 _cleanup_free_
char *k
= NULL
;
1659 r
= unit_full_printf(u
, rvalue
, &k
);
1661 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1665 if (!service_name_is_valid(k
)) {
1666 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1670 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1673 int config_parse_service_timeout(
1675 const char *filename
,
1677 const char *section
,
1678 unsigned section_line
,
1685 Service
*s
= userdata
;
1694 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1696 r
= parse_sec(rvalue
, &usec
);
1698 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1702 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1703 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1704 * all other timeouts. */
1706 usec
= USEC_INFINITY
;
1708 if (!streq(lvalue
, "TimeoutStopSec")) {
1709 s
->start_timeout_defined
= true;
1710 s
->timeout_start_usec
= usec
;
1713 if (!streq(lvalue
, "TimeoutStartSec"))
1714 s
->timeout_stop_usec
= usec
;
1719 int config_parse_sec_fix_0(
1721 const char *filename
,
1723 const char *section
,
1724 unsigned section_line
,
1731 usec_t
*usec
= data
;
1739 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1740 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1743 r
= parse_sec(rvalue
, usec
);
1745 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1750 *usec
= USEC_INFINITY
;
1755 int config_parse_busname_service(
1757 const char *filename
,
1759 const char *section
,
1760 unsigned section_line
,
1767 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1771 _cleanup_free_
char *p
= NULL
;
1778 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1780 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1784 if (!endswith(p
, ".service")) {
1785 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1789 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1791 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1795 unit_ref_set(&n
->service
, x
);
1800 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1802 int config_parse_bus_policy(
1804 const char *filename
,
1806 const char *section
,
1807 unsigned section_line
,
1814 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1815 _cleanup_free_
char *id_str
= NULL
;
1816 BusName
*busname
= data
;
1824 p
= new0(BusNamePolicy
, 1);
1828 if (streq(lvalue
, "AllowUser"))
1829 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1830 else if (streq(lvalue
, "AllowGroup"))
1831 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1833 assert_not_reached("Unknown lvalue");
1835 id_str
= strdup(rvalue
);
1839 access_str
= strpbrk(id_str
, WHITESPACE
);
1841 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy value '%s'", rvalue
);
1847 access_str
+= strspn(access_str
, WHITESPACE
);
1849 p
->access
= bus_policy_access_from_string(access_str
);
1850 if (p
->access
< 0) {
1851 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy access type '%s'", access_str
);
1858 LIST_PREPEND(policy
, busname
->policy
, p
);
1864 int config_parse_working_directory(
1866 const char *filename
,
1868 const char *section
,
1869 unsigned section_line
,
1876 ExecContext
*c
= data
;
1887 if (rvalue
[0] == '-') {
1893 if (streq(rvalue
, "~")) {
1894 c
->working_directory_home
= true;
1895 c
->working_directory
= mfree(c
->working_directory
);
1897 _cleanup_free_
char *k
= NULL
;
1899 r
= unit_full_printf(u
, rvalue
, &k
);
1901 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
1905 path_kill_slashes(k
);
1907 if (!utf8_is_valid(k
)) {
1908 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
1912 if (!path_is_absolute(k
)) {
1913 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
1917 free(c
->working_directory
);
1918 c
->working_directory
= k
;
1921 c
->working_directory_home
= false;
1924 c
->working_directory_missing_ok
= missing_ok
;
1928 int config_parse_unit_env_file(const char *unit
,
1929 const char *filename
,
1931 const char *section
,
1932 unsigned section_line
,
1941 _cleanup_free_
char *n
= NULL
;
1949 if (isempty(rvalue
)) {
1950 /* Empty assignment frees the list */
1951 *env
= strv_free(*env
);
1955 r
= unit_full_printf(u
, rvalue
, &n
);
1957 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1961 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
1962 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
1966 r
= strv_extend(env
, n
);
1973 int config_parse_environ(const char *unit
,
1974 const char *filename
,
1976 const char *section
,
1977 unsigned section_line
,
1986 const char *word
, *state
;
1988 _cleanup_free_
char *k
= NULL
;
1996 if (isempty(rvalue
)) {
1997 /* Empty assignment resets the list */
1998 *env
= strv_free(*env
);
2003 r
= unit_full_printf(u
, rvalue
, &k
);
2005 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2016 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
2017 _cleanup_free_
char *n
= NULL
;
2020 r
= cunescape_length(word
, l
, 0, &n
);
2022 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
2026 if (!env_assignment_is_valid(n
)) {
2027 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid environment assignment, ignoring: %s", rvalue
);
2031 x
= strv_env_set(*env
, n
);
2038 if (!isempty(state
))
2039 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2044 int config_parse_pass_environ(const char *unit
,
2045 const char *filename
,
2047 const char *section
,
2048 unsigned section_line
,
2055 const char *whole_rvalue
= rvalue
;
2056 char*** passenv
= data
;
2057 _cleanup_strv_free_
char **n
= NULL
;
2058 size_t nlen
= 0, nbufsize
= 0;
2066 if (isempty(rvalue
)) {
2067 /* Empty assignment resets the list */
2068 *passenv
= strv_free(*passenv
);
2073 _cleanup_free_
char *word
= NULL
;
2075 r
= extract_first_word(&rvalue
, &word
, WHITESPACE
, EXTRACT_QUOTES
);
2081 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2082 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2086 if (!env_name_is_valid(word
)) {
2087 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2088 "Invalid environment name for %s, ignoring: %s", lvalue
, word
);
2092 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2100 r
= strv_extend_strv(passenv
, n
, true);
2108 int config_parse_ip_tos(const char *unit
,
2109 const char *filename
,
2111 const char *section
,
2112 unsigned section_line
,
2119 int *ip_tos
= data
, x
;
2126 x
= ip_tos_from_string(rvalue
);
2128 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2136 int config_parse_unit_condition_path(
2138 const char *filename
,
2140 const char *section
,
2141 unsigned section_line
,
2148 _cleanup_free_
char *p
= NULL
;
2149 Condition
**list
= data
, *c
;
2150 ConditionType t
= ltype
;
2151 bool trigger
, negate
;
2160 if (isempty(rvalue
)) {
2161 /* Empty assignment resets the list */
2162 *list
= condition_free_list(*list
);
2166 trigger
= rvalue
[0] == '|';
2170 negate
= rvalue
[0] == '!';
2174 r
= unit_full_printf(u
, rvalue
, &p
);
2176 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2180 if (!path_is_absolute(p
)) {
2181 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2185 c
= condition_new(t
, p
, trigger
, negate
);
2189 LIST_PREPEND(conditions
, *list
, c
);
2193 int config_parse_unit_condition_string(
2195 const char *filename
,
2197 const char *section
,
2198 unsigned section_line
,
2205 _cleanup_free_
char *s
= NULL
;
2206 Condition
**list
= data
, *c
;
2207 ConditionType t
= ltype
;
2208 bool trigger
, negate
;
2217 if (isempty(rvalue
)) {
2218 /* Empty assignment resets the list */
2219 *list
= condition_free_list(*list
);
2223 trigger
= rvalue
[0] == '|';
2227 negate
= rvalue
[0] == '!';
2231 r
= unit_full_printf(u
, rvalue
, &s
);
2233 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2237 c
= condition_new(t
, s
, trigger
, negate
);
2241 LIST_PREPEND(conditions
, *list
, c
);
2245 int config_parse_unit_condition_null(
2247 const char *filename
,
2249 const char *section
,
2250 unsigned section_line
,
2257 Condition
**list
= data
, *c
;
2258 bool trigger
, negate
;
2266 if (isempty(rvalue
)) {
2267 /* Empty assignment resets the list */
2268 *list
= condition_free_list(*list
);
2272 trigger
= rvalue
[0] == '|';
2276 negate
= rvalue
[0] == '!';
2280 b
= parse_boolean(rvalue
);
2282 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2289 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2293 LIST_PREPEND(conditions
, *list
, c
);
2297 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2298 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2300 int config_parse_unit_requires_mounts_for(
2302 const char *filename
,
2304 const char *section
,
2305 unsigned section_line
,
2313 const char *word
, *state
;
2321 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2323 _cleanup_free_
char *n
;
2325 n
= strndup(word
, l
);
2329 if (!utf8_is_valid(n
)) {
2330 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2334 r
= unit_require_mounts_for(u
, n
);
2336 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount for, ignoring: %s", rvalue
);
2340 if (!isempty(state
))
2341 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2346 int config_parse_documentation(const char *unit
,
2347 const char *filename
,
2349 const char *section
,
2350 unsigned section_line
,
2366 if (isempty(rvalue
)) {
2367 /* Empty assignment resets the list */
2368 u
->documentation
= strv_free(u
->documentation
);
2372 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2373 rvalue
, data
, userdata
);
2377 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2379 if (documentation_url_is_valid(*a
))
2382 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2393 int config_parse_syscall_filter(
2395 const char *filename
,
2397 const char *section
,
2398 unsigned section_line
,
2405 static const char default_syscalls
[] =
2412 ExecContext
*c
= data
;
2414 bool invert
= false;
2415 const char *word
, *state
;
2424 if (isempty(rvalue
)) {
2425 /* Empty assignment resets the list */
2426 c
->syscall_filter
= set_free(c
->syscall_filter
);
2427 c
->syscall_whitelist
= false;
2431 if (rvalue
[0] == '~') {
2436 if (!c
->syscall_filter
) {
2437 c
->syscall_filter
= set_new(NULL
);
2438 if (!c
->syscall_filter
)
2442 /* Allow everything but the ones listed */
2443 c
->syscall_whitelist
= false;
2447 /* Allow nothing but the ones listed */
2448 c
->syscall_whitelist
= true;
2450 /* Accept default syscalls if we are on a whitelist */
2451 NULSTR_FOREACH(i
, default_syscalls
) {
2454 id
= seccomp_syscall_resolve_name(i
);
2458 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2467 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2468 _cleanup_free_
char *t
= NULL
;
2471 t
= strndup(word
, l
);
2475 id
= seccomp_syscall_resolve_name(t
);
2477 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2481 /* If we previously wanted to forbid a syscall and now
2482 * we want to allow it, then remove it from the list
2484 if (!invert
== c
->syscall_whitelist
) {
2485 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2491 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2493 if (!isempty(state
))
2494 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2496 /* Turn on NNP, but only if it wasn't configured explicitly
2497 * before, and only if we are in user mode. */
2498 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2499 c
->no_new_privileges
= true;
2504 int config_parse_syscall_archs(
2506 const char *filename
,
2508 const char *section
,
2509 unsigned section_line
,
2517 const char *word
, *state
;
2521 if (isempty(rvalue
)) {
2522 *archs
= set_free(*archs
);
2526 r
= set_ensure_allocated(archs
, NULL
);
2530 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2531 _cleanup_free_
char *t
= NULL
;
2534 t
= strndup(word
, l
);
2538 r
= seccomp_arch_from_string(t
, &a
);
2540 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call architecture, ignoring: %s", t
);
2544 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2550 if (!isempty(state
))
2551 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2556 int config_parse_syscall_errno(
2558 const char *filename
,
2560 const char *section
,
2561 unsigned section_line
,
2568 ExecContext
*c
= data
;
2575 if (isempty(rvalue
)) {
2576 /* Empty assignment resets to KILL */
2577 c
->syscall_errno
= 0;
2581 e
= errno_from_name(rvalue
);
2583 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2587 c
->syscall_errno
= e
;
2591 int config_parse_address_families(
2593 const char *filename
,
2595 const char *section
,
2596 unsigned section_line
,
2603 ExecContext
*c
= data
;
2604 bool invert
= false;
2605 const char *word
, *state
;
2613 if (isempty(rvalue
)) {
2614 /* Empty assignment resets the list */
2615 c
->address_families
= set_free(c
->address_families
);
2616 c
->address_families_whitelist
= false;
2620 if (rvalue
[0] == '~') {
2625 if (!c
->address_families
) {
2626 c
->address_families
= set_new(NULL
);
2627 if (!c
->address_families
)
2630 c
->address_families_whitelist
= !invert
;
2633 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2634 _cleanup_free_
char *t
= NULL
;
2637 t
= strndup(word
, l
);
2641 af
= af_from_name(t
);
2643 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse address family, ignoring: %s", t
);
2647 /* If we previously wanted to forbid an address family and now
2648 * we want to allow it, then remove it from the list
2650 if (!invert
== c
->address_families_whitelist
) {
2651 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2657 set_remove(c
->address_families
, INT_TO_PTR(af
));
2659 if (!isempty(state
))
2660 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2666 int config_parse_unit_slice(
2668 const char *filename
,
2670 const char *section
,
2671 unsigned section_line
,
2678 _cleanup_free_
char *k
= NULL
;
2679 Unit
*u
= userdata
, *slice
= NULL
;
2687 r
= unit_name_printf(u
, rvalue
, &k
);
2689 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2693 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2695 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2699 r
= unit_set_slice(u
, slice
);
2701 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2708 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2710 int config_parse_cpu_shares(
2712 const char *filename
,
2714 const char *section
,
2715 unsigned section_line
,
2722 uint64_t *shares
= data
;
2729 r
= cg_cpu_shares_parse(rvalue
, shares
);
2731 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2738 int config_parse_cpu_quota(
2740 const char *filename
,
2742 const char *section
,
2743 unsigned section_line
,
2750 CGroupContext
*c
= data
;
2757 if (isempty(rvalue
)) {
2758 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2762 if (!endswith(rvalue
, "%")) {
2763 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2767 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2768 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' invalid. Ignoring.", rvalue
);
2772 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2777 int config_parse_memory_limit(
2779 const char *filename
,
2781 const char *section
,
2782 unsigned section_line
,
2789 CGroupContext
*c
= data
;
2793 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2794 c
->memory_limit
= (uint64_t) -1;
2798 r
= parse_size(rvalue
, 1024, &bytes
);
2799 if (r
< 0 || bytes
< 1) {
2800 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2804 c
->memory_limit
= bytes
;
2808 int config_parse_tasks_max(
2810 const char *filename
,
2812 const char *section
,
2813 unsigned section_line
,
2820 uint64_t *tasks_max
= data
, u
;
2823 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2824 *tasks_max
= (uint64_t) -1;
2828 r
= safe_atou64(rvalue
, &u
);
2829 if (r
< 0 || u
< 1) {
2830 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2838 int config_parse_device_allow(
2840 const char *filename
,
2842 const char *section
,
2843 unsigned section_line
,
2850 _cleanup_free_
char *path
= NULL
;
2851 CGroupContext
*c
= data
;
2852 CGroupDeviceAllow
*a
;
2856 if (isempty(rvalue
)) {
2857 while (c
->device_allow
)
2858 cgroup_context_free_device_allow(c
, c
->device_allow
);
2863 n
= strcspn(rvalue
, WHITESPACE
);
2864 path
= strndup(rvalue
, n
);
2868 if (!startswith(path
, "/dev/") &&
2869 !startswith(path
, "block-") &&
2870 !startswith(path
, "char-")) {
2871 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2875 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2879 if (!in_charset(m
, "rwm")) {
2880 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
2884 a
= new0(CGroupDeviceAllow
, 1);
2890 a
->r
= !!strchr(m
, 'r');
2891 a
->w
= !!strchr(m
, 'w');
2892 a
->m
= !!strchr(m
, 'm');
2894 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2898 int config_parse_blockio_weight(
2900 const char *filename
,
2902 const char *section
,
2903 unsigned section_line
,
2910 uint64_t *weight
= data
;
2917 r
= cg_blkio_weight_parse(rvalue
, weight
);
2919 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2926 int config_parse_blockio_device_weight(
2928 const char *filename
,
2930 const char *section
,
2931 unsigned section_line
,
2938 _cleanup_free_
char *path
= NULL
;
2939 CGroupBlockIODeviceWeight
*w
;
2940 CGroupContext
*c
= data
;
2950 if (isempty(rvalue
)) {
2951 while (c
->blockio_device_weights
)
2952 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2957 n
= strcspn(rvalue
, WHITESPACE
);
2958 weight
= rvalue
+ n
;
2959 weight
+= strspn(weight
, WHITESPACE
);
2961 if (isempty(weight
)) {
2962 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
2966 path
= strndup(rvalue
, n
);
2970 if (!path_startswith(path
, "/dev")) {
2971 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2975 r
= cg_blkio_weight_parse(weight
, &u
);
2977 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
2981 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
2983 w
= new0(CGroupBlockIODeviceWeight
, 1);
2992 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2996 int config_parse_blockio_bandwidth(
2998 const char *filename
,
3000 const char *section
,
3001 unsigned section_line
,
3008 _cleanup_free_
char *path
= NULL
;
3009 CGroupBlockIODeviceBandwidth
*b
;
3010 CGroupContext
*c
= data
;
3011 const char *bandwidth
;
3021 read
= streq("BlockIOReadBandwidth", lvalue
);
3023 if (isempty(rvalue
)) {
3024 CGroupBlockIODeviceBandwidth
*next
;
3026 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
3027 if (b
->read
== read
)
3028 cgroup_context_free_blockio_device_bandwidth(c
, b
);
3033 n
= strcspn(rvalue
, WHITESPACE
);
3034 bandwidth
= rvalue
+ n
;
3035 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3038 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3042 path
= strndup(rvalue
, n
);
3046 if (!path_startswith(path
, "/dev")) {
3047 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3051 r
= parse_size(bandwidth
, 1000, &bytes
);
3052 if (r
< 0 || bytes
<= 0) {
3053 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3057 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3063 b
->bandwidth
= bytes
;
3066 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3071 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3073 int config_parse_job_mode_isolate(
3075 const char *filename
,
3077 const char *section
,
3078 unsigned section_line
,
3092 r
= parse_boolean(rvalue
);
3094 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3098 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3102 int config_parse_runtime_directory(
3104 const char *filename
,
3106 const char *section
,
3107 unsigned section_line
,
3116 const char *word
, *state
;
3125 if (isempty(rvalue
)) {
3126 /* Empty assignment resets the list */
3127 *rt
= strv_free(*rt
);
3131 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3132 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3134 t
= strndup(word
, l
);
3138 r
= unit_name_printf(u
, t
, &n
);
3140 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
3144 if (!filename_is_valid(n
)) {
3145 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3149 r
= strv_push(rt
, n
);
3155 if (!isempty(state
))
3156 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3161 int config_parse_set_status(
3163 const char *filename
,
3165 const char *section
,
3166 unsigned section_line
,
3174 const char *word
, *state
;
3176 ExitStatusSet
*status_set
= data
;
3183 /* Empty assignment resets the list */
3184 if (isempty(rvalue
)) {
3185 exit_status_set_free(status_set
);
3189 FOREACH_WORD(word
, l
, rvalue
, state
) {
3190 _cleanup_free_
char *temp
;
3194 temp
= strndup(word
, l
);
3198 r
= safe_atoi(temp
, &val
);
3200 val
= signal_from_string_try_harder(temp
);
3203 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3206 set
= &status_set
->signal
;
3208 if (val
< 0 || val
> 255) {
3209 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3212 set
= &status_set
->status
;
3215 r
= set_ensure_allocated(set
, NULL
);
3219 r
= set_put(*set
, INT_TO_PTR(val
));
3221 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3225 if (!isempty(state
))
3226 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3231 int config_parse_namespace_path_strv(
3233 const char *filename
,
3235 const char *section
,
3236 unsigned section_line
,
3253 if (isempty(rvalue
)) {
3254 /* Empty assignment resets the list */
3255 *sv
= strv_free(*sv
);
3259 prev
= cur
= rvalue
;
3261 _cleanup_free_
char *word
= NULL
;
3264 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3270 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage, ignoring: %s", prev
);
3274 if (!utf8_is_valid(word
)) {
3275 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3280 offset
= word
[0] == '-';
3281 if (!path_is_absolute(word
+ offset
)) {
3282 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", word
);
3287 path_kill_slashes(word
+ offset
);
3289 r
= strv_push(sv
, word
);
3300 int config_parse_no_new_privileges(
3302 const char *filename
,
3304 const char *section
,
3305 unsigned section_line
,
3312 ExecContext
*c
= data
;
3320 k
= parse_boolean(rvalue
);
3322 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3326 c
->no_new_privileges
= !!k
;
3327 c
->no_new_privileges_set
= true;
3332 int config_parse_protect_home(
3334 const char *filename
,
3336 const char *section
,
3337 unsigned section_line
,
3344 ExecContext
*c
= data
;
3352 /* Our enum shall be a superset of booleans, hence first try
3353 * to parse as as boolean, and then as enum */
3355 k
= parse_boolean(rvalue
);
3357 c
->protect_home
= PROTECT_HOME_YES
;
3359 c
->protect_home
= PROTECT_HOME_NO
;
3363 h
= protect_home_from_string(rvalue
);
3365 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
3369 c
->protect_home
= h
;
3375 int config_parse_protect_system(
3377 const char *filename
,
3379 const char *section
,
3380 unsigned section_line
,
3387 ExecContext
*c
= data
;
3395 /* Our enum shall be a superset of booleans, hence first try
3396 * to parse as as boolean, and then as enum */
3398 k
= parse_boolean(rvalue
);
3400 c
->protect_system
= PROTECT_SYSTEM_YES
;
3402 c
->protect_system
= PROTECT_SYSTEM_NO
;
3406 s
= protect_system_from_string(rvalue
);
3408 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
3412 c
->protect_system
= s
;
3418 #define FOLLOW_MAX 8
3420 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3431 /* This will update the filename pointer if the loaded file is
3432 * reached by a symlink. The old string will be freed. */
3435 char *target
, *name
;
3437 if (c
++ >= FOLLOW_MAX
)
3440 path_kill_slashes(*filename
);
3442 /* Add the file name we are currently looking at to
3443 * the names of this unit, but only if it is a valid
3445 name
= basename(*filename
);
3447 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3449 id
= set_get(names
, name
);
3455 r
= set_consume(names
, id
);
3461 /* Try to open the file name, but don't if its a symlink */
3462 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3469 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3470 r
= readlink_and_make_absolute(*filename
, &target
);
3478 f
= fdopen(fd
, "re");
3489 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3497 /* Let's try to add in all symlink names we found */
3498 while ((k
= set_steal_first(names
))) {
3500 /* First try to merge in the other name into our
3502 r
= unit_merge_by_name(*u
, k
);
3506 /* Hmm, we couldn't merge the other unit into
3507 * ours? Then let's try it the other way
3510 other
= manager_get_unit((*u
)->manager
, k
);
3514 r
= unit_merge(other
, *u
);
3517 return merge_by_names(u
, names
, NULL
);
3525 unit_choose_id(*u
, id
);
3533 static int load_from_path(Unit
*u
, const char *path
) {
3535 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3536 _cleanup_fclose_
FILE *f
= NULL
;
3537 _cleanup_free_
char *filename
= NULL
;
3545 symlink_names
= set_new(&string_hash_ops
);
3549 if (path_is_absolute(path
)) {
3551 filename
= strdup(path
);
3555 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3557 filename
= mfree(filename
);
3565 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3567 /* Instead of opening the path right away, we manually
3568 * follow all symlinks and add their name to our unit
3569 * name set while doing so */
3570 filename
= path_make_absolute(path
, *p
);
3574 if (u
->manager
->unit_path_cache
&&
3575 !set_get(u
->manager
->unit_path_cache
, filename
))
3578 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3581 filename
= mfree(filename
);
3585 /* Empty the symlink names for the next run */
3586 set_clear_free(symlink_names
);
3595 /* Hmm, no suitable file found? */
3599 r
= merge_by_names(&merged
, symlink_names
, id
);
3604 u
->load_state
= UNIT_MERGED
;
3608 if (fstat(fileno(f
), &st
) < 0)
3611 if (null_or_empty(&st
))
3612 u
->load_state
= UNIT_MASKED
;
3614 u
->load_state
= UNIT_LOADED
;
3616 /* Now, parse the file contents */
3617 r
= config_parse(u
->id
, filename
, f
,
3618 UNIT_VTABLE(u
)->sections
,
3619 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3620 false, true, false, u
);
3625 free(u
->fragment_path
);
3626 u
->fragment_path
= filename
;
3629 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3631 if (u
->source_path
) {
3632 if (stat(u
->source_path
, &st
) >= 0)
3633 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3635 u
->source_mtime
= 0;
3641 int unit_load_fragment(Unit
*u
) {
3647 assert(u
->load_state
== UNIT_STUB
);
3651 u
->load_state
= UNIT_LOADED
;
3655 /* First, try to find the unit under its id. We always look
3656 * for unit files in the default directories, to make it easy
3657 * to override things by placing things in /etc/systemd/system */
3658 r
= load_from_path(u
, u
->id
);
3662 /* Try to find an alias we can load this with */
3663 if (u
->load_state
== UNIT_STUB
) {
3664 SET_FOREACH(t
, u
->names
, i
) {
3669 r
= load_from_path(u
, t
);
3673 if (u
->load_state
!= UNIT_STUB
)
3678 /* And now, try looking for it under the suggested (originally linked) path */
3679 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3681 r
= load_from_path(u
, u
->fragment_path
);
3685 if (u
->load_state
== UNIT_STUB
)
3686 /* Hmm, this didn't work? Then let's get rid
3687 * of the fragment path stored for us, so that
3688 * we don't point to an invalid location. */
3689 u
->fragment_path
= mfree(u
->fragment_path
);
3692 /* Look for a template */
3693 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3694 _cleanup_free_
char *k
= NULL
;
3696 r
= unit_name_template(u
->id
, &k
);
3700 r
= load_from_path(u
, k
);
3704 if (u
->load_state
== UNIT_STUB
) {
3705 SET_FOREACH(t
, u
->names
, i
) {
3706 _cleanup_free_
char *z
= NULL
;
3711 r
= unit_name_template(t
, &z
);
3715 r
= load_from_path(u
, z
);
3719 if (u
->load_state
!= UNIT_STUB
)
3728 void unit_dump_config_items(FILE *f
) {
3729 static const struct {
3730 const ConfigParserCallback callback
;
3733 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3734 { config_parse_warn_compat
, "NOTSUPPORTED" },
3736 { config_parse_int
, "INTEGER" },
3737 { config_parse_unsigned
, "UNSIGNED" },
3738 { config_parse_iec_size
, "SIZE" },
3739 { config_parse_iec_uint64
, "SIZE" },
3740 { config_parse_si_size
, "SIZE" },
3741 { config_parse_bool
, "BOOLEAN" },
3742 { config_parse_string
, "STRING" },
3743 { config_parse_path
, "PATH" },
3744 { config_parse_unit_path_printf
, "PATH" },
3745 { config_parse_strv
, "STRING [...]" },
3746 { config_parse_exec_nice
, "NICE" },
3747 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3748 { config_parse_exec_io_class
, "IOCLASS" },
3749 { config_parse_exec_io_priority
, "IOPRIORITY" },
3750 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3751 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3752 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3753 { config_parse_mode
, "MODE" },
3754 { config_parse_unit_env_file
, "FILE" },
3755 { config_parse_output
, "OUTPUT" },
3756 { config_parse_input
, "INPUT" },
3757 { config_parse_log_facility
, "FACILITY" },
3758 { config_parse_log_level
, "LEVEL" },
3759 { config_parse_exec_secure_bits
, "SECUREBITS" },
3760 { config_parse_capability_set
, "BOUNDINGSET" },
3761 { config_parse_limit
, "LIMIT" },
3762 { config_parse_unit_deps
, "UNIT [...]" },
3763 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3764 { config_parse_service_type
, "SERVICETYPE" },
3765 { config_parse_service_restart
, "SERVICERESTART" },
3766 #ifdef HAVE_SYSV_COMPAT
3767 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3769 { config_parse_kill_mode
, "KILLMODE" },
3770 { config_parse_signal
, "SIGNAL" },
3771 { config_parse_socket_listen
, "SOCKET [...]" },
3772 { config_parse_socket_bind
, "SOCKETBIND" },
3773 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3774 { config_parse_sec
, "SECONDS" },
3775 { config_parse_nsec
, "NANOSECONDS" },
3776 { config_parse_namespace_path_strv
, "PATH [...]" },
3777 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3778 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3779 { config_parse_unit_string_printf
, "STRING" },
3780 { config_parse_trigger_unit
, "UNIT" },
3781 { config_parse_timer
, "TIMER" },
3782 { config_parse_path_spec
, "PATH" },
3783 { config_parse_notify_access
, "ACCESS" },
3784 { config_parse_ip_tos
, "TOS" },
3785 { config_parse_unit_condition_path
, "CONDITION" },
3786 { config_parse_unit_condition_string
, "CONDITION" },
3787 { config_parse_unit_condition_null
, "CONDITION" },
3788 { config_parse_unit_slice
, "SLICE" },
3789 { config_parse_documentation
, "URL" },
3790 { config_parse_service_timeout
, "SECONDS" },
3791 { config_parse_failure_action
, "ACTION" },
3792 { config_parse_set_status
, "STATUS" },
3793 { config_parse_service_sockets
, "SOCKETS" },
3794 { config_parse_environ
, "ENVIRON" },
3796 { config_parse_syscall_filter
, "SYSCALLS" },
3797 { config_parse_syscall_archs
, "ARCHS" },
3798 { config_parse_syscall_errno
, "ERRNO" },
3799 { config_parse_address_families
, "FAMILIES" },
3801 { config_parse_cpu_shares
, "SHARES" },
3802 { config_parse_memory_limit
, "LIMIT" },
3803 { config_parse_device_allow
, "DEVICE" },
3804 { config_parse_device_policy
, "POLICY" },
3805 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3806 { config_parse_blockio_weight
, "WEIGHT" },
3807 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3808 { config_parse_long
, "LONG" },
3809 { config_parse_socket_service
, "SERVICE" },
3811 { config_parse_exec_selinux_context
, "LABEL" },
3813 { config_parse_job_mode
, "MODE" },
3814 { config_parse_job_mode_isolate
, "BOOLEAN" },
3815 { config_parse_personality
, "PERSONALITY" },
3818 const char *prev
= NULL
;
3823 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3824 const char *rvalue
= "OTHER", *lvalue
;
3828 const ConfigPerfItem
*p
;
3830 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3832 dot
= strchr(i
, '.');
3833 lvalue
= dot
? dot
+ 1 : i
;
3837 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3841 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3844 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3845 if (p
->parse
== table
[j
].callback
) {
3846 rvalue
= table
[j
].rvalue
;
3850 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);