1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
6 Copyright 2012 Holger Hans Peter Freyther
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include <linux/oom.h>
31 #include <sys/resource.h>
35 #include "alloc-util.h"
36 #include "bus-error.h"
37 #include "bus-internal.h"
40 #include "capability-util.h"
42 #include "conf-parser.h"
43 #include "cpu-set-util.h"
45 #include "errno-list.h"
51 #include "journal-util.h"
52 #include "load-fragment.h"
55 #include "mount-util.h"
56 #include "parse-util.h"
57 #include "path-util.h"
58 #include "process-util.h"
59 #include "rlimit-util.h"
61 #include "seccomp-util.h"
63 #include "securebits.h"
64 #include "securebits-util.h"
65 #include "signal-util.h"
66 #include "stat-util.h"
67 #include "string-util.h"
69 #include "unit-name.h"
70 #include "unit-printf.h"
72 #include "user-util.h"
76 int config_parse_warn_compat(
81 unsigned section_line
,
87 Disabled reason
= ltype
;
90 case DISABLED_CONFIGURATION
:
91 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
92 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
95 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
96 "Support for option %s= has been removed and it is ignored", lvalue
);
98 case DISABLED_EXPERIMENTAL
:
99 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
100 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
107 DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode
, collect_mode
, CollectMode
, "Failed to parse garbage collection mode");
109 int config_parse_unit_deps(
111 const char *filename
,
114 unsigned section_line
,
121 UnitDependency d
= ltype
;
131 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
134 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_RETAIN_ESCAPE
);
140 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
144 r
= unit_name_printf(u
, word
, &k
);
146 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
150 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
152 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
158 int config_parse_obsolete_unit_deps(
160 const char *filename
,
163 unsigned section_line
,
170 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
171 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue
, unit_dependency_to_string(ltype
));
173 return config_parse_unit_deps(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, data
, userdata
);
176 int config_parse_unit_string_printf(
178 const char *filename
,
181 unsigned section_line
,
188 _cleanup_free_
char *k
= NULL
;
197 r
= unit_full_printf(u
, rvalue
, &k
);
199 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
203 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
206 int config_parse_unit_strv_printf(
208 const char *filename
,
211 unsigned section_line
,
219 _cleanup_free_
char *k
= NULL
;
227 r
= unit_full_printf(u
, rvalue
, &k
);
229 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
233 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
236 int config_parse_unit_path_printf(
238 const char *filename
,
241 unsigned section_line
,
248 _cleanup_free_
char *k
= NULL
;
258 r
= unit_full_printf(u
, rvalue
, &k
);
260 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
261 "Failed to resolve unit specifiers on %s%s: %m",
262 fatal
? "" : ", ignoring", rvalue
);
263 return fatal
? -ENOEXEC
: 0;
266 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
269 int config_parse_unit_path_strv_printf(
271 const char *filename
,
274 unsigned section_line
,
291 if (isempty(rvalue
)) {
297 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
299 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
305 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
306 "Invalid syntax, ignoring: %s", rvalue
);
310 r
= unit_full_printf(u
, word
, &k
);
312 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
313 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
317 if (!utf8_is_valid(k
)) {
318 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
322 if (!path_is_absolute(k
)) {
323 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
324 "Symlink path is not absolute: %s", k
);
328 path_kill_slashes(k
);
337 int config_parse_socket_listen(const char *unit
,
338 const char *filename
,
341 unsigned section_line
,
348 _cleanup_free_ SocketPort
*p
= NULL
;
360 if (isempty(rvalue
)) {
361 /* An empty assignment removes all ports */
362 socket_free_ports(s
);
366 p
= new0(SocketPort
, 1);
370 if (ltype
!= SOCKET_SOCKET
) {
373 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
375 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
379 path_kill_slashes(p
->path
);
381 } else if (streq(lvalue
, "ListenNetlink")) {
382 _cleanup_free_
char *k
= NULL
;
384 p
->type
= SOCKET_SOCKET
;
385 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
387 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
391 r
= socket_address_parse_netlink(&p
->address
, k
);
393 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
398 _cleanup_free_
char *k
= NULL
;
400 p
->type
= SOCKET_SOCKET
;
401 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
403 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
407 r
= socket_address_parse_and_warn(&p
->address
, k
);
409 if (r
!= -EAFNOSUPPORT
)
410 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
415 if (streq(lvalue
, "ListenStream"))
416 p
->address
.type
= SOCK_STREAM
;
417 else if (streq(lvalue
, "ListenDatagram"))
418 p
->address
.type
= SOCK_DGRAM
;
420 assert(streq(lvalue
, "ListenSequentialPacket"));
421 p
->address
.type
= SOCK_SEQPACKET
;
424 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
425 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
431 p
->auxiliary_fds
= NULL
;
432 p
->n_auxiliary_fds
= 0;
436 LIST_FIND_TAIL(port
, s
->ports
, tail
);
437 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
439 LIST_PREPEND(port
, s
->ports
, p
);
445 int config_parse_socket_protocol(const char *unit
,
446 const char *filename
,
449 unsigned section_line
,
464 if (streq(rvalue
, "udplite"))
465 s
->socket_protocol
= IPPROTO_UDPLITE
;
466 else if (streq(rvalue
, "sctp"))
467 s
->socket_protocol
= IPPROTO_SCTP
;
469 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
476 int config_parse_socket_bind(const char *unit
,
477 const char *filename
,
480 unsigned section_line
,
488 SocketAddressBindIPv6Only b
;
497 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
501 r
= parse_boolean(rvalue
);
503 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
507 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
509 s
->bind_ipv6_only
= b
;
514 int config_parse_exec_nice(
516 const char *filename
,
519 unsigned section_line
,
526 ExecContext
*c
= data
;
534 r
= parse_nice(rvalue
, &priority
);
537 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
539 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
550 int config_parse_exec_oom_score_adjust(const char* unit
,
551 const char *filename
,
554 unsigned section_line
,
561 ExecContext
*c
= data
;
569 r
= safe_atoi(rvalue
, &oa
);
571 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
575 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
576 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
580 c
->oom_score_adjust
= oa
;
581 c
->oom_score_adjust_set
= true;
586 int config_parse_exec(
588 const char *filename
,
591 unsigned section_line
,
598 ExecCommand
**e
= data
;
610 rvalue
+= strspn(rvalue
, WHITESPACE
);
612 if (isempty(rvalue
)) {
613 /* An empty assignment resets the list */
614 *e
= exec_command_free_list(*e
);
620 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
621 ExecCommandFlags flags
= 0;
622 bool ignore
= false, separate_argv0
= false;
623 _cleanup_free_ ExecCommand
*nce
= NULL
;
624 _cleanup_strv_free_
char **n
= NULL
;
625 size_t nlen
= 0, nbufsize
= 0;
630 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
636 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
637 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
638 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
639 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
640 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
641 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
642 * other sandboxing, with some special exceptions for changing UID.
644 * The idea is that '!!' may be used to write services that can take benefit of systemd's
645 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
646 * privilege dropping within the daemon if the kernel does not offer that. */
648 if (*f
== '-' && !(flags
& EXEC_COMMAND_IGNORE_FAILURE
)) {
649 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
651 } else if (*f
== '@' && !separate_argv0
)
652 separate_argv0
= true;
653 else if (*f
== '+' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
654 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
655 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
656 flags
|= EXEC_COMMAND_NO_SETUID
;
657 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
658 flags
&= ~EXEC_COMMAND_NO_SETUID
;
659 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
665 r
= unit_full_printf(u
, f
, &path
);
667 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
668 "Failed to resolve unit specifiers on %s%s: %m",
669 f
, ignore
? ", ignoring" : "");
670 return ignore
? 0 : -ENOEXEC
;
674 /* First word is either "-" or "@" with no command. */
675 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
676 "Empty path in command line%s: \"%s\"",
677 ignore
? ", ignoring" : "", rvalue
);
678 return ignore
? 0 : -ENOEXEC
;
680 if (!string_is_safe(path
)) {
681 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
682 "Executable path contains special characters%s: %s",
683 ignore
? ", ignoring" : "", rvalue
);
684 return ignore
? 0 : -ENOEXEC
;
686 if (!path_is_absolute(path
)) {
687 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
688 "Executable path is not absolute%s: %s",
689 ignore
? ", ignoring" : "", rvalue
);
690 return ignore
? 0 : -ENOEXEC
;
692 if (endswith(path
, "/")) {
693 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
694 "Executable path specifies a directory%s: %s",
695 ignore
? ", ignoring" : "", rvalue
);
696 return ignore
? 0 : -ENOEXEC
;
699 if (!separate_argv0
) {
702 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
712 path_kill_slashes(path
);
714 while (!isempty(p
)) {
715 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
717 /* Check explicitly for an unquoted semicolon as
718 * command separator token. */
719 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
721 p
+= strspn(p
, WHITESPACE
);
726 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
727 * extract_first_word() would return the same for all of those. */
728 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
732 p
+= strspn(p
, WHITESPACE
);
734 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
745 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
749 return ignore
? 0 : -ENOEXEC
;
751 r
= unit_full_printf(u
, word
, &resolved
);
753 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
754 "Failed to resolve unit specifiers on %s%s: %m",
755 word
, ignore
? ", ignoring" : "");
756 return ignore
? 0 : -ENOEXEC
;
759 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
761 n
[nlen
++] = resolved
;
767 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
768 "Empty executable name or zeroeth argument%s: %s",
769 ignore
? ", ignoring" : "", rvalue
);
770 return ignore
? 0 : -ENOEXEC
;
773 nce
= new0(ExecCommand
, 1);
781 exec_command_append_list(e
, nce
);
783 /* Do not _cleanup_free_ these. */
794 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
795 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
797 int config_parse_socket_bindtodevice(
799 const char *filename
,
802 unsigned section_line
,
817 if (rvalue
[0] && !streq(rvalue
, "*")) {
818 if (!ifname_valid(rvalue
)) {
819 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
829 free(s
->bind_to_device
);
830 s
->bind_to_device
= n
;
835 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input literal specifier");
836 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output literal specifier");
838 int config_parse_exec_input(const char *unit
,
839 const char *filename
,
842 unsigned section_line
,
848 ExecContext
*c
= data
;
857 name
= startswith(rvalue
, "fd:");
859 /* Strip prefix and validate fd name */
860 if (!fdname_is_valid(name
)) {
861 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
864 c
->std_input
= EXEC_INPUT_NAMED_FD
;
865 r
= free_and_strdup(&c
->stdio_fdname
[STDIN_FILENO
], name
);
870 ExecInput ei
= exec_input_from_string(rvalue
);
871 if (ei
== _EXEC_INPUT_INVALID
)
872 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
879 int config_parse_exec_output(const char *unit
,
880 const char *filename
,
883 unsigned section_line
,
889 ExecContext
*c
= data
;
900 name
= startswith(rvalue
, "fd:");
902 /* Strip prefix and validate fd name */
903 if (!fdname_is_valid(name
)) {
904 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
907 eo
= EXEC_OUTPUT_NAMED_FD
;
909 eo
= exec_output_from_string(rvalue
);
910 if (eo
== _EXEC_OUTPUT_INVALID
) {
911 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
916 if (streq(lvalue
, "StandardOutput")) {
918 r
= free_and_strdup(&c
->stdio_fdname
[STDOUT_FILENO
], name
);
922 } else if (streq(lvalue
, "StandardError")) {
924 r
= free_and_strdup(&c
->stdio_fdname
[STDERR_FILENO
], name
);
929 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output property, ignoring: %s", lvalue
);
934 int config_parse_exec_io_class(const char *unit
,
935 const char *filename
,
938 unsigned section_line
,
945 ExecContext
*c
= data
;
953 x
= ioprio_class_from_string(rvalue
);
955 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
959 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
960 c
->ioprio_set
= true;
965 int config_parse_exec_io_priority(const char *unit
,
966 const char *filename
,
969 unsigned section_line
,
976 ExecContext
*c
= data
;
984 r
= ioprio_parse_priority(rvalue
, &i
);
986 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
990 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
991 c
->ioprio_set
= true;
996 int config_parse_exec_cpu_sched_policy(const char *unit
,
997 const char *filename
,
1000 unsigned section_line
,
1008 ExecContext
*c
= data
;
1016 x
= sched_policy_from_string(rvalue
);
1018 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1022 c
->cpu_sched_policy
= x
;
1023 /* Moving to or from real-time policy? We need to adjust the priority */
1024 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
1025 c
->cpu_sched_set
= true;
1030 int config_parse_exec_cpu_sched_prio(const char *unit
,
1031 const char *filename
,
1033 const char *section
,
1034 unsigned section_line
,
1041 ExecContext
*c
= data
;
1049 r
= safe_atoi(rvalue
, &i
);
1051 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1055 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1056 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1057 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1059 if (i
< min
|| i
> max
) {
1060 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1064 c
->cpu_sched_priority
= i
;
1065 c
->cpu_sched_set
= true;
1070 int config_parse_exec_cpu_affinity(const char *unit
,
1071 const char *filename
,
1073 const char *section
,
1074 unsigned section_line
,
1081 ExecContext
*c
= data
;
1082 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1090 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1095 CPU_FREE(c
->cpuset
);
1098 /* An empty assignment resets the CPU list */
1104 c
->cpuset_ncpus
= ncpus
;
1109 int config_parse_exec_secure_bits(const char *unit
,
1110 const char *filename
,
1112 const char *section
,
1113 unsigned section_line
,
1120 ExecContext
*c
= data
;
1128 if (isempty(rvalue
)) {
1129 /* An empty assignment resets the field */
1134 r
= secure_bits_from_string(rvalue
);
1138 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1139 "Invalid syntax, ignoring: %s", rvalue
);
1148 int config_parse_capability_set(
1150 const char *filename
,
1152 const char *section
,
1153 unsigned section_line
,
1160 uint64_t *capability_set
= data
;
1161 uint64_t sum
= 0, initial
= 0;
1162 bool invert
= false;
1170 if (rvalue
[0] == '~') {
1175 if (streq(lvalue
, "CapabilityBoundingSet"))
1176 initial
= CAP_ALL
; /* initialized to all bits on */
1177 /* else "AmbientCapabilities" initialized to all bits off */
1179 r
= capability_set_from_string(rvalue
, &sum
);
1183 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word: %s", rvalue
);
1187 if (sum
== 0 || *capability_set
== initial
)
1188 /* "", "~" or uninitialized data -> replace */
1189 *capability_set
= invert
? ~sum
: sum
;
1191 /* previous data -> merge */
1193 *capability_set
&= ~sum
;
1195 *capability_set
|= sum
;
1201 int config_parse_limit(
1203 const char *filename
,
1205 const char *section
,
1206 unsigned section_line
,
1213 struct rlimit
**rl
= data
, d
= {};
1221 r
= rlimit_parse(ltype
, rvalue
, &d
);
1223 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1227 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1234 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1242 #if HAVE_SYSV_COMPAT
1243 int config_parse_sysv_priority(const char *unit
,
1244 const char *filename
,
1246 const char *section
,
1247 unsigned section_line
,
1254 int *priority
= data
;
1262 r
= safe_atoi(rvalue
, &i
);
1263 if (r
< 0 || i
< 0) {
1264 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1268 *priority
= (int) i
;
1273 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1274 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1276 int config_parse_exec_mount_flags(
1278 const char *filename
,
1280 const char *section
,
1281 unsigned section_line
,
1289 ExecContext
*c
= data
;
1297 r
= mount_propagation_flags_from_string(rvalue
, &c
->mount_flags
);
1299 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1304 int config_parse_exec_selinux_context(
1306 const char *filename
,
1308 const char *section
,
1309 unsigned section_line
,
1316 ExecContext
*c
= data
;
1327 if (isempty(rvalue
)) {
1328 c
->selinux_context
= mfree(c
->selinux_context
);
1329 c
->selinux_context_ignore
= false;
1333 if (rvalue
[0] == '-') {
1339 r
= unit_full_printf(u
, rvalue
, &k
);
1341 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1342 "Failed to resolve specifiers%s: %m",
1343 ignore
? ", ignoring" : "");
1344 return ignore
? 0 : -ENOEXEC
;
1347 free(c
->selinux_context
);
1348 c
->selinux_context
= k
;
1349 c
->selinux_context_ignore
= ignore
;
1354 int config_parse_exec_apparmor_profile(
1356 const char *filename
,
1358 const char *section
,
1359 unsigned section_line
,
1366 ExecContext
*c
= data
;
1377 if (isempty(rvalue
)) {
1378 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1379 c
->apparmor_profile_ignore
= false;
1383 if (rvalue
[0] == '-') {
1389 r
= unit_full_printf(u
, rvalue
, &k
);
1391 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1392 "Failed to resolve specifiers%s: %m",
1393 ignore
? ", ignoring" : "");
1394 return ignore
? 0 : -ENOEXEC
;
1397 free(c
->apparmor_profile
);
1398 c
->apparmor_profile
= k
;
1399 c
->apparmor_profile_ignore
= ignore
;
1404 int config_parse_exec_smack_process_label(
1406 const char *filename
,
1408 const char *section
,
1409 unsigned section_line
,
1416 ExecContext
*c
= data
;
1427 if (isempty(rvalue
)) {
1428 c
->smack_process_label
= mfree(c
->smack_process_label
);
1429 c
->smack_process_label_ignore
= false;
1433 if (rvalue
[0] == '-') {
1439 r
= unit_full_printf(u
, rvalue
, &k
);
1441 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1442 "Failed to resolve specifiers%s: %m",
1443 ignore
? ", ignoring" : "");
1444 return ignore
? 0 : -ENOEXEC
;
1447 free(c
->smack_process_label
);
1448 c
->smack_process_label
= k
;
1449 c
->smack_process_label_ignore
= ignore
;
1454 int config_parse_timer(const char *unit
,
1455 const char *filename
,
1457 const char *section
,
1458 unsigned section_line
,
1469 CalendarSpec
*c
= NULL
;
1471 _cleanup_free_
char *k
= NULL
;
1479 if (isempty(rvalue
)) {
1480 /* Empty assignment resets list */
1481 timer_free_values(t
);
1485 b
= timer_base_from_string(lvalue
);
1487 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1491 r
= unit_full_printf(u
, rvalue
, &k
);
1493 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1497 if (b
== TIMER_CALENDAR
) {
1498 if (calendar_spec_from_string(k
, &c
) < 0) {
1499 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1503 if (parse_sec(k
, &usec
) < 0) {
1504 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1509 v
= new0(TimerValue
, 1);
1511 calendar_spec_free(c
);
1517 v
->calendar_spec
= c
;
1519 LIST_PREPEND(value
, t
->values
, v
);
1524 int config_parse_trigger_unit(
1526 const char *filename
,
1528 const char *section
,
1529 unsigned section_line
,
1536 _cleanup_free_
char *p
= NULL
;
1546 if (!hashmap_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1547 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1551 r
= unit_name_printf(u
, rvalue
, &p
);
1553 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1557 type
= unit_name_to_type(p
);
1559 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1563 if (type
== u
->type
) {
1564 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1568 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1570 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1577 int config_parse_path_spec(const char *unit
,
1578 const char *filename
,
1580 const char *section
,
1581 unsigned section_line
,
1591 _cleanup_free_
char *k
= NULL
;
1599 if (isempty(rvalue
)) {
1600 /* Empty assignment clears list */
1605 b
= path_type_from_string(lvalue
);
1607 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1611 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1613 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1617 if (!path_is_absolute(k
)) {
1618 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1622 s
= new0(PathSpec
, 1);
1627 s
->path
= path_kill_slashes(k
);
1632 LIST_PREPEND(spec
, p
->specs
, s
);
1637 int config_parse_socket_service(
1639 const char *filename
,
1641 const char *section
,
1642 unsigned section_line
,
1649 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1650 _cleanup_free_
char *p
= NULL
;
1660 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1662 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", rvalue
);
1666 if (!endswith(p
, ".service")) {
1667 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service: %s", rvalue
);
1671 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1673 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s: %s", rvalue
, bus_error_message(&error
, r
));
1677 unit_ref_set(&s
->service
, x
);
1682 int config_parse_fdname(
1684 const char *filename
,
1686 const char *section
,
1687 unsigned section_line
,
1694 _cleanup_free_
char *p
= NULL
;
1703 if (isempty(rvalue
)) {
1704 s
->fdname
= mfree(s
->fdname
);
1708 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1710 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1714 if (!fdname_is_valid(p
)) {
1715 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1719 return free_and_replace(s
->fdname
, p
);
1722 int config_parse_service_sockets(
1724 const char *filename
,
1726 const char *section
,
1727 unsigned section_line
,
1745 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1747 r
= extract_first_word(&p
, &word
, NULL
, 0);
1753 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1757 r
= unit_name_printf(UNIT(s
), word
, &k
);
1759 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1763 if (!endswith(k
, ".socket")) {
1764 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1768 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1770 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1772 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1774 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1780 int config_parse_bus_name(
1782 const char *filename
,
1784 const char *section
,
1785 unsigned section_line
,
1792 _cleanup_free_
char *k
= NULL
;
1801 r
= unit_full_printf(u
, rvalue
, &k
);
1803 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1807 if (!service_name_is_valid(k
)) {
1808 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1812 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1815 int config_parse_service_timeout(
1817 const char *filename
,
1819 const char *section
,
1820 unsigned section_line
,
1827 Service
*s
= userdata
;
1836 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1838 r
= parse_sec(rvalue
, &usec
);
1840 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1844 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1845 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1846 * all other timeouts. */
1848 usec
= USEC_INFINITY
;
1850 if (!streq(lvalue
, "TimeoutStopSec")) {
1851 s
->start_timeout_defined
= true;
1852 s
->timeout_start_usec
= usec
;
1855 if (!streq(lvalue
, "TimeoutStartSec"))
1856 s
->timeout_stop_usec
= usec
;
1861 int config_parse_sec_fix_0(
1863 const char *filename
,
1865 const char *section
,
1866 unsigned section_line
,
1873 usec_t
*usec
= data
;
1881 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1882 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1885 r
= parse_sec_fix_0(rvalue
, usec
);
1887 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1894 int config_parse_user_group(
1896 const char *filename
,
1898 const char *section
,
1899 unsigned section_line
,
1906 char **user
= data
, *n
;
1915 if (isempty(rvalue
))
1918 _cleanup_free_
char *k
= NULL
;
1920 r
= unit_full_printf(u
, rvalue
, &k
);
1922 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
1926 if (!valid_user_group_name_or_id(k
)) {
1927 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1941 int config_parse_user_group_strv(
1943 const char *filename
,
1945 const char *section
,
1946 unsigned section_line
,
1953 char ***users
= data
;
1963 if (isempty(rvalue
)) {
1964 *users
= strv_free(*users
);
1970 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1972 r
= extract_first_word(&p
, &word
, NULL
, 0);
1978 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax: %s", rvalue
);
1982 r
= unit_full_printf(u
, word
, &k
);
1984 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", word
);
1988 if (!valid_user_group_name_or_id(k
)) {
1989 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1993 r
= strv_push(users
, k
);
2003 int config_parse_working_directory(
2005 const char *filename
,
2007 const char *section
,
2008 unsigned section_line
,
2015 ExecContext
*c
= data
;
2026 if (rvalue
[0] == '-') {
2032 if (streq(rvalue
, "~")) {
2033 c
->working_directory_home
= true;
2034 c
->working_directory
= mfree(c
->working_directory
);
2036 _cleanup_free_
char *k
= NULL
;
2038 r
= unit_full_printf(u
, rvalue
, &k
);
2040 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2041 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2042 rvalue
, missing_ok
? ", ignoring" : "");
2043 return missing_ok
? 0 : -ENOEXEC
;
2046 path_kill_slashes(k
);
2048 if (!utf8_is_valid(k
)) {
2049 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2050 return missing_ok
? 0 : -ENOEXEC
;
2053 if (!path_is_absolute(k
)) {
2054 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2055 "Working directory path '%s' is not absolute%s.",
2056 rvalue
, missing_ok
? ", ignoring" : "");
2057 return missing_ok
? 0 : -ENOEXEC
;
2060 c
->working_directory_home
= false;
2061 free_and_replace(c
->working_directory
, k
);
2064 c
->working_directory_missing_ok
= missing_ok
;
2068 int config_parse_unit_env_file(const char *unit
,
2069 const char *filename
,
2071 const char *section
,
2072 unsigned section_line
,
2081 _cleanup_free_
char *n
= NULL
;
2089 if (isempty(rvalue
)) {
2090 /* Empty assignment frees the list */
2091 *env
= strv_free(*env
);
2095 r
= unit_full_printf(u
, rvalue
, &n
);
2097 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2101 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2102 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2106 r
= strv_extend(env
, n
);
2113 int config_parse_environ(
2115 const char *filename
,
2117 const char *section
,
2118 unsigned section_line
,
2135 if (isempty(rvalue
)) {
2136 /* Empty assignment resets the list */
2137 *env
= strv_free(*env
);
2141 for (p
= rvalue
;; ) {
2142 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2144 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2150 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2151 "Invalid syntax, ignoring: %s", rvalue
);
2156 r
= unit_full_printf(u
, word
, &k
);
2158 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2159 "Failed to resolve specifiers, ignoring: %s", word
);
2167 if (!env_assignment_is_valid(k
)) {
2168 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2169 "Invalid environment assignment, ignoring: %s", k
);
2173 r
= strv_env_replace(env
, k
);
2181 int config_parse_pass_environ(
2183 const char *filename
,
2185 const char *section
,
2186 unsigned section_line
,
2193 const char *whole_rvalue
= rvalue
;
2194 _cleanup_strv_free_
char **n
= NULL
;
2195 size_t nlen
= 0, nbufsize
= 0;
2196 char*** passenv
= data
;
2205 if (isempty(rvalue
)) {
2206 /* Empty assignment resets the list */
2207 *passenv
= strv_free(*passenv
);
2212 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2214 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2220 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2221 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2226 r
= unit_full_printf(u
, word
, &k
);
2228 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2229 "Failed to resolve specifiers, ignoring: %s", word
);
2237 if (!env_name_is_valid(k
)) {
2238 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2239 "Invalid environment name for %s, ignoring: %s", lvalue
, k
);
2243 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2252 r
= strv_extend_strv(passenv
, n
, true);
2260 int config_parse_unset_environ(
2262 const char *filename
,
2264 const char *section
,
2265 unsigned section_line
,
2272 _cleanup_strv_free_
char **n
= NULL
;
2273 const char *whole_rvalue
= rvalue
;
2274 size_t nlen
= 0, nbufsize
= 0;
2275 char*** unsetenv
= data
;
2284 if (isempty(rvalue
)) {
2285 /* Empty assignment resets the list */
2286 *unsetenv
= strv_free(*unsetenv
);
2291 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2293 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2299 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2300 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2305 r
= unit_full_printf(u
, word
, &k
);
2307 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2308 "Failed to resolve specifiers, ignoring: %s", word
);
2316 if (!env_assignment_is_valid(k
) && !env_name_is_valid(k
)) {
2317 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2318 "Invalid environment name or assignment %s, ignoring: %s", lvalue
, k
);
2322 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2331 r
= strv_extend_strv(unsetenv
, n
, true);
2339 int config_parse_log_extra_fields(
2341 const char *filename
,
2343 const char *section
,
2344 unsigned section_line
,
2351 ExecContext
*c
= data
;
2361 if (isempty(rvalue
)) {
2362 exec_context_free_log_extra_fields(c
);
2366 for (p
= rvalue
;; ) {
2367 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2371 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2377 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2381 r
= unit_full_printf(u
, word
, &k
);
2383 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring field: %m", word
);
2387 eq
= strchr(k
, '=');
2389 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field lacks '=' character, ignoring field: %s", k
);
2393 if (!journal_field_valid(k
, eq
-k
, false)) {
2394 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field name is invalid, ignoring field: %s", k
);
2398 t
= realloc_multiply(c
->log_extra_fields
, sizeof(struct iovec
), c
->n_log_extra_fields
+1);
2402 c
->log_extra_fields
= t
;
2403 c
->log_extra_fields
[c
->n_log_extra_fields
++] = IOVEC_MAKE_STRING(k
);
2411 int config_parse_ip_tos(const char *unit
,
2412 const char *filename
,
2414 const char *section
,
2415 unsigned section_line
,
2422 int *ip_tos
= data
, x
;
2429 x
= ip_tos_from_string(rvalue
);
2431 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2439 int config_parse_unit_condition_path(
2441 const char *filename
,
2443 const char *section
,
2444 unsigned section_line
,
2451 _cleanup_free_
char *p
= NULL
;
2452 Condition
**list
= data
, *c
;
2453 ConditionType t
= ltype
;
2454 bool trigger
, negate
;
2463 if (isempty(rvalue
)) {
2464 /* Empty assignment resets the list */
2465 *list
= condition_free_list(*list
);
2469 trigger
= rvalue
[0] == '|';
2473 negate
= rvalue
[0] == '!';
2477 r
= unit_full_printf(u
, rvalue
, &p
);
2479 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2483 if (!path_is_absolute(p
)) {
2484 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2488 c
= condition_new(t
, p
, trigger
, negate
);
2492 LIST_PREPEND(conditions
, *list
, c
);
2496 int config_parse_unit_condition_string(
2498 const char *filename
,
2500 const char *section
,
2501 unsigned section_line
,
2508 _cleanup_free_
char *s
= NULL
;
2509 Condition
**list
= data
, *c
;
2510 ConditionType t
= ltype
;
2511 bool trigger
, negate
;
2520 if (isempty(rvalue
)) {
2521 /* Empty assignment resets the list */
2522 *list
= condition_free_list(*list
);
2526 trigger
= rvalue
[0] == '|';
2530 negate
= rvalue
[0] == '!';
2534 r
= unit_full_printf(u
, rvalue
, &s
);
2536 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2540 c
= condition_new(t
, s
, trigger
, negate
);
2544 LIST_PREPEND(conditions
, *list
, c
);
2548 int config_parse_unit_condition_null(
2550 const char *filename
,
2552 const char *section
,
2553 unsigned section_line
,
2560 Condition
**list
= data
, *c
;
2561 bool trigger
, negate
;
2569 if (isempty(rvalue
)) {
2570 /* Empty assignment resets the list */
2571 *list
= condition_free_list(*list
);
2575 trigger
= rvalue
[0] == '|';
2579 negate
= rvalue
[0] == '!';
2583 b
= parse_boolean(rvalue
);
2585 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2592 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2596 LIST_PREPEND(conditions
, *list
, c
);
2600 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2601 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2603 int config_parse_unit_requires_mounts_for(
2605 const char *filename
,
2607 const char *section
,
2608 unsigned section_line
,
2624 for (p
= rvalue
;; ) {
2625 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2627 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2633 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2634 "Invalid syntax, ignoring: %s", rvalue
);
2638 if (!utf8_is_valid(word
)) {
2639 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2643 r
= unit_full_printf(u
, word
, &resolved
);
2645 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit name \"%s\", ignoring: %m", word
);
2649 r
= unit_require_mounts_for(u
, resolved
, UNIT_DEPENDENCY_FILE
);
2651 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", resolved
);
2657 int config_parse_documentation(const char *unit
,
2658 const char *filename
,
2660 const char *section
,
2661 unsigned section_line
,
2677 if (isempty(rvalue
)) {
2678 /* Empty assignment resets the list */
2679 u
->documentation
= strv_free(u
->documentation
);
2683 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2684 rvalue
, data
, userdata
);
2688 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2690 if (documentation_url_is_valid(*a
))
2693 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2705 static int syscall_filter_parse_one(
2707 const char *filename
,
2717 const SyscallFilterSet
*set
;
2720 set
= syscall_filter_set_find(t
);
2723 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Don't know system call group, ignoring: %s", t
);
2727 NULSTR_FOREACH(i
, set
->value
) {
2728 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, i
, false, errno_num
);
2735 id
= seccomp_syscall_resolve_name(t
);
2736 if (id
== __NR_SCMP_ERROR
) {
2738 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2742 /* If we previously wanted to forbid a syscall and now
2743 * we want to allow it, then remove it from the list
2745 if (!invert
== c
->syscall_whitelist
) {
2746 r
= hashmap_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1), INT_TO_PTR(errno_num
));
2752 (void) hashmap_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2758 int config_parse_syscall_filter(
2760 const char *filename
,
2762 const char *section
,
2763 unsigned section_line
,
2770 ExecContext
*c
= data
;
2772 bool invert
= false;
2781 if (isempty(rvalue
)) {
2782 /* Empty assignment resets the list */
2783 c
->syscall_filter
= hashmap_free(c
->syscall_filter
);
2784 c
->syscall_whitelist
= false;
2788 if (rvalue
[0] == '~') {
2793 if (!c
->syscall_filter
) {
2794 c
->syscall_filter
= hashmap_new(NULL
);
2795 if (!c
->syscall_filter
)
2799 /* Allow everything but the ones listed */
2800 c
->syscall_whitelist
= false;
2802 /* Allow nothing but the ones listed */
2803 c
->syscall_whitelist
= true;
2805 /* Accept default syscalls if we are on a whitelist */
2806 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, false, "@default", false, -1);
2814 _cleanup_free_
char *word
= NULL
, *name
= NULL
;
2817 r
= extract_first_word(&p
, &word
, NULL
, 0);
2823 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2827 r
= parse_syscall_and_errno(word
, &name
, &num
);
2829 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse syscall:errno, ignoring: %s", word
);
2833 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, name
, true, num
);
2841 int config_parse_syscall_archs(
2843 const char *filename
,
2845 const char *section
,
2846 unsigned section_line
,
2857 if (isempty(rvalue
)) {
2858 *archs
= set_free(*archs
);
2862 r
= set_ensure_allocated(archs
, NULL
);
2866 for (p
= rvalue
;;) {
2867 _cleanup_free_
char *word
= NULL
;
2870 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2876 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2877 "Invalid syntax, ignoring: %s", rvalue
);
2881 r
= seccomp_arch_from_string(word
, &a
);
2883 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2884 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
2888 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2894 int config_parse_syscall_errno(
2896 const char *filename
,
2898 const char *section
,
2899 unsigned section_line
,
2906 ExecContext
*c
= data
;
2913 if (isempty(rvalue
)) {
2914 /* Empty assignment resets to KILL */
2915 c
->syscall_errno
= 0;
2919 e
= parse_errno(rvalue
);
2921 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2925 c
->syscall_errno
= e
;
2929 int config_parse_address_families(
2931 const char *filename
,
2933 const char *section
,
2934 unsigned section_line
,
2941 ExecContext
*c
= data
;
2942 bool invert
= false;
2950 if (isempty(rvalue
)) {
2951 /* Empty assignment resets the list */
2952 c
->address_families
= set_free(c
->address_families
);
2953 c
->address_families_whitelist
= false;
2957 if (rvalue
[0] == '~') {
2962 if (!c
->address_families
) {
2963 c
->address_families
= set_new(NULL
);
2964 if (!c
->address_families
)
2967 c
->address_families_whitelist
= !invert
;
2970 for (p
= rvalue
;;) {
2971 _cleanup_free_
char *word
= NULL
;
2974 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2980 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2981 "Invalid syntax, ignoring: %s", rvalue
);
2985 af
= af_from_name(word
);
2987 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2988 "Failed to parse address family \"%s\", ignoring: %m", word
);
2992 /* If we previously wanted to forbid an address family and now
2993 * we want to allow it, then just remove it from the list.
2995 if (!invert
== c
->address_families_whitelist
) {
2996 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
3000 set_remove(c
->address_families
, INT_TO_PTR(af
));
3004 int config_parse_restrict_namespaces(
3006 const char *filename
,
3008 const char *section
,
3009 unsigned section_line
,
3016 ExecContext
*c
= data
;
3017 bool invert
= false;
3020 if (isempty(rvalue
)) {
3021 /* Reset to the default. */
3022 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3026 if (rvalue
[0] == '~') {
3031 r
= parse_boolean(rvalue
);
3033 c
->restrict_namespaces
= 0;
3035 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3037 /* Not a boolean argument, in this case it's a list of namespace types. */
3039 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
3041 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
3047 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
3053 int config_parse_unit_slice(
3055 const char *filename
,
3057 const char *section
,
3058 unsigned section_line
,
3065 _cleanup_free_
char *k
= NULL
;
3066 Unit
*u
= userdata
, *slice
= NULL
;
3074 r
= unit_name_printf(u
, rvalue
, &k
);
3076 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
3080 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
3082 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
3086 r
= unit_set_slice(u
, slice
);
3088 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
3095 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
3097 int config_parse_cpu_weight(
3099 const char *filename
,
3101 const char *section
,
3102 unsigned section_line
,
3109 uint64_t *weight
= data
;
3116 r
= cg_weight_parse(rvalue
, weight
);
3118 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
3125 int config_parse_cpu_shares(
3127 const char *filename
,
3129 const char *section
,
3130 unsigned section_line
,
3137 uint64_t *shares
= data
;
3144 r
= cg_cpu_shares_parse(rvalue
, shares
);
3146 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
3153 int config_parse_cpu_quota(
3155 const char *filename
,
3157 const char *section
,
3158 unsigned section_line
,
3165 CGroupContext
*c
= data
;
3172 if (isempty(rvalue
)) {
3173 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3177 r
= parse_percent_unbounded(rvalue
);
3179 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3183 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3187 int config_parse_memory_limit(
3189 const char *filename
,
3191 const char *section
,
3192 unsigned section_line
,
3199 CGroupContext
*c
= data
;
3200 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3203 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3205 r
= parse_percent(rvalue
);
3207 r
= parse_size(rvalue
, 1024, &bytes
);
3209 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3213 bytes
= physical_memory_scale(r
, 100U);
3215 if (bytes
<= 0 || bytes
>= UINT64_MAX
) {
3216 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3221 if (streq(lvalue
, "MemoryLow"))
3222 c
->memory_low
= bytes
;
3223 else if (streq(lvalue
, "MemoryHigh"))
3224 c
->memory_high
= bytes
;
3225 else if (streq(lvalue
, "MemoryMax"))
3226 c
->memory_max
= bytes
;
3227 else if (streq(lvalue
, "MemorySwapMax"))
3228 c
->memory_swap_max
= bytes
;
3229 else if (streq(lvalue
, "MemoryLimit"))
3230 c
->memory_limit
= bytes
;
3237 int config_parse_tasks_max(
3239 const char *filename
,
3241 const char *section
,
3242 unsigned section_line
,
3249 uint64_t *tasks_max
= data
, v
;
3253 if (isempty(rvalue
)) {
3254 *tasks_max
= u
->manager
->default_tasks_max
;
3258 if (streq(rvalue
, "infinity")) {
3259 *tasks_max
= CGROUP_LIMIT_MAX
;
3263 r
= parse_percent(rvalue
);
3265 r
= safe_atou64(rvalue
, &v
);
3267 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3271 v
= system_tasks_max_scale(r
, 100U);
3273 if (v
<= 0 || v
>= UINT64_MAX
) {
3274 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3282 int config_parse_delegate(
3284 const char *filename
,
3286 const char *section
,
3287 unsigned section_line
,
3294 CGroupContext
*c
= data
;
3297 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3298 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3299 * mask to delegate. */
3301 if (isempty(rvalue
)) {
3303 c
->delegate_controllers
= 0;
3307 r
= parse_boolean(rvalue
);
3309 const char *p
= rvalue
;
3310 CGroupMask mask
= 0;
3313 _cleanup_free_
char *word
= NULL
;
3314 CGroupController cc
;
3316 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3322 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
3326 cc
= cgroup_controller_from_string(word
);
3328 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid controller name '%s', ignoring", rvalue
);
3332 mask
|= CGROUP_CONTROLLER_TO_MASK(cc
);
3336 c
->delegate_controllers
|= mask
;
3340 c
->delegate_controllers
= _CGROUP_MASK_ALL
;
3342 c
->delegate
= false;
3343 c
->delegate_controllers
= 0;
3349 int config_parse_device_allow(
3351 const char *filename
,
3353 const char *section
,
3354 unsigned section_line
,
3361 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3362 CGroupContext
*c
= data
;
3363 CGroupDeviceAllow
*a
;
3364 const char *m
= NULL
;
3368 if (isempty(rvalue
)) {
3369 while (c
->device_allow
)
3370 cgroup_context_free_device_allow(c
, c
->device_allow
);
3375 r
= unit_full_printf(userdata
, rvalue
, &t
);
3377 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3378 "Failed to resolve specifiers in %s, ignoring: %m",
3382 n
= strcspn(t
, WHITESPACE
);
3384 path
= strndup(t
, n
);
3388 if (!is_deviceallow_pattern(path
)) {
3389 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3393 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3397 if (!in_charset(m
, "rwm")) {
3398 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3402 a
= new0(CGroupDeviceAllow
, 1);
3408 a
->r
= !!strchr(m
, 'r');
3409 a
->w
= !!strchr(m
, 'w');
3410 a
->m
= !!strchr(m
, 'm');
3412 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3416 int config_parse_io_weight(
3418 const char *filename
,
3420 const char *section
,
3421 unsigned section_line
,
3428 uint64_t *weight
= data
;
3435 r
= cg_weight_parse(rvalue
, weight
);
3437 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3444 int config_parse_io_device_weight(
3446 const char *filename
,
3448 const char *section
,
3449 unsigned section_line
,
3456 _cleanup_free_
char *path
= NULL
;
3457 CGroupIODeviceWeight
*w
;
3458 CGroupContext
*c
= data
;
3468 if (isempty(rvalue
)) {
3469 while (c
->io_device_weights
)
3470 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3475 n
= strcspn(rvalue
, WHITESPACE
);
3476 weight
= rvalue
+ n
;
3477 weight
+= strspn(weight
, WHITESPACE
);
3479 if (isempty(weight
)) {
3480 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3484 path
= strndup(rvalue
, n
);
3488 if (!path_startswith(path
, "/dev")) {
3489 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3493 r
= cg_weight_parse(weight
, &u
);
3495 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3499 assert(u
!= CGROUP_WEIGHT_INVALID
);
3501 w
= new0(CGroupIODeviceWeight
, 1);
3510 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3514 int config_parse_io_limit(
3516 const char *filename
,
3518 const char *section
,
3519 unsigned section_line
,
3526 _cleanup_free_
char *path
= NULL
;
3527 CGroupIODeviceLimit
*l
= NULL
, *t
;
3528 CGroupContext
*c
= data
;
3529 CGroupIOLimitType type
;
3539 type
= cgroup_io_limit_type_from_string(lvalue
);
3542 if (isempty(rvalue
)) {
3543 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3544 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3548 n
= strcspn(rvalue
, WHITESPACE
);
3550 limit
+= strspn(limit
, WHITESPACE
);
3553 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3557 path
= strndup(rvalue
, n
);
3561 if (!path_startswith(path
, "/dev")) {
3562 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3566 if (streq("infinity", limit
)) {
3567 num
= CGROUP_LIMIT_MAX
;
3569 r
= parse_size(limit
, 1000, &num
);
3570 if (r
< 0 || num
<= 0) {
3571 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3576 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3577 if (path_equal(path
, t
->path
)) {
3584 CGroupIOLimitType ttype
;
3586 l
= new0(CGroupIODeviceLimit
, 1);
3592 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3593 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3595 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3598 l
->limits
[type
] = num
;
3603 int config_parse_blockio_weight(
3605 const char *filename
,
3607 const char *section
,
3608 unsigned section_line
,
3615 uint64_t *weight
= data
;
3622 r
= cg_blkio_weight_parse(rvalue
, weight
);
3624 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3631 int config_parse_blockio_device_weight(
3633 const char *filename
,
3635 const char *section
,
3636 unsigned section_line
,
3643 _cleanup_free_
char *path
= NULL
;
3644 CGroupBlockIODeviceWeight
*w
;
3645 CGroupContext
*c
= data
;
3655 if (isempty(rvalue
)) {
3656 while (c
->blockio_device_weights
)
3657 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3662 n
= strcspn(rvalue
, WHITESPACE
);
3663 weight
= rvalue
+ n
;
3664 weight
+= strspn(weight
, WHITESPACE
);
3666 if (isempty(weight
)) {
3667 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3671 path
= strndup(rvalue
, n
);
3675 if (!path_startswith(path
, "/dev")) {
3676 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3680 r
= cg_blkio_weight_parse(weight
, &u
);
3682 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3686 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3688 w
= new0(CGroupBlockIODeviceWeight
, 1);
3697 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3701 int config_parse_blockio_bandwidth(
3703 const char *filename
,
3705 const char *section
,
3706 unsigned section_line
,
3713 _cleanup_free_
char *path
= NULL
;
3714 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3715 CGroupContext
*c
= data
;
3716 const char *bandwidth
;
3726 read
= streq("BlockIOReadBandwidth", lvalue
);
3728 if (isempty(rvalue
)) {
3729 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3730 b
->rbps
= CGROUP_LIMIT_MAX
;
3731 b
->wbps
= CGROUP_LIMIT_MAX
;
3736 n
= strcspn(rvalue
, WHITESPACE
);
3737 bandwidth
= rvalue
+ n
;
3738 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3741 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3745 path
= strndup(rvalue
, n
);
3749 if (!path_startswith(path
, "/dev")) {
3750 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3754 r
= parse_size(bandwidth
, 1000, &bytes
);
3755 if (r
< 0 || bytes
<= 0) {
3756 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3760 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3761 if (path_equal(path
, t
->path
)) {
3768 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3774 b
->rbps
= CGROUP_LIMIT_MAX
;
3775 b
->wbps
= CGROUP_LIMIT_MAX
;
3777 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3788 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3790 int config_parse_job_mode_isolate(
3792 const char *filename
,
3794 const char *section
,
3795 unsigned section_line
,
3809 r
= parse_boolean(rvalue
);
3811 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3815 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3819 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode
, exec_preserve_mode
, ExecPreserveMode
, "Failed to parse runtime directory preserve mode");
3821 int config_parse_exec_directories(
3823 const char *filename
,
3825 const char *section
,
3826 unsigned section_line
,
3843 if (isempty(rvalue
)) {
3844 /* Empty assignment resets the list */
3845 *rt
= strv_free(*rt
);
3849 for (p
= rvalue
;;) {
3850 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3852 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3856 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3857 "Invalid syntax, ignoring: %s", rvalue
);
3863 r
= unit_full_printf(u
, word
, &k
);
3865 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3866 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
3870 if (!path_is_safe(k
) || path_is_absolute(k
)) {
3871 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3872 "%s= path is not valid, ignoring assignment: %s", lvalue
, rvalue
);
3876 r
= strv_push(rt
, k
);
3883 int config_parse_set_status(
3885 const char *filename
,
3887 const char *section
,
3888 unsigned section_line
,
3896 const char *word
, *state
;
3898 ExitStatusSet
*status_set
= data
;
3905 /* Empty assignment resets the list */
3906 if (isempty(rvalue
)) {
3907 exit_status_set_free(status_set
);
3911 FOREACH_WORD(word
, l
, rvalue
, state
) {
3912 _cleanup_free_
char *temp
;
3916 temp
= strndup(word
, l
);
3920 r
= safe_atoi(temp
, &val
);
3922 val
= signal_from_string_try_harder(temp
);
3925 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3928 set
= &status_set
->signal
;
3930 if (val
< 0 || val
> 255) {
3931 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3934 set
= &status_set
->status
;
3937 r
= set_ensure_allocated(set
, NULL
);
3941 r
= set_put(*set
, INT_TO_PTR(val
));
3943 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3947 if (!isempty(state
))
3948 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3953 int config_parse_namespace_path_strv(
3955 const char *filename
,
3957 const char *section
,
3958 unsigned section_line
,
3975 if (isempty(rvalue
)) {
3976 /* Empty assignment resets the list */
3977 *sv
= strv_free(*sv
);
3983 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
3985 bool ignore_enoent
= false, shall_prefix
= false;
3987 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3993 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
3997 if (!utf8_is_valid(word
)) {
3998 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
4003 if (startswith(w
, "-")) {
4004 ignore_enoent
= true;
4007 if (startswith(w
, "+")) {
4008 shall_prefix
= true;
4012 r
= unit_full_printf(u
, w
, &resolved
);
4014 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s: %m", word
);
4018 if (!path_is_absolute(resolved
)) {
4019 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
4023 path_kill_slashes(resolved
);
4025 joined
= strjoin(ignore_enoent
? "-" : "",
4026 shall_prefix
? "+" : "",
4029 r
= strv_push(sv
, joined
);
4039 int config_parse_bind_paths(
4041 const char *filename
,
4043 const char *section
,
4044 unsigned section_line
,
4051 ExecContext
*c
= data
;
4061 if (isempty(rvalue
)) {
4062 /* Empty assignment resets the list */
4063 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
4064 c
->bind_mounts
= NULL
;
4065 c
->n_bind_mounts
= 0;
4071 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
4072 _cleanup_free_
char *sresolved
= NULL
, *dresolved
= NULL
;
4073 char *s
= NULL
, *d
= NULL
;
4074 bool rbind
= true, ignore_enoent
= false;
4076 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4082 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4086 r
= unit_full_printf(u
, source
, &sresolved
);
4088 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4089 "Failed to resolved specifiers in \"%s\", ignoring: %m", source
);
4095 ignore_enoent
= true;
4099 if (!utf8_is_valid(s
)) {
4100 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, s
);
4103 if (!path_is_absolute(s
)) {
4104 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute source path, ignoring: %s", s
);
4108 path_kill_slashes(s
);
4110 /* Optionally, the destination is specified. */
4111 if (p
&& p
[-1] == ':') {
4112 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4116 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4120 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':': %s", rvalue
);
4124 r
= unit_full_printf(u
, destination
, &dresolved
);
4126 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4127 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination
);
4131 if (!utf8_is_valid(dresolved
)) {
4132 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, dresolved
);
4135 if (!path_is_absolute(dresolved
)) {
4136 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute destination path, ignoring: %s", dresolved
);
4140 d
= path_kill_slashes(dresolved
);
4142 /* Optionally, there's also a short option string specified */
4143 if (p
&& p
[-1] == ':') {
4144 _cleanup_free_
char *options
= NULL
;
4146 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
4150 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4154 if (isempty(options
) || streq(options
, "rbind"))
4156 else if (streq(options
, "norbind"))
4159 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
4166 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
4170 .read_only
= !!strstr(lvalue
, "ReadOnly"),
4172 .ignore_enoent
= ignore_enoent
,
4181 int config_parse_no_new_privileges(
4183 const char *filename
,
4185 const char *section
,
4186 unsigned section_line
,
4193 ExecContext
*c
= data
;
4201 k
= parse_boolean(rvalue
);
4203 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
4207 c
->no_new_privileges
= k
;
4212 int config_parse_protect_home(
4214 const char *filename
,
4216 const char *section
,
4217 unsigned section_line
,
4224 ExecContext
*c
= data
;
4232 /* Our enum shall be a superset of booleans, hence first try
4233 * to parse as boolean, and then as enum */
4235 k
= parse_boolean(rvalue
);
4237 c
->protect_home
= PROTECT_HOME_YES
;
4239 c
->protect_home
= PROTECT_HOME_NO
;
4243 h
= protect_home_from_string(rvalue
);
4245 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
4249 c
->protect_home
= h
;
4255 int config_parse_protect_system(
4257 const char *filename
,
4259 const char *section
,
4260 unsigned section_line
,
4267 ExecContext
*c
= data
;
4275 /* Our enum shall be a superset of booleans, hence first try
4276 * to parse as boolean, and then as enum */
4278 k
= parse_boolean(rvalue
);
4280 c
->protect_system
= PROTECT_SYSTEM_YES
;
4282 c
->protect_system
= PROTECT_SYSTEM_NO
;
4286 s
= protect_system_from_string(rvalue
);
4288 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
4292 c
->protect_system
= s
;
4298 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode
, exec_keyring_mode
, ExecKeyringMode
, "Failed to parse keyring mode");
4300 int config_parse_job_timeout_sec(
4302 const char *filename
,
4304 const char *section
,
4305 unsigned section_line
,
4321 r
= parse_sec_fix_0(rvalue
, &usec
);
4323 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue
);
4327 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
4328 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
4331 if (!u
->job_running_timeout_set
)
4332 u
->job_running_timeout
= usec
;
4334 u
->job_timeout
= usec
;
4339 int config_parse_job_running_timeout_sec(
4341 const char *filename
,
4343 const char *section
,
4344 unsigned section_line
,
4360 r
= parse_sec_fix_0(rvalue
, &usec
);
4362 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue
);
4366 u
->job_running_timeout
= usec
;
4367 u
->job_running_timeout_set
= true;
4372 #define FOLLOW_MAX 8
4374 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4385 /* This will update the filename pointer if the loaded file is
4386 * reached by a symlink. The old string will be freed. */
4389 char *target
, *name
;
4391 if (c
++ >= FOLLOW_MAX
)
4394 path_kill_slashes(*filename
);
4396 /* Add the file name we are currently looking at to
4397 * the names of this unit, but only if it is a valid
4399 name
= basename(*filename
);
4400 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4402 id
= set_get(names
, name
);
4408 r
= set_consume(names
, id
);
4414 /* Try to open the file name, but don't if its a symlink */
4415 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4422 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4423 r
= readlink_and_make_absolute(*filename
, &target
);
4431 f
= fdopen(fd
, "re");
4443 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4451 /* Let's try to add in all symlink names we found */
4452 while ((k
= set_steal_first(names
))) {
4454 /* First try to merge in the other name into our
4456 r
= unit_merge_by_name(*u
, k
);
4460 /* Hmm, we couldn't merge the other unit into
4461 * ours? Then let's try it the other way
4464 /* If the symlink name we are looking at is unit template, then
4465 we must search for instance of this template */
4466 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4467 _cleanup_free_
char *instance
= NULL
;
4469 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4473 other
= manager_get_unit((*u
)->manager
, instance
);
4475 other
= manager_get_unit((*u
)->manager
, k
);
4480 r
= unit_merge(other
, *u
);
4483 return merge_by_names(u
, names
, NULL
);
4491 unit_choose_id(*u
, id
);
4499 static int load_from_path(Unit
*u
, const char *path
) {
4500 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4501 _cleanup_fclose_
FILE *f
= NULL
;
4502 _cleanup_free_
char *filename
= NULL
;
4511 symlink_names
= set_new(&string_hash_ops
);
4515 if (path_is_absolute(path
)) {
4517 filename
= strdup(path
);
4521 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4523 filename
= mfree(filename
);
4531 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4533 /* Instead of opening the path right away, we manually
4534 * follow all symlinks and add their name to our unit
4535 * name set while doing so */
4536 filename
= path_make_absolute(path
, *p
);
4540 if (u
->manager
->unit_path_cache
&&
4541 !set_get(u
->manager
->unit_path_cache
, filename
))
4544 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4547 filename
= mfree(filename
);
4549 /* ENOENT means that the file is missing or is a dangling symlink.
4550 * ENOTDIR means that one of paths we expect to be is a directory
4551 * is not a directory, we should just ignore that.
4552 * EACCES means that the directory or file permissions are wrong.
4555 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4556 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4559 /* Empty the symlink names for the next run */
4560 set_clear_free(symlink_names
);
4565 /* Hmm, no suitable file found? */
4568 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4569 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4574 r
= merge_by_names(&merged
, symlink_names
, id
);
4579 u
->load_state
= UNIT_MERGED
;
4583 if (fstat(fileno(f
), &st
) < 0)
4586 if (null_or_empty(&st
)) {
4587 u
->load_state
= UNIT_MASKED
;
4588 u
->fragment_mtime
= 0;
4590 u
->load_state
= UNIT_LOADED
;
4591 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4593 /* Now, parse the file contents */
4594 r
= config_parse(u
->id
, filename
, f
,
4595 UNIT_VTABLE(u
)->sections
,
4596 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4597 CONFIG_PARSE_ALLOW_INCLUDE
, u
);
4602 free(u
->fragment_path
);
4603 u
->fragment_path
= filename
;
4606 if (u
->source_path
) {
4607 if (stat(u
->source_path
, &st
) >= 0)
4608 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4610 u
->source_mtime
= 0;
4616 int unit_load_fragment(Unit
*u
) {
4622 assert(u
->load_state
== UNIT_STUB
);
4626 u
->load_state
= UNIT_LOADED
;
4630 /* First, try to find the unit under its id. We always look
4631 * for unit files in the default directories, to make it easy
4632 * to override things by placing things in /etc/systemd/system */
4633 r
= load_from_path(u
, u
->id
);
4637 /* Try to find an alias we can load this with */
4638 if (u
->load_state
== UNIT_STUB
) {
4639 SET_FOREACH(t
, u
->names
, i
) {
4644 r
= load_from_path(u
, t
);
4648 if (u
->load_state
!= UNIT_STUB
)
4653 /* And now, try looking for it under the suggested (originally linked) path */
4654 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4656 r
= load_from_path(u
, u
->fragment_path
);
4660 if (u
->load_state
== UNIT_STUB
)
4661 /* Hmm, this didn't work? Then let's get rid
4662 * of the fragment path stored for us, so that
4663 * we don't point to an invalid location. */
4664 u
->fragment_path
= mfree(u
->fragment_path
);
4667 /* Look for a template */
4668 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4669 _cleanup_free_
char *k
= NULL
;
4671 r
= unit_name_template(u
->id
, &k
);
4675 r
= load_from_path(u
, k
);
4678 log_unit_notice(u
, "Unit configuration has fatal error, unit will not be started.");
4682 if (u
->load_state
== UNIT_STUB
) {
4683 SET_FOREACH(t
, u
->names
, i
) {
4684 _cleanup_free_
char *z
= NULL
;
4689 r
= unit_name_template(t
, &z
);
4693 r
= load_from_path(u
, z
);
4697 if (u
->load_state
!= UNIT_STUB
)
4706 void unit_dump_config_items(FILE *f
) {
4707 static const struct {
4708 const ConfigParserCallback callback
;
4711 #if !HAVE_SYSV_COMPAT || !HAVE_SECCOMP || !HAVE_PAM || !HAVE_SELINUX || !ENABLE_SMACK || !HAVE_APPARMOR
4712 { config_parse_warn_compat
, "NOTSUPPORTED" },
4714 { config_parse_int
, "INTEGER" },
4715 { config_parse_unsigned
, "UNSIGNED" },
4716 { config_parse_iec_size
, "SIZE" },
4717 { config_parse_iec_uint64
, "SIZE" },
4718 { config_parse_si_size
, "SIZE" },
4719 { config_parse_bool
, "BOOLEAN" },
4720 { config_parse_string
, "STRING" },
4721 { config_parse_path
, "PATH" },
4722 { config_parse_unit_path_printf
, "PATH" },
4723 { config_parse_strv
, "STRING [...]" },
4724 { config_parse_exec_nice
, "NICE" },
4725 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4726 { config_parse_exec_io_class
, "IOCLASS" },
4727 { config_parse_exec_io_priority
, "IOPRIORITY" },
4728 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4729 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4730 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4731 { config_parse_mode
, "MODE" },
4732 { config_parse_unit_env_file
, "FILE" },
4733 { config_parse_exec_output
, "OUTPUT" },
4734 { config_parse_exec_input
, "INPUT" },
4735 { config_parse_log_facility
, "FACILITY" },
4736 { config_parse_log_level
, "LEVEL" },
4737 { config_parse_exec_secure_bits
, "SECUREBITS" },
4738 { config_parse_capability_set
, "BOUNDINGSET" },
4739 { config_parse_limit
, "LIMIT" },
4740 { config_parse_unit_deps
, "UNIT [...]" },
4741 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4742 { config_parse_service_type
, "SERVICETYPE" },
4743 { config_parse_service_restart
, "SERVICERESTART" },
4744 #if HAVE_SYSV_COMPAT
4745 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4747 { config_parse_kill_mode
, "KILLMODE" },
4748 { config_parse_signal
, "SIGNAL" },
4749 { config_parse_socket_listen
, "SOCKET [...]" },
4750 { config_parse_socket_bind
, "SOCKETBIND" },
4751 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4752 { config_parse_sec
, "SECONDS" },
4753 { config_parse_nsec
, "NANOSECONDS" },
4754 { config_parse_namespace_path_strv
, "PATH [...]" },
4755 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4756 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4757 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4758 { config_parse_unit_string_printf
, "STRING" },
4759 { config_parse_trigger_unit
, "UNIT" },
4760 { config_parse_timer
, "TIMER" },
4761 { config_parse_path_spec
, "PATH" },
4762 { config_parse_notify_access
, "ACCESS" },
4763 { config_parse_ip_tos
, "TOS" },
4764 { config_parse_unit_condition_path
, "CONDITION" },
4765 { config_parse_unit_condition_string
, "CONDITION" },
4766 { config_parse_unit_condition_null
, "CONDITION" },
4767 { config_parse_unit_slice
, "SLICE" },
4768 { config_parse_documentation
, "URL" },
4769 { config_parse_service_timeout
, "SECONDS" },
4770 { config_parse_emergency_action
, "ACTION" },
4771 { config_parse_set_status
, "STATUS" },
4772 { config_parse_service_sockets
, "SOCKETS" },
4773 { config_parse_environ
, "ENVIRON" },
4775 { config_parse_syscall_filter
, "SYSCALLS" },
4776 { config_parse_syscall_archs
, "ARCHS" },
4777 { config_parse_syscall_errno
, "ERRNO" },
4778 { config_parse_address_families
, "FAMILIES" },
4779 { config_parse_restrict_namespaces
, "NAMESPACES" },
4781 { config_parse_cpu_shares
, "SHARES" },
4782 { config_parse_cpu_weight
, "WEIGHT" },
4783 { config_parse_memory_limit
, "LIMIT" },
4784 { config_parse_device_allow
, "DEVICE" },
4785 { config_parse_device_policy
, "POLICY" },
4786 { config_parse_io_limit
, "LIMIT" },
4787 { config_parse_io_weight
, "WEIGHT" },
4788 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4789 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4790 { config_parse_blockio_weight
, "WEIGHT" },
4791 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4792 { config_parse_long
, "LONG" },
4793 { config_parse_socket_service
, "SERVICE" },
4795 { config_parse_exec_selinux_context
, "LABEL" },
4797 { config_parse_job_mode
, "MODE" },
4798 { config_parse_job_mode_isolate
, "BOOLEAN" },
4799 { config_parse_personality
, "PERSONALITY" },
4802 const char *prev
= NULL
;
4807 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4808 const char *rvalue
= "OTHER", *lvalue
;
4812 const ConfigPerfItem
*p
;
4814 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4816 dot
= strchr(i
, '.');
4817 lvalue
= dot
? dot
+ 1 : i
;
4821 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
4825 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
4828 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
4829 if (p
->parse
== table
[j
].callback
) {
4830 rvalue
= table
[j
].rvalue
;
4834 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);