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"
50 #include "journal-util.h"
51 #include "load-fragment.h"
54 #include "mount-util.h"
55 #include "parse-util.h"
56 #include "path-util.h"
57 #include "process-util.h"
58 #include "rlimit-util.h"
60 #include "seccomp-util.h"
62 #include "securebits.h"
63 #include "securebits-util.h"
64 #include "signal-util.h"
65 #include "stat-util.h"
66 #include "string-util.h"
68 #include "unit-name.h"
69 #include "unit-printf.h"
71 #include "user-util.h"
75 int config_parse_warn_compat(
80 unsigned section_line
,
86 Disabled reason
= ltype
;
89 case DISABLED_CONFIGURATION
:
90 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
91 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
94 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
95 "Support for option %s= has been removed and it is ignored", lvalue
);
97 case DISABLED_EXPERIMENTAL
:
98 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
99 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
106 DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode
, collect_mode
, CollectMode
, "Failed to parse garbage collection mode");
108 int config_parse_unit_deps(
110 const char *filename
,
113 unsigned section_line
,
120 UnitDependency d
= ltype
;
130 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
133 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_RETAIN_ESCAPE
);
139 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
143 r
= unit_name_printf(u
, word
, &k
);
145 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
149 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
151 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
157 int config_parse_obsolete_unit_deps(
159 const char *filename
,
162 unsigned section_line
,
169 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
170 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue
, unit_dependency_to_string(ltype
));
172 return config_parse_unit_deps(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, data
, userdata
);
175 int config_parse_unit_string_printf(
177 const char *filename
,
180 unsigned section_line
,
187 _cleanup_free_
char *k
= NULL
;
196 r
= unit_full_printf(u
, rvalue
, &k
);
198 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
202 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
205 int config_parse_unit_strv_printf(
207 const char *filename
,
210 unsigned section_line
,
218 _cleanup_free_
char *k
= NULL
;
226 r
= unit_full_printf(u
, rvalue
, &k
);
228 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
232 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
235 int config_parse_unit_path_printf(
237 const char *filename
,
240 unsigned section_line
,
247 _cleanup_free_
char *k
= NULL
;
257 r
= unit_full_printf(u
, rvalue
, &k
);
259 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
260 "Failed to resolve unit specifiers on %s%s: %m",
261 fatal
? "" : ", ignoring", rvalue
);
262 return fatal
? -ENOEXEC
: 0;
265 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
268 int config_parse_unit_path_strv_printf(
270 const char *filename
,
273 unsigned section_line
,
290 if (isempty(rvalue
)) {
296 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
298 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
304 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
305 "Invalid syntax, ignoring: %s", rvalue
);
309 r
= unit_full_printf(u
, word
, &k
);
311 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
312 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
316 if (!utf8_is_valid(k
)) {
317 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
321 if (!path_is_absolute(k
)) {
322 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
323 "Symlink path is not absolute: %s", k
);
327 path_kill_slashes(k
);
336 int config_parse_socket_listen(const char *unit
,
337 const char *filename
,
340 unsigned section_line
,
347 _cleanup_free_ SocketPort
*p
= NULL
;
359 if (isempty(rvalue
)) {
360 /* An empty assignment removes all ports */
361 socket_free_ports(s
);
365 p
= new0(SocketPort
, 1);
369 if (ltype
!= SOCKET_SOCKET
) {
372 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
374 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
378 path_kill_slashes(p
->path
);
380 } else if (streq(lvalue
, "ListenNetlink")) {
381 _cleanup_free_
char *k
= NULL
;
383 p
->type
= SOCKET_SOCKET
;
384 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
386 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
390 r
= socket_address_parse_netlink(&p
->address
, k
);
392 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
397 _cleanup_free_
char *k
= NULL
;
399 p
->type
= SOCKET_SOCKET
;
400 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
402 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
406 r
= socket_address_parse_and_warn(&p
->address
, k
);
408 if (r
!= -EAFNOSUPPORT
)
409 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
414 if (streq(lvalue
, "ListenStream"))
415 p
->address
.type
= SOCK_STREAM
;
416 else if (streq(lvalue
, "ListenDatagram"))
417 p
->address
.type
= SOCK_DGRAM
;
419 assert(streq(lvalue
, "ListenSequentialPacket"));
420 p
->address
.type
= SOCK_SEQPACKET
;
423 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
424 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
430 p
->auxiliary_fds
= NULL
;
431 p
->n_auxiliary_fds
= 0;
435 LIST_FIND_TAIL(port
, s
->ports
, tail
);
436 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
438 LIST_PREPEND(port
, s
->ports
, p
);
444 int config_parse_socket_protocol(const char *unit
,
445 const char *filename
,
448 unsigned section_line
,
463 if (streq(rvalue
, "udplite"))
464 s
->socket_protocol
= IPPROTO_UDPLITE
;
465 else if (streq(rvalue
, "sctp"))
466 s
->socket_protocol
= IPPROTO_SCTP
;
468 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
475 int config_parse_socket_bind(const char *unit
,
476 const char *filename
,
479 unsigned section_line
,
487 SocketAddressBindIPv6Only b
;
496 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
500 r
= parse_boolean(rvalue
);
502 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
506 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
508 s
->bind_ipv6_only
= b
;
513 int config_parse_exec_nice(
515 const char *filename
,
518 unsigned section_line
,
525 ExecContext
*c
= data
;
533 r
= parse_nice(rvalue
, &priority
);
536 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
538 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
549 int config_parse_exec_oom_score_adjust(const char* unit
,
550 const char *filename
,
553 unsigned section_line
,
560 ExecContext
*c
= data
;
568 r
= safe_atoi(rvalue
, &oa
);
570 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
574 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
575 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
579 c
->oom_score_adjust
= oa
;
580 c
->oom_score_adjust_set
= true;
585 int config_parse_exec(
587 const char *filename
,
590 unsigned section_line
,
597 ExecCommand
**e
= data
;
609 rvalue
+= strspn(rvalue
, WHITESPACE
);
611 if (isempty(rvalue
)) {
612 /* An empty assignment resets the list */
613 *e
= exec_command_free_list(*e
);
619 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
620 ExecCommandFlags flags
= 0;
621 bool ignore
= false, separate_argv0
= false;
622 _cleanup_free_ ExecCommand
*nce
= NULL
;
623 _cleanup_strv_free_
char **n
= NULL
;
624 size_t nlen
= 0, nbufsize
= 0;
629 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
635 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
636 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
637 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
638 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
639 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
640 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
641 * other sandboxing, with some special exceptions for changing UID.
643 * The idea is that '!!' may be used to write services that can take benefit of systemd's
644 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
645 * privilege dropping within the daemon if the kernel does not offer that. */
647 if (*f
== '-' && !(flags
& EXEC_COMMAND_IGNORE_FAILURE
)) {
648 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
650 } else if (*f
== '@' && !separate_argv0
)
651 separate_argv0
= true;
652 else if (*f
== '+' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
653 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
654 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
655 flags
|= EXEC_COMMAND_NO_SETUID
;
656 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
657 flags
&= ~EXEC_COMMAND_NO_SETUID
;
658 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
664 r
= unit_full_printf(u
, f
, &path
);
666 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
667 "Failed to resolve unit specifiers on %s%s: %m",
668 f
, ignore
? ", ignoring" : "");
669 return ignore
? 0 : -ENOEXEC
;
673 /* First word is either "-" or "@" with no command. */
674 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
675 "Empty path in command line%s: \"%s\"",
676 ignore
? ", ignoring" : "", rvalue
);
677 return ignore
? 0 : -ENOEXEC
;
679 if (!string_is_safe(path
)) {
680 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
681 "Executable path contains special characters%s: %s",
682 ignore
? ", ignoring" : "", rvalue
);
683 return ignore
? 0 : -ENOEXEC
;
685 if (!path_is_absolute(path
)) {
686 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
687 "Executable path is not absolute%s: %s",
688 ignore
? ", ignoring" : "", rvalue
);
689 return ignore
? 0 : -ENOEXEC
;
691 if (endswith(path
, "/")) {
692 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
693 "Executable path specifies a directory%s: %s",
694 ignore
? ", ignoring" : "", rvalue
);
695 return ignore
? 0 : -ENOEXEC
;
698 if (!separate_argv0
) {
701 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
711 path_kill_slashes(path
);
713 while (!isempty(p
)) {
714 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
716 /* Check explicitly for an unquoted semicolon as
717 * command separator token. */
718 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
720 p
+= strspn(p
, WHITESPACE
);
725 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
726 * extract_first_word() would return the same for all of those. */
727 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
731 p
+= strspn(p
, WHITESPACE
);
733 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
744 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
748 return ignore
? 0 : -ENOEXEC
;
750 r
= unit_full_printf(u
, word
, &resolved
);
752 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
753 "Failed to resolve unit specifiers on %s%s: %m",
754 word
, ignore
? ", ignoring" : "");
755 return ignore
? 0 : -ENOEXEC
;
758 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
760 n
[nlen
++] = resolved
;
766 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
767 "Empty executable name or zeroeth argument%s: %s",
768 ignore
? ", ignoring" : "", rvalue
);
769 return ignore
? 0 : -ENOEXEC
;
772 nce
= new0(ExecCommand
, 1);
780 exec_command_append_list(e
, nce
);
782 /* Do not _cleanup_free_ these. */
793 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
794 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
796 int config_parse_socket_bindtodevice(
798 const char *filename
,
801 unsigned section_line
,
816 if (rvalue
[0] && !streq(rvalue
, "*")) {
817 if (!ifname_valid(rvalue
)) {
818 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
828 free(s
->bind_to_device
);
829 s
->bind_to_device
= n
;
834 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input literal specifier");
835 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output literal specifier");
837 int config_parse_exec_input(const char *unit
,
838 const char *filename
,
841 unsigned section_line
,
847 ExecContext
*c
= data
;
856 name
= startswith(rvalue
, "fd:");
858 /* Strip prefix and validate fd name */
859 if (!fdname_is_valid(name
)) {
860 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
863 c
->std_input
= EXEC_INPUT_NAMED_FD
;
864 r
= free_and_strdup(&c
->stdio_fdname
[STDIN_FILENO
], name
);
869 ExecInput ei
= exec_input_from_string(rvalue
);
870 if (ei
== _EXEC_INPUT_INVALID
)
871 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
878 int config_parse_exec_output(const char *unit
,
879 const char *filename
,
882 unsigned section_line
,
888 ExecContext
*c
= data
;
899 name
= startswith(rvalue
, "fd:");
901 /* Strip prefix and validate fd name */
902 if (!fdname_is_valid(name
)) {
903 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
906 eo
= EXEC_OUTPUT_NAMED_FD
;
908 eo
= exec_output_from_string(rvalue
);
909 if (eo
== _EXEC_OUTPUT_INVALID
) {
910 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
915 if (streq(lvalue
, "StandardOutput")) {
917 r
= free_and_strdup(&c
->stdio_fdname
[STDOUT_FILENO
], name
);
921 } else if (streq(lvalue
, "StandardError")) {
923 r
= free_and_strdup(&c
->stdio_fdname
[STDERR_FILENO
], name
);
928 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output property, ignoring: %s", lvalue
);
933 int config_parse_exec_io_class(const char *unit
,
934 const char *filename
,
937 unsigned section_line
,
944 ExecContext
*c
= data
;
952 x
= ioprio_class_from_string(rvalue
);
954 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
958 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
959 c
->ioprio_set
= true;
964 int config_parse_exec_io_priority(const char *unit
,
965 const char *filename
,
968 unsigned section_line
,
975 ExecContext
*c
= data
;
983 r
= ioprio_parse_priority(rvalue
, &i
);
985 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
989 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
990 c
->ioprio_set
= true;
995 int config_parse_exec_cpu_sched_policy(const char *unit
,
996 const char *filename
,
999 unsigned section_line
,
1007 ExecContext
*c
= data
;
1015 x
= sched_policy_from_string(rvalue
);
1017 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1021 c
->cpu_sched_policy
= x
;
1022 /* Moving to or from real-time policy? We need to adjust the priority */
1023 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
1024 c
->cpu_sched_set
= true;
1029 int config_parse_exec_cpu_sched_prio(const char *unit
,
1030 const char *filename
,
1032 const char *section
,
1033 unsigned section_line
,
1040 ExecContext
*c
= data
;
1048 r
= safe_atoi(rvalue
, &i
);
1050 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1054 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1055 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1056 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1058 if (i
< min
|| i
> max
) {
1059 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1063 c
->cpu_sched_priority
= i
;
1064 c
->cpu_sched_set
= true;
1069 int config_parse_exec_cpu_affinity(const char *unit
,
1070 const char *filename
,
1072 const char *section
,
1073 unsigned section_line
,
1080 ExecContext
*c
= data
;
1081 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1089 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1094 CPU_FREE(c
->cpuset
);
1097 /* An empty assignment resets the CPU list */
1103 c
->cpuset_ncpus
= ncpus
;
1108 int config_parse_exec_secure_bits(const char *unit
,
1109 const char *filename
,
1111 const char *section
,
1112 unsigned section_line
,
1119 ExecContext
*c
= data
;
1127 if (isempty(rvalue
)) {
1128 /* An empty assignment resets the field */
1133 r
= secure_bits_from_string(rvalue
);
1137 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1138 "Invalid syntax, ignoring: %s", rvalue
);
1147 int config_parse_capability_set(
1149 const char *filename
,
1151 const char *section
,
1152 unsigned section_line
,
1159 uint64_t *capability_set
= data
;
1160 uint64_t sum
= 0, initial
= 0;
1161 bool invert
= false;
1169 if (rvalue
[0] == '~') {
1174 if (streq(lvalue
, "CapabilityBoundingSet"))
1175 initial
= CAP_ALL
; /* initialized to all bits on */
1176 /* else "AmbientCapabilities" initialized to all bits off */
1178 r
= capability_set_from_string(rvalue
, &sum
);
1182 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word: %s", rvalue
);
1186 if (sum
== 0 || *capability_set
== initial
)
1187 /* "", "~" or uninitialized data -> replace */
1188 *capability_set
= invert
? ~sum
: sum
;
1190 /* previous data -> merge */
1192 *capability_set
&= ~sum
;
1194 *capability_set
|= sum
;
1200 int config_parse_limit(
1202 const char *filename
,
1204 const char *section
,
1205 unsigned section_line
,
1212 struct rlimit
**rl
= data
, d
= {};
1220 r
= rlimit_parse(ltype
, rvalue
, &d
);
1222 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1226 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1233 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1241 #if HAVE_SYSV_COMPAT
1242 int config_parse_sysv_priority(const char *unit
,
1243 const char *filename
,
1245 const char *section
,
1246 unsigned section_line
,
1253 int *priority
= data
;
1261 r
= safe_atoi(rvalue
, &i
);
1262 if (r
< 0 || i
< 0) {
1263 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1267 *priority
= (int) i
;
1272 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1273 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1275 int config_parse_exec_mount_flags(
1277 const char *filename
,
1279 const char *section
,
1280 unsigned section_line
,
1288 ExecContext
*c
= data
;
1296 r
= mount_propagation_flags_from_string(rvalue
, &c
->mount_flags
);
1298 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1303 int config_parse_exec_selinux_context(
1305 const char *filename
,
1307 const char *section
,
1308 unsigned section_line
,
1315 ExecContext
*c
= data
;
1326 if (isempty(rvalue
)) {
1327 c
->selinux_context
= mfree(c
->selinux_context
);
1328 c
->selinux_context_ignore
= false;
1332 if (rvalue
[0] == '-') {
1338 r
= unit_full_printf(u
, rvalue
, &k
);
1340 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1341 "Failed to resolve specifiers%s: %m",
1342 ignore
? ", ignoring" : "");
1343 return ignore
? 0 : -ENOEXEC
;
1346 free(c
->selinux_context
);
1347 c
->selinux_context
= k
;
1348 c
->selinux_context_ignore
= ignore
;
1353 int config_parse_exec_apparmor_profile(
1355 const char *filename
,
1357 const char *section
,
1358 unsigned section_line
,
1365 ExecContext
*c
= data
;
1376 if (isempty(rvalue
)) {
1377 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1378 c
->apparmor_profile_ignore
= false;
1382 if (rvalue
[0] == '-') {
1388 r
= unit_full_printf(u
, rvalue
, &k
);
1390 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1391 "Failed to resolve specifiers%s: %m",
1392 ignore
? ", ignoring" : "");
1393 return ignore
? 0 : -ENOEXEC
;
1396 free(c
->apparmor_profile
);
1397 c
->apparmor_profile
= k
;
1398 c
->apparmor_profile_ignore
= ignore
;
1403 int config_parse_exec_smack_process_label(
1405 const char *filename
,
1407 const char *section
,
1408 unsigned section_line
,
1415 ExecContext
*c
= data
;
1426 if (isempty(rvalue
)) {
1427 c
->smack_process_label
= mfree(c
->smack_process_label
);
1428 c
->smack_process_label_ignore
= false;
1432 if (rvalue
[0] == '-') {
1438 r
= unit_full_printf(u
, rvalue
, &k
);
1440 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1441 "Failed to resolve specifiers%s: %m",
1442 ignore
? ", ignoring" : "");
1443 return ignore
? 0 : -ENOEXEC
;
1446 free(c
->smack_process_label
);
1447 c
->smack_process_label
= k
;
1448 c
->smack_process_label_ignore
= ignore
;
1453 int config_parse_timer(const char *unit
,
1454 const char *filename
,
1456 const char *section
,
1457 unsigned section_line
,
1468 CalendarSpec
*c
= NULL
;
1470 _cleanup_free_
char *k
= NULL
;
1478 if (isempty(rvalue
)) {
1479 /* Empty assignment resets list */
1480 timer_free_values(t
);
1484 b
= timer_base_from_string(lvalue
);
1486 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1490 r
= unit_full_printf(u
, rvalue
, &k
);
1492 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1496 if (b
== TIMER_CALENDAR
) {
1497 if (calendar_spec_from_string(k
, &c
) < 0) {
1498 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1502 if (parse_sec(k
, &usec
) < 0) {
1503 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1508 v
= new0(TimerValue
, 1);
1510 calendar_spec_free(c
);
1516 v
->calendar_spec
= c
;
1518 LIST_PREPEND(value
, t
->values
, v
);
1523 int config_parse_trigger_unit(
1525 const char *filename
,
1527 const char *section
,
1528 unsigned section_line
,
1535 _cleanup_free_
char *p
= NULL
;
1545 if (!hashmap_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1546 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1550 r
= unit_name_printf(u
, rvalue
, &p
);
1552 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1556 type
= unit_name_to_type(p
);
1558 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1562 if (type
== u
->type
) {
1563 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1567 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1569 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1576 int config_parse_path_spec(const char *unit
,
1577 const char *filename
,
1579 const char *section
,
1580 unsigned section_line
,
1590 _cleanup_free_
char *k
= NULL
;
1598 if (isempty(rvalue
)) {
1599 /* Empty assignment clears list */
1604 b
= path_type_from_string(lvalue
);
1606 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1610 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1612 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1616 if (!path_is_absolute(k
)) {
1617 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1621 s
= new0(PathSpec
, 1);
1626 s
->path
= path_kill_slashes(k
);
1631 LIST_PREPEND(spec
, p
->specs
, s
);
1636 int config_parse_socket_service(
1638 const char *filename
,
1640 const char *section
,
1641 unsigned section_line
,
1648 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1649 _cleanup_free_
char *p
= NULL
;
1659 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1661 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", rvalue
);
1665 if (!endswith(p
, ".service")) {
1666 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service: %s", rvalue
);
1670 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1672 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s: %s", rvalue
, bus_error_message(&error
, r
));
1676 unit_ref_set(&s
->service
, x
);
1681 int config_parse_fdname(
1683 const char *filename
,
1685 const char *section
,
1686 unsigned section_line
,
1693 _cleanup_free_
char *p
= NULL
;
1702 if (isempty(rvalue
)) {
1703 s
->fdname
= mfree(s
->fdname
);
1707 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1709 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1713 if (!fdname_is_valid(p
)) {
1714 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1718 return free_and_replace(s
->fdname
, p
);
1721 int config_parse_service_sockets(
1723 const char *filename
,
1725 const char *section
,
1726 unsigned section_line
,
1744 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1746 r
= extract_first_word(&p
, &word
, NULL
, 0);
1752 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1756 r
= unit_name_printf(UNIT(s
), word
, &k
);
1758 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1762 if (!endswith(k
, ".socket")) {
1763 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1767 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1769 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1771 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1773 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1779 int config_parse_bus_name(
1781 const char *filename
,
1783 const char *section
,
1784 unsigned section_line
,
1791 _cleanup_free_
char *k
= NULL
;
1800 r
= unit_full_printf(u
, rvalue
, &k
);
1802 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1806 if (!service_name_is_valid(k
)) {
1807 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1811 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1814 int config_parse_service_timeout(
1816 const char *filename
,
1818 const char *section
,
1819 unsigned section_line
,
1826 Service
*s
= userdata
;
1835 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1837 r
= parse_sec(rvalue
, &usec
);
1839 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1843 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1844 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1845 * all other timeouts. */
1847 usec
= USEC_INFINITY
;
1849 if (!streq(lvalue
, "TimeoutStopSec")) {
1850 s
->start_timeout_defined
= true;
1851 s
->timeout_start_usec
= usec
;
1854 if (!streq(lvalue
, "TimeoutStartSec"))
1855 s
->timeout_stop_usec
= usec
;
1860 int config_parse_sec_fix_0(
1862 const char *filename
,
1864 const char *section
,
1865 unsigned section_line
,
1872 usec_t
*usec
= data
;
1880 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1881 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1884 r
= parse_sec_fix_0(rvalue
, usec
);
1886 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1893 int config_parse_user_group(
1895 const char *filename
,
1897 const char *section
,
1898 unsigned section_line
,
1905 char **user
= data
, *n
;
1914 if (isempty(rvalue
))
1917 _cleanup_free_
char *k
= NULL
;
1919 r
= unit_full_printf(u
, rvalue
, &k
);
1921 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
1925 if (!valid_user_group_name_or_id(k
)) {
1926 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1940 int config_parse_user_group_strv(
1942 const char *filename
,
1944 const char *section
,
1945 unsigned section_line
,
1952 char ***users
= data
;
1962 if (isempty(rvalue
)) {
1963 *users
= strv_free(*users
);
1969 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1971 r
= extract_first_word(&p
, &word
, NULL
, 0);
1977 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax: %s", rvalue
);
1981 r
= unit_full_printf(u
, word
, &k
);
1983 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", word
);
1987 if (!valid_user_group_name_or_id(k
)) {
1988 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1992 r
= strv_push(users
, k
);
2002 int config_parse_working_directory(
2004 const char *filename
,
2006 const char *section
,
2007 unsigned section_line
,
2014 ExecContext
*c
= data
;
2025 if (rvalue
[0] == '-') {
2031 if (streq(rvalue
, "~")) {
2032 c
->working_directory_home
= true;
2033 c
->working_directory
= mfree(c
->working_directory
);
2035 _cleanup_free_
char *k
= NULL
;
2037 r
= unit_full_printf(u
, rvalue
, &k
);
2039 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2040 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2041 rvalue
, missing_ok
? ", ignoring" : "");
2042 return missing_ok
? 0 : -ENOEXEC
;
2045 path_kill_slashes(k
);
2047 if (!utf8_is_valid(k
)) {
2048 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2049 return missing_ok
? 0 : -ENOEXEC
;
2052 if (!path_is_absolute(k
)) {
2053 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2054 "Working directory path '%s' is not absolute%s.",
2055 rvalue
, missing_ok
? ", ignoring" : "");
2056 return missing_ok
? 0 : -ENOEXEC
;
2059 c
->working_directory_home
= false;
2060 free_and_replace(c
->working_directory
, k
);
2063 c
->working_directory_missing_ok
= missing_ok
;
2067 int config_parse_unit_env_file(const char *unit
,
2068 const char *filename
,
2070 const char *section
,
2071 unsigned section_line
,
2080 _cleanup_free_
char *n
= NULL
;
2088 if (isempty(rvalue
)) {
2089 /* Empty assignment frees the list */
2090 *env
= strv_free(*env
);
2094 r
= unit_full_printf(u
, rvalue
, &n
);
2096 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2100 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2101 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2105 r
= strv_extend(env
, n
);
2112 int config_parse_environ(
2114 const char *filename
,
2116 const char *section
,
2117 unsigned section_line
,
2134 if (isempty(rvalue
)) {
2135 /* Empty assignment resets the list */
2136 *env
= strv_free(*env
);
2140 for (p
= rvalue
;; ) {
2141 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2143 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2149 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2150 "Invalid syntax, ignoring: %s", rvalue
);
2155 r
= unit_full_printf(u
, word
, &k
);
2157 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2158 "Failed to resolve specifiers, ignoring: %s", word
);
2166 if (!env_assignment_is_valid(k
)) {
2167 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2168 "Invalid environment assignment, ignoring: %s", k
);
2172 r
= strv_env_replace(env
, k
);
2180 int config_parse_pass_environ(
2182 const char *filename
,
2184 const char *section
,
2185 unsigned section_line
,
2192 const char *whole_rvalue
= rvalue
;
2193 _cleanup_strv_free_
char **n
= NULL
;
2194 size_t nlen
= 0, nbufsize
= 0;
2195 char*** passenv
= data
;
2204 if (isempty(rvalue
)) {
2205 /* Empty assignment resets the list */
2206 *passenv
= strv_free(*passenv
);
2211 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2213 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2219 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2220 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2225 r
= unit_full_printf(u
, word
, &k
);
2227 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2228 "Failed to resolve specifiers, ignoring: %s", word
);
2236 if (!env_name_is_valid(k
)) {
2237 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2238 "Invalid environment name for %s, ignoring: %s", lvalue
, k
);
2242 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2251 r
= strv_extend_strv(passenv
, n
, true);
2259 int config_parse_unset_environ(
2261 const char *filename
,
2263 const char *section
,
2264 unsigned section_line
,
2271 _cleanup_strv_free_
char **n
= NULL
;
2272 const char *whole_rvalue
= rvalue
;
2273 size_t nlen
= 0, nbufsize
= 0;
2274 char*** unsetenv
= data
;
2283 if (isempty(rvalue
)) {
2284 /* Empty assignment resets the list */
2285 *unsetenv
= strv_free(*unsetenv
);
2290 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2292 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2298 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2299 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2304 r
= unit_full_printf(u
, word
, &k
);
2306 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2307 "Failed to resolve specifiers, ignoring: %s", word
);
2315 if (!env_assignment_is_valid(k
) && !env_name_is_valid(k
)) {
2316 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2317 "Invalid environment name or assignment %s, ignoring: %s", lvalue
, k
);
2321 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2330 r
= strv_extend_strv(unsetenv
, n
, true);
2338 int config_parse_log_extra_fields(
2340 const char *filename
,
2342 const char *section
,
2343 unsigned section_line
,
2350 ExecContext
*c
= data
;
2360 if (isempty(rvalue
)) {
2361 exec_context_free_log_extra_fields(c
);
2365 for (p
= rvalue
;; ) {
2366 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2370 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2376 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2380 r
= unit_full_printf(u
, word
, &k
);
2382 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring field: %m", word
);
2386 eq
= strchr(k
, '=');
2388 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field lacks '=' character, ignoring field: %s", k
);
2392 if (!journal_field_valid(k
, eq
-k
, false)) {
2393 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field name is invalid, ignoring field: %s", k
);
2397 t
= realloc_multiply(c
->log_extra_fields
, sizeof(struct iovec
), c
->n_log_extra_fields
+1);
2401 c
->log_extra_fields
= t
;
2402 c
->log_extra_fields
[c
->n_log_extra_fields
++] = IOVEC_MAKE_STRING(k
);
2410 int config_parse_ip_tos(const char *unit
,
2411 const char *filename
,
2413 const char *section
,
2414 unsigned section_line
,
2421 int *ip_tos
= data
, x
;
2428 x
= ip_tos_from_string(rvalue
);
2430 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2438 int config_parse_unit_condition_path(
2440 const char *filename
,
2442 const char *section
,
2443 unsigned section_line
,
2450 _cleanup_free_
char *p
= NULL
;
2451 Condition
**list
= data
, *c
;
2452 ConditionType t
= ltype
;
2453 bool trigger
, negate
;
2462 if (isempty(rvalue
)) {
2463 /* Empty assignment resets the list */
2464 *list
= condition_free_list(*list
);
2468 trigger
= rvalue
[0] == '|';
2472 negate
= rvalue
[0] == '!';
2476 r
= unit_full_printf(u
, rvalue
, &p
);
2478 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2482 if (!path_is_absolute(p
)) {
2483 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2487 c
= condition_new(t
, p
, trigger
, negate
);
2491 LIST_PREPEND(conditions
, *list
, c
);
2495 int config_parse_unit_condition_string(
2497 const char *filename
,
2499 const char *section
,
2500 unsigned section_line
,
2507 _cleanup_free_
char *s
= NULL
;
2508 Condition
**list
= data
, *c
;
2509 ConditionType t
= ltype
;
2510 bool trigger
, negate
;
2519 if (isempty(rvalue
)) {
2520 /* Empty assignment resets the list */
2521 *list
= condition_free_list(*list
);
2525 trigger
= rvalue
[0] == '|';
2529 negate
= rvalue
[0] == '!';
2533 r
= unit_full_printf(u
, rvalue
, &s
);
2535 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2539 c
= condition_new(t
, s
, trigger
, negate
);
2543 LIST_PREPEND(conditions
, *list
, c
);
2547 int config_parse_unit_condition_null(
2549 const char *filename
,
2551 const char *section
,
2552 unsigned section_line
,
2559 Condition
**list
= data
, *c
;
2560 bool trigger
, negate
;
2568 if (isempty(rvalue
)) {
2569 /* Empty assignment resets the list */
2570 *list
= condition_free_list(*list
);
2574 trigger
= rvalue
[0] == '|';
2578 negate
= rvalue
[0] == '!';
2582 b
= parse_boolean(rvalue
);
2584 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2591 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2595 LIST_PREPEND(conditions
, *list
, c
);
2599 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2600 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2602 int config_parse_unit_requires_mounts_for(
2604 const char *filename
,
2606 const char *section
,
2607 unsigned section_line
,
2623 for (p
= rvalue
;; ) {
2624 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2626 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2632 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2633 "Invalid syntax, ignoring: %s", rvalue
);
2637 if (!utf8_is_valid(word
)) {
2638 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2642 r
= unit_full_printf(u
, word
, &resolved
);
2644 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit name \"%s\", ignoring: %m", word
);
2648 r
= unit_require_mounts_for(u
, resolved
, UNIT_DEPENDENCY_FILE
);
2650 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", resolved
);
2656 int config_parse_documentation(const char *unit
,
2657 const char *filename
,
2659 const char *section
,
2660 unsigned section_line
,
2676 if (isempty(rvalue
)) {
2677 /* Empty assignment resets the list */
2678 u
->documentation
= strv_free(u
->documentation
);
2682 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2683 rvalue
, data
, userdata
);
2687 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2689 if (documentation_url_is_valid(*a
))
2692 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2704 static int syscall_filter_parse_one(
2706 const char *filename
,
2716 const SyscallFilterSet
*set
;
2719 set
= syscall_filter_set_find(t
);
2722 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Don't know system call group, ignoring: %s", t
);
2726 NULSTR_FOREACH(i
, set
->value
) {
2727 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, i
, false, errno_num
);
2734 id
= seccomp_syscall_resolve_name(t
);
2735 if (id
== __NR_SCMP_ERROR
) {
2737 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2741 /* If we previously wanted to forbid a syscall and now
2742 * we want to allow it, then remove it from the list
2744 if (!invert
== c
->syscall_whitelist
) {
2745 r
= hashmap_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1), INT_TO_PTR(errno_num
));
2751 (void) hashmap_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2757 int config_parse_syscall_filter(
2759 const char *filename
,
2761 const char *section
,
2762 unsigned section_line
,
2769 ExecContext
*c
= data
;
2771 bool invert
= false;
2780 if (isempty(rvalue
)) {
2781 /* Empty assignment resets the list */
2782 c
->syscall_filter
= hashmap_free(c
->syscall_filter
);
2783 c
->syscall_whitelist
= false;
2787 if (rvalue
[0] == '~') {
2792 if (!c
->syscall_filter
) {
2793 c
->syscall_filter
= hashmap_new(NULL
);
2794 if (!c
->syscall_filter
)
2798 /* Allow everything but the ones listed */
2799 c
->syscall_whitelist
= false;
2801 /* Allow nothing but the ones listed */
2802 c
->syscall_whitelist
= true;
2804 /* Accept default syscalls if we are on a whitelist */
2805 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, false, "@default", false, -1);
2813 _cleanup_free_
char *word
= NULL
, *name
= NULL
;
2816 r
= extract_first_word(&p
, &word
, NULL
, 0);
2822 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2826 r
= parse_syscall_and_errno(word
, &name
, &num
);
2828 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse syscall:errno, ignoring: %s", word
);
2832 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, name
, true, num
);
2840 int config_parse_syscall_archs(
2842 const char *filename
,
2844 const char *section
,
2845 unsigned section_line
,
2856 if (isempty(rvalue
)) {
2857 *archs
= set_free(*archs
);
2861 r
= set_ensure_allocated(archs
, NULL
);
2865 for (p
= rvalue
;;) {
2866 _cleanup_free_
char *word
= NULL
;
2869 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2875 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2876 "Invalid syntax, ignoring: %s", rvalue
);
2880 r
= seccomp_arch_from_string(word
, &a
);
2882 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2883 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
2887 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2893 int config_parse_syscall_errno(
2895 const char *filename
,
2897 const char *section
,
2898 unsigned section_line
,
2905 ExecContext
*c
= data
;
2912 if (isempty(rvalue
)) {
2913 /* Empty assignment resets to KILL */
2914 c
->syscall_errno
= 0;
2918 e
= parse_errno(rvalue
);
2920 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2924 c
->syscall_errno
= e
;
2928 int config_parse_address_families(
2930 const char *filename
,
2932 const char *section
,
2933 unsigned section_line
,
2940 ExecContext
*c
= data
;
2941 bool invert
= false;
2949 if (isempty(rvalue
)) {
2950 /* Empty assignment resets the list */
2951 c
->address_families
= set_free(c
->address_families
);
2952 c
->address_families_whitelist
= false;
2956 if (rvalue
[0] == '~') {
2961 if (!c
->address_families
) {
2962 c
->address_families
= set_new(NULL
);
2963 if (!c
->address_families
)
2966 c
->address_families_whitelist
= !invert
;
2969 for (p
= rvalue
;;) {
2970 _cleanup_free_
char *word
= NULL
;
2973 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2979 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2980 "Invalid syntax, ignoring: %s", rvalue
);
2984 af
= af_from_name(word
);
2986 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2987 "Failed to parse address family \"%s\", ignoring: %m", word
);
2991 /* If we previously wanted to forbid an address family and now
2992 * we want to allow it, then just remove it from the list.
2994 if (!invert
== c
->address_families_whitelist
) {
2995 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2999 set_remove(c
->address_families
, INT_TO_PTR(af
));
3003 int config_parse_restrict_namespaces(
3005 const char *filename
,
3007 const char *section
,
3008 unsigned section_line
,
3015 ExecContext
*c
= data
;
3016 bool invert
= false;
3019 if (isempty(rvalue
)) {
3020 /* Reset to the default. */
3021 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3025 if (rvalue
[0] == '~') {
3030 r
= parse_boolean(rvalue
);
3032 c
->restrict_namespaces
= 0;
3034 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3036 /* Not a boolean argument, in this case it's a list of namespace types. */
3038 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
3040 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
3046 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
3052 int config_parse_unit_slice(
3054 const char *filename
,
3056 const char *section
,
3057 unsigned section_line
,
3064 _cleanup_free_
char *k
= NULL
;
3065 Unit
*u
= userdata
, *slice
= NULL
;
3073 r
= unit_name_printf(u
, rvalue
, &k
);
3075 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
3079 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
3081 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
3085 r
= unit_set_slice(u
, slice
);
3087 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
3094 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
3096 int config_parse_cpu_weight(
3098 const char *filename
,
3100 const char *section
,
3101 unsigned section_line
,
3108 uint64_t *weight
= data
;
3115 r
= cg_weight_parse(rvalue
, weight
);
3117 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
3124 int config_parse_cpu_shares(
3126 const char *filename
,
3128 const char *section
,
3129 unsigned section_line
,
3136 uint64_t *shares
= data
;
3143 r
= cg_cpu_shares_parse(rvalue
, shares
);
3145 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
3152 int config_parse_cpu_quota(
3154 const char *filename
,
3156 const char *section
,
3157 unsigned section_line
,
3164 CGroupContext
*c
= data
;
3171 if (isempty(rvalue
)) {
3172 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3176 r
= parse_percent_unbounded(rvalue
);
3178 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3182 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3186 int config_parse_memory_limit(
3188 const char *filename
,
3190 const char *section
,
3191 unsigned section_line
,
3198 CGroupContext
*c
= data
;
3199 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3202 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3204 r
= parse_percent(rvalue
);
3206 r
= parse_size(rvalue
, 1024, &bytes
);
3208 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3212 bytes
= physical_memory_scale(r
, 100U);
3214 if (bytes
<= 0 || bytes
>= UINT64_MAX
) {
3215 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3220 if (streq(lvalue
, "MemoryLow"))
3221 c
->memory_low
= bytes
;
3222 else if (streq(lvalue
, "MemoryHigh"))
3223 c
->memory_high
= bytes
;
3224 else if (streq(lvalue
, "MemoryMax"))
3225 c
->memory_max
= bytes
;
3226 else if (streq(lvalue
, "MemorySwapMax"))
3227 c
->memory_swap_max
= bytes
;
3228 else if (streq(lvalue
, "MemoryLimit"))
3229 c
->memory_limit
= bytes
;
3236 int config_parse_tasks_max(
3238 const char *filename
,
3240 const char *section
,
3241 unsigned section_line
,
3248 uint64_t *tasks_max
= data
, v
;
3252 if (isempty(rvalue
)) {
3253 *tasks_max
= u
->manager
->default_tasks_max
;
3257 if (streq(rvalue
, "infinity")) {
3258 *tasks_max
= CGROUP_LIMIT_MAX
;
3262 r
= parse_percent(rvalue
);
3264 r
= safe_atou64(rvalue
, &v
);
3266 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3270 v
= system_tasks_max_scale(r
, 100U);
3272 if (v
<= 0 || v
>= UINT64_MAX
) {
3273 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3281 int config_parse_delegate(
3283 const char *filename
,
3285 const char *section
,
3286 unsigned section_line
,
3293 CGroupContext
*c
= data
;
3296 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3297 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3298 * mask to delegate. */
3300 if (isempty(rvalue
)) {
3302 c
->delegate_controllers
= 0;
3306 r
= parse_boolean(rvalue
);
3308 const char *p
= rvalue
;
3309 CGroupMask mask
= 0;
3312 _cleanup_free_
char *word
= NULL
;
3313 CGroupController cc
;
3315 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3321 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
3325 cc
= cgroup_controller_from_string(word
);
3327 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid controller name '%s', ignoring", rvalue
);
3331 mask
|= CGROUP_CONTROLLER_TO_MASK(cc
);
3335 c
->delegate_controllers
|= mask
;
3339 c
->delegate_controllers
= _CGROUP_MASK_ALL
;
3341 c
->delegate
= false;
3342 c
->delegate_controllers
= 0;
3348 int config_parse_device_allow(
3350 const char *filename
,
3352 const char *section
,
3353 unsigned section_line
,
3360 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3361 CGroupContext
*c
= data
;
3362 CGroupDeviceAllow
*a
;
3363 const char *m
= NULL
;
3367 if (isempty(rvalue
)) {
3368 while (c
->device_allow
)
3369 cgroup_context_free_device_allow(c
, c
->device_allow
);
3374 r
= unit_full_printf(userdata
, rvalue
, &t
);
3376 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3377 "Failed to resolve specifiers in %s, ignoring: %m",
3381 n
= strcspn(t
, WHITESPACE
);
3383 path
= strndup(t
, n
);
3387 if (!is_deviceallow_pattern(path
)) {
3388 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3392 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3396 if (!in_charset(m
, "rwm")) {
3397 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3401 a
= new0(CGroupDeviceAllow
, 1);
3407 a
->r
= !!strchr(m
, 'r');
3408 a
->w
= !!strchr(m
, 'w');
3409 a
->m
= !!strchr(m
, 'm');
3411 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3415 int config_parse_io_weight(
3417 const char *filename
,
3419 const char *section
,
3420 unsigned section_line
,
3427 uint64_t *weight
= data
;
3434 r
= cg_weight_parse(rvalue
, weight
);
3436 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3443 int config_parse_io_device_weight(
3445 const char *filename
,
3447 const char *section
,
3448 unsigned section_line
,
3455 _cleanup_free_
char *path
= NULL
;
3456 CGroupIODeviceWeight
*w
;
3457 CGroupContext
*c
= data
;
3467 if (isempty(rvalue
)) {
3468 while (c
->io_device_weights
)
3469 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3474 n
= strcspn(rvalue
, WHITESPACE
);
3475 weight
= rvalue
+ n
;
3476 weight
+= strspn(weight
, WHITESPACE
);
3478 if (isempty(weight
)) {
3479 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3483 path
= strndup(rvalue
, n
);
3487 if (!path_startswith(path
, "/dev")) {
3488 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3492 r
= cg_weight_parse(weight
, &u
);
3494 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3498 assert(u
!= CGROUP_WEIGHT_INVALID
);
3500 w
= new0(CGroupIODeviceWeight
, 1);
3509 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3513 int config_parse_io_limit(
3515 const char *filename
,
3517 const char *section
,
3518 unsigned section_line
,
3525 _cleanup_free_
char *path
= NULL
;
3526 CGroupIODeviceLimit
*l
= NULL
, *t
;
3527 CGroupContext
*c
= data
;
3528 CGroupIOLimitType type
;
3538 type
= cgroup_io_limit_type_from_string(lvalue
);
3541 if (isempty(rvalue
)) {
3542 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3543 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3547 n
= strcspn(rvalue
, WHITESPACE
);
3549 limit
+= strspn(limit
, WHITESPACE
);
3552 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3556 path
= strndup(rvalue
, n
);
3560 if (!path_startswith(path
, "/dev")) {
3561 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3565 if (streq("infinity", limit
)) {
3566 num
= CGROUP_LIMIT_MAX
;
3568 r
= parse_size(limit
, 1000, &num
);
3569 if (r
< 0 || num
<= 0) {
3570 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3575 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3576 if (path_equal(path
, t
->path
)) {
3583 CGroupIOLimitType ttype
;
3585 l
= new0(CGroupIODeviceLimit
, 1);
3591 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3592 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3594 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3597 l
->limits
[type
] = num
;
3602 int config_parse_blockio_weight(
3604 const char *filename
,
3606 const char *section
,
3607 unsigned section_line
,
3614 uint64_t *weight
= data
;
3621 r
= cg_blkio_weight_parse(rvalue
, weight
);
3623 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3630 int config_parse_blockio_device_weight(
3632 const char *filename
,
3634 const char *section
,
3635 unsigned section_line
,
3642 _cleanup_free_
char *path
= NULL
;
3643 CGroupBlockIODeviceWeight
*w
;
3644 CGroupContext
*c
= data
;
3654 if (isempty(rvalue
)) {
3655 while (c
->blockio_device_weights
)
3656 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3661 n
= strcspn(rvalue
, WHITESPACE
);
3662 weight
= rvalue
+ n
;
3663 weight
+= strspn(weight
, WHITESPACE
);
3665 if (isempty(weight
)) {
3666 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3670 path
= strndup(rvalue
, n
);
3674 if (!path_startswith(path
, "/dev")) {
3675 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3679 r
= cg_blkio_weight_parse(weight
, &u
);
3681 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3685 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3687 w
= new0(CGroupBlockIODeviceWeight
, 1);
3696 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3700 int config_parse_blockio_bandwidth(
3702 const char *filename
,
3704 const char *section
,
3705 unsigned section_line
,
3712 _cleanup_free_
char *path
= NULL
;
3713 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3714 CGroupContext
*c
= data
;
3715 const char *bandwidth
;
3725 read
= streq("BlockIOReadBandwidth", lvalue
);
3727 if (isempty(rvalue
)) {
3728 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3729 b
->rbps
= CGROUP_LIMIT_MAX
;
3730 b
->wbps
= CGROUP_LIMIT_MAX
;
3735 n
= strcspn(rvalue
, WHITESPACE
);
3736 bandwidth
= rvalue
+ n
;
3737 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3740 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3744 path
= strndup(rvalue
, n
);
3748 if (!path_startswith(path
, "/dev")) {
3749 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3753 r
= parse_size(bandwidth
, 1000, &bytes
);
3754 if (r
< 0 || bytes
<= 0) {
3755 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3759 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3760 if (path_equal(path
, t
->path
)) {
3767 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3773 b
->rbps
= CGROUP_LIMIT_MAX
;
3774 b
->wbps
= CGROUP_LIMIT_MAX
;
3776 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3787 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3789 int config_parse_job_mode_isolate(
3791 const char *filename
,
3793 const char *section
,
3794 unsigned section_line
,
3808 r
= parse_boolean(rvalue
);
3810 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3814 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3818 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode
, exec_preserve_mode
, ExecPreserveMode
, "Failed to parse runtime directory preserve mode");
3820 int config_parse_exec_directories(
3822 const char *filename
,
3824 const char *section
,
3825 unsigned section_line
,
3842 if (isempty(rvalue
)) {
3843 /* Empty assignment resets the list */
3844 *rt
= strv_free(*rt
);
3848 for (p
= rvalue
;;) {
3849 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3851 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3855 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3856 "Invalid syntax, ignoring: %s", rvalue
);
3862 r
= unit_full_printf(u
, word
, &k
);
3864 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3865 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
3869 if (!path_is_safe(k
) || path_is_absolute(k
)) {
3870 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3871 "%s= path is not valid, ignoring assignment: %s", lvalue
, rvalue
);
3875 r
= strv_push(rt
, k
);
3882 int config_parse_set_status(
3884 const char *filename
,
3886 const char *section
,
3887 unsigned section_line
,
3895 const char *word
, *state
;
3897 ExitStatusSet
*status_set
= data
;
3904 /* Empty assignment resets the list */
3905 if (isempty(rvalue
)) {
3906 exit_status_set_free(status_set
);
3910 FOREACH_WORD(word
, l
, rvalue
, state
) {
3911 _cleanup_free_
char *temp
;
3915 temp
= strndup(word
, l
);
3919 r
= safe_atoi(temp
, &val
);
3921 val
= signal_from_string_try_harder(temp
);
3924 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3927 set
= &status_set
->signal
;
3929 if (val
< 0 || val
> 255) {
3930 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3933 set
= &status_set
->status
;
3936 r
= set_ensure_allocated(set
, NULL
);
3940 r
= set_put(*set
, INT_TO_PTR(val
));
3942 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3946 if (!isempty(state
))
3947 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3952 int config_parse_namespace_path_strv(
3954 const char *filename
,
3956 const char *section
,
3957 unsigned section_line
,
3974 if (isempty(rvalue
)) {
3975 /* Empty assignment resets the list */
3976 *sv
= strv_free(*sv
);
3982 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
3984 bool ignore_enoent
= false, shall_prefix
= false;
3986 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3992 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
3996 if (!utf8_is_valid(word
)) {
3997 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
4002 if (startswith(w
, "-")) {
4003 ignore_enoent
= true;
4006 if (startswith(w
, "+")) {
4007 shall_prefix
= true;
4011 r
= unit_full_printf(u
, w
, &resolved
);
4013 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s: %m", word
);
4017 if (!path_is_absolute(resolved
)) {
4018 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
4022 path_kill_slashes(resolved
);
4024 joined
= strjoin(ignore_enoent
? "-" : "",
4025 shall_prefix
? "+" : "",
4028 r
= strv_push(sv
, joined
);
4038 int config_parse_bind_paths(
4040 const char *filename
,
4042 const char *section
,
4043 unsigned section_line
,
4050 ExecContext
*c
= data
;
4060 if (isempty(rvalue
)) {
4061 /* Empty assignment resets the list */
4062 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
4063 c
->bind_mounts
= NULL
;
4064 c
->n_bind_mounts
= 0;
4070 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
4071 _cleanup_free_
char *sresolved
= NULL
, *dresolved
= NULL
;
4072 char *s
= NULL
, *d
= NULL
;
4073 bool rbind
= true, ignore_enoent
= false;
4075 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4081 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4085 r
= unit_full_printf(u
, source
, &sresolved
);
4087 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4088 "Failed to resolved specifiers in \"%s\", ignoring: %m", source
);
4094 ignore_enoent
= true;
4098 if (!utf8_is_valid(s
)) {
4099 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, s
);
4102 if (!path_is_absolute(s
)) {
4103 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute source path, ignoring: %s", s
);
4107 path_kill_slashes(s
);
4109 /* Optionally, the destination is specified. */
4110 if (p
&& p
[-1] == ':') {
4111 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4115 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4119 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':': %s", rvalue
);
4123 r
= unit_full_printf(u
, destination
, &dresolved
);
4125 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4126 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination
);
4130 if (!utf8_is_valid(dresolved
)) {
4131 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, dresolved
);
4134 if (!path_is_absolute(dresolved
)) {
4135 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute destination path, ignoring: %s", dresolved
);
4139 d
= path_kill_slashes(dresolved
);
4141 /* Optionally, there's also a short option string specified */
4142 if (p
&& p
[-1] == ':') {
4143 _cleanup_free_
char *options
= NULL
;
4145 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
4149 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4153 if (isempty(options
) || streq(options
, "rbind"))
4155 else if (streq(options
, "norbind"))
4158 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
4165 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
4169 .read_only
= !!strstr(lvalue
, "ReadOnly"),
4171 .ignore_enoent
= ignore_enoent
,
4180 int config_parse_no_new_privileges(
4182 const char *filename
,
4184 const char *section
,
4185 unsigned section_line
,
4192 ExecContext
*c
= data
;
4200 k
= parse_boolean(rvalue
);
4202 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
4206 c
->no_new_privileges
= k
;
4211 int config_parse_protect_home(
4213 const char *filename
,
4215 const char *section
,
4216 unsigned section_line
,
4223 ExecContext
*c
= data
;
4231 /* Our enum shall be a superset of booleans, hence first try
4232 * to parse as boolean, and then as enum */
4234 k
= parse_boolean(rvalue
);
4236 c
->protect_home
= PROTECT_HOME_YES
;
4238 c
->protect_home
= PROTECT_HOME_NO
;
4242 h
= protect_home_from_string(rvalue
);
4244 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
4248 c
->protect_home
= h
;
4254 int config_parse_protect_system(
4256 const char *filename
,
4258 const char *section
,
4259 unsigned section_line
,
4266 ExecContext
*c
= data
;
4274 /* Our enum shall be a superset of booleans, hence first try
4275 * to parse as boolean, and then as enum */
4277 k
= parse_boolean(rvalue
);
4279 c
->protect_system
= PROTECT_SYSTEM_YES
;
4281 c
->protect_system
= PROTECT_SYSTEM_NO
;
4285 s
= protect_system_from_string(rvalue
);
4287 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
4291 c
->protect_system
= s
;
4297 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode
, exec_keyring_mode
, ExecKeyringMode
, "Failed to parse keyring mode");
4299 int config_parse_job_timeout_sec(
4301 const char *filename
,
4303 const char *section
,
4304 unsigned section_line
,
4320 r
= parse_sec_fix_0(rvalue
, &usec
);
4322 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue
);
4326 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
4327 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
4330 if (!u
->job_running_timeout_set
)
4331 u
->job_running_timeout
= usec
;
4333 u
->job_timeout
= usec
;
4338 int config_parse_job_running_timeout_sec(
4340 const char *filename
,
4342 const char *section
,
4343 unsigned section_line
,
4359 r
= parse_sec_fix_0(rvalue
, &usec
);
4361 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue
);
4365 u
->job_running_timeout
= usec
;
4366 u
->job_running_timeout_set
= true;
4371 #define FOLLOW_MAX 8
4373 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4384 /* This will update the filename pointer if the loaded file is
4385 * reached by a symlink. The old string will be freed. */
4388 char *target
, *name
;
4390 if (c
++ >= FOLLOW_MAX
)
4393 path_kill_slashes(*filename
);
4395 /* Add the file name we are currently looking at to
4396 * the names of this unit, but only if it is a valid
4398 name
= basename(*filename
);
4399 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4401 id
= set_get(names
, name
);
4407 r
= set_consume(names
, id
);
4413 /* Try to open the file name, but don't if its a symlink */
4414 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4421 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4422 r
= readlink_and_make_absolute(*filename
, &target
);
4430 f
= fdopen(fd
, "re");
4442 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4450 /* Let's try to add in all symlink names we found */
4451 while ((k
= set_steal_first(names
))) {
4453 /* First try to merge in the other name into our
4455 r
= unit_merge_by_name(*u
, k
);
4459 /* Hmm, we couldn't merge the other unit into
4460 * ours? Then let's try it the other way
4463 /* If the symlink name we are looking at is unit template, then
4464 we must search for instance of this template */
4465 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4466 _cleanup_free_
char *instance
= NULL
;
4468 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4472 other
= manager_get_unit((*u
)->manager
, instance
);
4474 other
= manager_get_unit((*u
)->manager
, k
);
4479 r
= unit_merge(other
, *u
);
4482 return merge_by_names(u
, names
, NULL
);
4490 unit_choose_id(*u
, id
);
4498 static int load_from_path(Unit
*u
, const char *path
) {
4499 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4500 _cleanup_fclose_
FILE *f
= NULL
;
4501 _cleanup_free_
char *filename
= NULL
;
4510 symlink_names
= set_new(&string_hash_ops
);
4514 if (path_is_absolute(path
)) {
4516 filename
= strdup(path
);
4520 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4522 filename
= mfree(filename
);
4530 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4532 /* Instead of opening the path right away, we manually
4533 * follow all symlinks and add their name to our unit
4534 * name set while doing so */
4535 filename
= path_make_absolute(path
, *p
);
4539 if (u
->manager
->unit_path_cache
&&
4540 !set_get(u
->manager
->unit_path_cache
, filename
))
4543 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4546 filename
= mfree(filename
);
4548 /* ENOENT means that the file is missing or is a dangling symlink.
4549 * ENOTDIR means that one of paths we expect to be is a directory
4550 * is not a directory, we should just ignore that.
4551 * EACCES means that the directory or file permissions are wrong.
4554 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4555 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4558 /* Empty the symlink names for the next run */
4559 set_clear_free(symlink_names
);
4564 /* Hmm, no suitable file found? */
4567 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4568 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4573 r
= merge_by_names(&merged
, symlink_names
, id
);
4578 u
->load_state
= UNIT_MERGED
;
4582 if (fstat(fileno(f
), &st
) < 0)
4585 if (null_or_empty(&st
)) {
4586 u
->load_state
= UNIT_MASKED
;
4587 u
->fragment_mtime
= 0;
4589 u
->load_state
= UNIT_LOADED
;
4590 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4592 /* Now, parse the file contents */
4593 r
= config_parse(u
->id
, filename
, f
,
4594 UNIT_VTABLE(u
)->sections
,
4595 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4596 CONFIG_PARSE_ALLOW_INCLUDE
, u
);
4601 free(u
->fragment_path
);
4602 u
->fragment_path
= filename
;
4605 if (u
->source_path
) {
4606 if (stat(u
->source_path
, &st
) >= 0)
4607 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4609 u
->source_mtime
= 0;
4615 int unit_load_fragment(Unit
*u
) {
4621 assert(u
->load_state
== UNIT_STUB
);
4625 u
->load_state
= UNIT_LOADED
;
4629 /* First, try to find the unit under its id. We always look
4630 * for unit files in the default directories, to make it easy
4631 * to override things by placing things in /etc/systemd/system */
4632 r
= load_from_path(u
, u
->id
);
4636 /* Try to find an alias we can load this with */
4637 if (u
->load_state
== UNIT_STUB
) {
4638 SET_FOREACH(t
, u
->names
, i
) {
4643 r
= load_from_path(u
, t
);
4647 if (u
->load_state
!= UNIT_STUB
)
4652 /* And now, try looking for it under the suggested (originally linked) path */
4653 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4655 r
= load_from_path(u
, u
->fragment_path
);
4659 if (u
->load_state
== UNIT_STUB
)
4660 /* Hmm, this didn't work? Then let's get rid
4661 * of the fragment path stored for us, so that
4662 * we don't point to an invalid location. */
4663 u
->fragment_path
= mfree(u
->fragment_path
);
4666 /* Look for a template */
4667 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4668 _cleanup_free_
char *k
= NULL
;
4670 r
= unit_name_template(u
->id
, &k
);
4674 r
= load_from_path(u
, k
);
4677 log_unit_notice(u
, "Unit configuration has fatal error, unit will not be started.");
4681 if (u
->load_state
== UNIT_STUB
) {
4682 SET_FOREACH(t
, u
->names
, i
) {
4683 _cleanup_free_
char *z
= NULL
;
4688 r
= unit_name_template(t
, &z
);
4692 r
= load_from_path(u
, z
);
4696 if (u
->load_state
!= UNIT_STUB
)
4705 void unit_dump_config_items(FILE *f
) {
4706 static const struct {
4707 const ConfigParserCallback callback
;
4710 #if !HAVE_SYSV_COMPAT || !HAVE_SECCOMP || !HAVE_PAM || !HAVE_SELINUX || !ENABLE_SMACK || !HAVE_APPARMOR
4711 { config_parse_warn_compat
, "NOTSUPPORTED" },
4713 { config_parse_int
, "INTEGER" },
4714 { config_parse_unsigned
, "UNSIGNED" },
4715 { config_parse_iec_size
, "SIZE" },
4716 { config_parse_iec_uint64
, "SIZE" },
4717 { config_parse_si_size
, "SIZE" },
4718 { config_parse_bool
, "BOOLEAN" },
4719 { config_parse_string
, "STRING" },
4720 { config_parse_path
, "PATH" },
4721 { config_parse_unit_path_printf
, "PATH" },
4722 { config_parse_strv
, "STRING [...]" },
4723 { config_parse_exec_nice
, "NICE" },
4724 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4725 { config_parse_exec_io_class
, "IOCLASS" },
4726 { config_parse_exec_io_priority
, "IOPRIORITY" },
4727 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4728 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4729 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4730 { config_parse_mode
, "MODE" },
4731 { config_parse_unit_env_file
, "FILE" },
4732 { config_parse_exec_output
, "OUTPUT" },
4733 { config_parse_exec_input
, "INPUT" },
4734 { config_parse_log_facility
, "FACILITY" },
4735 { config_parse_log_level
, "LEVEL" },
4736 { config_parse_exec_secure_bits
, "SECUREBITS" },
4737 { config_parse_capability_set
, "BOUNDINGSET" },
4738 { config_parse_limit
, "LIMIT" },
4739 { config_parse_unit_deps
, "UNIT [...]" },
4740 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4741 { config_parse_service_type
, "SERVICETYPE" },
4742 { config_parse_service_restart
, "SERVICERESTART" },
4743 #if HAVE_SYSV_COMPAT
4744 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4746 { config_parse_kill_mode
, "KILLMODE" },
4747 { config_parse_signal
, "SIGNAL" },
4748 { config_parse_socket_listen
, "SOCKET [...]" },
4749 { config_parse_socket_bind
, "SOCKETBIND" },
4750 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4751 { config_parse_sec
, "SECONDS" },
4752 { config_parse_nsec
, "NANOSECONDS" },
4753 { config_parse_namespace_path_strv
, "PATH [...]" },
4754 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4755 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4756 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4757 { config_parse_unit_string_printf
, "STRING" },
4758 { config_parse_trigger_unit
, "UNIT" },
4759 { config_parse_timer
, "TIMER" },
4760 { config_parse_path_spec
, "PATH" },
4761 { config_parse_notify_access
, "ACCESS" },
4762 { config_parse_ip_tos
, "TOS" },
4763 { config_parse_unit_condition_path
, "CONDITION" },
4764 { config_parse_unit_condition_string
, "CONDITION" },
4765 { config_parse_unit_condition_null
, "CONDITION" },
4766 { config_parse_unit_slice
, "SLICE" },
4767 { config_parse_documentation
, "URL" },
4768 { config_parse_service_timeout
, "SECONDS" },
4769 { config_parse_emergency_action
, "ACTION" },
4770 { config_parse_set_status
, "STATUS" },
4771 { config_parse_service_sockets
, "SOCKETS" },
4772 { config_parse_environ
, "ENVIRON" },
4774 { config_parse_syscall_filter
, "SYSCALLS" },
4775 { config_parse_syscall_archs
, "ARCHS" },
4776 { config_parse_syscall_errno
, "ERRNO" },
4777 { config_parse_address_families
, "FAMILIES" },
4778 { config_parse_restrict_namespaces
, "NAMESPACES" },
4780 { config_parse_cpu_shares
, "SHARES" },
4781 { config_parse_cpu_weight
, "WEIGHT" },
4782 { config_parse_memory_limit
, "LIMIT" },
4783 { config_parse_device_allow
, "DEVICE" },
4784 { config_parse_device_policy
, "POLICY" },
4785 { config_parse_io_limit
, "LIMIT" },
4786 { config_parse_io_weight
, "WEIGHT" },
4787 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4788 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4789 { config_parse_blockio_weight
, "WEIGHT" },
4790 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4791 { config_parse_long
, "LONG" },
4792 { config_parse_socket_service
, "SERVICE" },
4794 { config_parse_exec_selinux_context
, "LABEL" },
4796 { config_parse_job_mode
, "MODE" },
4797 { config_parse_job_mode_isolate
, "BOOLEAN" },
4798 { config_parse_personality
, "PERSONALITY" },
4801 const char *prev
= NULL
;
4806 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4807 const char *rvalue
= "OTHER", *lvalue
;
4811 const ConfigPerfItem
*p
;
4813 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4815 dot
= strchr(i
, '.');
4816 lvalue
= dot
? dot
+ 1 : i
;
4820 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
4824 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
4827 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
4828 if (p
->parse
== table
[j
].callback
) {
4829 rvalue
= table
[j
].rvalue
;
4833 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);