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"
49 #include "hexdecoct.h"
52 #include "journal-util.h"
53 #include "load-fragment.h"
56 #include "mount-util.h"
57 #include "parse-util.h"
58 #include "path-util.h"
59 #include "process-util.h"
60 #include "rlimit-util.h"
62 #include "seccomp-util.h"
64 #include "securebits.h"
65 #include "securebits-util.h"
66 #include "signal-util.h"
67 #include "stat-util.h"
68 #include "string-util.h"
70 #include "unit-name.h"
71 #include "unit-printf.h"
73 #include "user-util.h"
77 int config_parse_warn_compat(
82 unsigned section_line
,
88 Disabled reason
= ltype
;
91 case DISABLED_CONFIGURATION
:
92 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
93 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
96 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
97 "Support for option %s= has been removed and it is ignored", lvalue
);
99 case DISABLED_EXPERIMENTAL
:
100 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
101 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
108 DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode
, collect_mode
, CollectMode
, "Failed to parse garbage collection mode");
110 int config_parse_unit_deps(
112 const char *filename
,
115 unsigned section_line
,
122 UnitDependency d
= ltype
;
132 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
135 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_RETAIN_ESCAPE
);
141 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
145 r
= unit_name_printf(u
, word
, &k
);
147 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
151 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
153 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
159 int config_parse_obsolete_unit_deps(
161 const char *filename
,
164 unsigned section_line
,
171 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
172 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue
, unit_dependency_to_string(ltype
));
174 return config_parse_unit_deps(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, data
, userdata
);
177 int config_parse_unit_string_printf(
179 const char *filename
,
182 unsigned section_line
,
189 _cleanup_free_
char *k
= NULL
;
198 r
= unit_full_printf(u
, rvalue
, &k
);
200 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
204 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
207 int config_parse_unit_strv_printf(
209 const char *filename
,
212 unsigned section_line
,
220 _cleanup_free_
char *k
= NULL
;
228 r
= unit_full_printf(u
, rvalue
, &k
);
230 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
234 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
237 int config_parse_unit_path_printf(
239 const char *filename
,
242 unsigned section_line
,
249 _cleanup_free_
char *k
= NULL
;
259 r
= unit_full_printf(u
, rvalue
, &k
);
261 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
262 "Failed to resolve unit specifiers on %s%s: %m",
263 fatal
? "" : ", ignoring", rvalue
);
264 return fatal
? -ENOEXEC
: 0;
267 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
270 int config_parse_unit_path_strv_printf(
272 const char *filename
,
275 unsigned section_line
,
292 if (isempty(rvalue
)) {
298 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
300 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
306 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
307 "Invalid syntax, ignoring: %s", rvalue
);
311 r
= unit_full_printf(u
, word
, &k
);
313 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
314 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
318 if (!utf8_is_valid(k
)) {
319 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
323 if (!path_is_absolute(k
)) {
324 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
325 "Symlink path is not absolute: %s", k
);
329 path_kill_slashes(k
);
338 int config_parse_socket_listen(const char *unit
,
339 const char *filename
,
342 unsigned section_line
,
349 _cleanup_free_ SocketPort
*p
= NULL
;
361 if (isempty(rvalue
)) {
362 /* An empty assignment removes all ports */
363 socket_free_ports(s
);
367 p
= new0(SocketPort
, 1);
371 if (ltype
!= SOCKET_SOCKET
) {
374 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
376 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
380 path_kill_slashes(p
->path
);
382 } else if (streq(lvalue
, "ListenNetlink")) {
383 _cleanup_free_
char *k
= NULL
;
385 p
->type
= SOCKET_SOCKET
;
386 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
388 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
392 r
= socket_address_parse_netlink(&p
->address
, k
);
394 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
399 _cleanup_free_
char *k
= NULL
;
401 p
->type
= SOCKET_SOCKET
;
402 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
404 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
408 r
= socket_address_parse_and_warn(&p
->address
, k
);
410 if (r
!= -EAFNOSUPPORT
)
411 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
416 if (streq(lvalue
, "ListenStream"))
417 p
->address
.type
= SOCK_STREAM
;
418 else if (streq(lvalue
, "ListenDatagram"))
419 p
->address
.type
= SOCK_DGRAM
;
421 assert(streq(lvalue
, "ListenSequentialPacket"));
422 p
->address
.type
= SOCK_SEQPACKET
;
425 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
426 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
432 p
->auxiliary_fds
= NULL
;
433 p
->n_auxiliary_fds
= 0;
437 LIST_FIND_TAIL(port
, s
->ports
, tail
);
438 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
440 LIST_PREPEND(port
, s
->ports
, p
);
446 int config_parse_socket_protocol(const char *unit
,
447 const char *filename
,
450 unsigned section_line
,
465 if (streq(rvalue
, "udplite"))
466 s
->socket_protocol
= IPPROTO_UDPLITE
;
467 else if (streq(rvalue
, "sctp"))
468 s
->socket_protocol
= IPPROTO_SCTP
;
470 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
477 int config_parse_socket_bind(const char *unit
,
478 const char *filename
,
481 unsigned section_line
,
489 SocketAddressBindIPv6Only b
;
498 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
502 r
= parse_boolean(rvalue
);
504 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
508 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
510 s
->bind_ipv6_only
= b
;
515 int config_parse_exec_nice(
517 const char *filename
,
520 unsigned section_line
,
527 ExecContext
*c
= data
;
535 r
= parse_nice(rvalue
, &priority
);
538 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
540 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
551 int config_parse_exec_oom_score_adjust(const char* unit
,
552 const char *filename
,
555 unsigned section_line
,
562 ExecContext
*c
= data
;
570 r
= safe_atoi(rvalue
, &oa
);
572 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
576 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
577 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
581 c
->oom_score_adjust
= oa
;
582 c
->oom_score_adjust_set
= true;
587 int config_parse_exec(
589 const char *filename
,
592 unsigned section_line
,
599 ExecCommand
**e
= data
;
611 rvalue
+= strspn(rvalue
, WHITESPACE
);
613 if (isempty(rvalue
)) {
614 /* An empty assignment resets the list */
615 *e
= exec_command_free_list(*e
);
621 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
622 ExecCommandFlags flags
= 0;
623 bool ignore
= false, separate_argv0
= false;
624 _cleanup_free_ ExecCommand
*nce
= NULL
;
625 _cleanup_strv_free_
char **n
= NULL
;
626 size_t nlen
= 0, nbufsize
= 0;
631 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
637 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
638 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
639 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
640 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
641 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
642 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
643 * other sandboxing, with some special exceptions for changing UID.
645 * The idea is that '!!' may be used to write services that can take benefit of systemd's
646 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
647 * privilege dropping within the daemon if the kernel does not offer that. */
649 if (*f
== '-' && !(flags
& EXEC_COMMAND_IGNORE_FAILURE
)) {
650 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
652 } else if (*f
== '@' && !separate_argv0
)
653 separate_argv0
= true;
654 else if (*f
== '+' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
655 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
656 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
657 flags
|= EXEC_COMMAND_NO_SETUID
;
658 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
659 flags
&= ~EXEC_COMMAND_NO_SETUID
;
660 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
666 r
= unit_full_printf(u
, f
, &path
);
668 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
669 "Failed to resolve unit specifiers on %s%s: %m",
670 f
, ignore
? ", ignoring" : "");
671 return ignore
? 0 : -ENOEXEC
;
675 /* First word is either "-" or "@" with no command. */
676 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
677 "Empty path in command line%s: \"%s\"",
678 ignore
? ", ignoring" : "", rvalue
);
679 return ignore
? 0 : -ENOEXEC
;
681 if (!string_is_safe(path
)) {
682 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
683 "Executable path contains special characters%s: %s",
684 ignore
? ", ignoring" : "", rvalue
);
685 return ignore
? 0 : -ENOEXEC
;
687 if (!path_is_absolute(path
)) {
688 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
689 "Executable path is not absolute%s: %s",
690 ignore
? ", ignoring" : "", rvalue
);
691 return ignore
? 0 : -ENOEXEC
;
693 if (endswith(path
, "/")) {
694 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
695 "Executable path specifies a directory%s: %s",
696 ignore
? ", ignoring" : "", rvalue
);
697 return ignore
? 0 : -ENOEXEC
;
700 if (!separate_argv0
) {
703 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
713 path_kill_slashes(path
);
715 while (!isempty(p
)) {
716 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
718 /* Check explicitly for an unquoted semicolon as
719 * command separator token. */
720 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
722 p
+= strspn(p
, WHITESPACE
);
727 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
728 * extract_first_word() would return the same for all of those. */
729 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
733 p
+= strspn(p
, WHITESPACE
);
735 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
746 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
750 return ignore
? 0 : -ENOEXEC
;
752 r
= unit_full_printf(u
, word
, &resolved
);
754 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
755 "Failed to resolve unit specifiers on %s%s: %m",
756 word
, ignore
? ", ignoring" : "");
757 return ignore
? 0 : -ENOEXEC
;
760 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
762 n
[nlen
++] = resolved
;
768 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
769 "Empty executable name or zeroeth argument%s: %s",
770 ignore
? ", ignoring" : "", rvalue
);
771 return ignore
? 0 : -ENOEXEC
;
774 nce
= new0(ExecCommand
, 1);
782 exec_command_append_list(e
, nce
);
784 /* Do not _cleanup_free_ these. */
795 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
796 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
798 int config_parse_socket_bindtodevice(
800 const char *filename
,
803 unsigned section_line
,
818 if (rvalue
[0] && !streq(rvalue
, "*")) {
819 if (!ifname_valid(rvalue
)) {
820 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
830 free(s
->bind_to_device
);
831 s
->bind_to_device
= n
;
836 int config_parse_exec_input(
838 const char *filename
,
841 unsigned section_line
,
848 ExecContext
*c
= data
;
859 n
= startswith(rvalue
, "fd:");
861 _cleanup_free_
char *resolved
= NULL
;
863 r
= unit_full_printf(u
, n
, &resolved
);
865 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
867 if (isempty(resolved
))
868 resolved
= mfree(resolved
);
869 else if (!fdname_is_valid(resolved
)) {
870 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name: %s", resolved
);
874 free_and_replace(c
->stdio_fdname
[STDIN_FILENO
], resolved
);
876 ei
= EXEC_INPUT_NAMED_FD
;
878 } else if ((n
= startswith(rvalue
, "file:"))) {
879 _cleanup_free_
char *resolved
= NULL
;
881 r
= unit_full_printf(u
, n
, &resolved
);
883 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
885 if (!path_is_absolute(resolved
)) {
886 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires an absolute path name: %s", resolved
);
890 if (!path_is_normalized(resolved
)) {
891 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires a normalized path name: %s", resolved
);
895 free_and_replace(c
->stdio_file
[STDIN_FILENO
], resolved
);
897 ei
= EXEC_INPUT_FILE
;
900 ei
= exec_input_from_string(rvalue
);
902 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
911 int config_parse_exec_input_text(
913 const char *filename
,
916 unsigned section_line
,
923 _cleanup_free_
char *unescaped
= NULL
, *resolved
= NULL
;
924 ExecContext
*c
= data
;
935 if (isempty(rvalue
)) {
936 /* Reset if the empty string is assigned */
937 c
->stdin_data
= mfree(c
->stdin_data
);
938 c
->stdin_data_size
= 0;
942 r
= cunescape(rvalue
, 0, &unescaped
);
944 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to decode C escaped text: %s", rvalue
);
946 r
= unit_full_printf(u
, unescaped
, &resolved
);
948 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", unescaped
);
950 sz
= strlen(resolved
);
951 if (c
->stdin_data_size
+ sz
+ 1 < c
->stdin_data_size
|| /* check for overflow */
952 c
->stdin_data_size
+ sz
+ 1 > EXEC_STDIN_DATA_MAX
) {
953 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c
->stdin_data_size
+ sz
, (size_t) EXEC_STDIN_DATA_MAX
);
957 p
= realloc(c
->stdin_data
, c
->stdin_data_size
+ sz
+ 1);
961 *((char*) mempcpy((char*) p
+ c
->stdin_data_size
, resolved
, sz
)) = '\n';
964 c
->stdin_data_size
+= sz
+ 1;
969 int config_parse_exec_input_data(
971 const char *filename
,
974 unsigned section_line
,
981 _cleanup_free_
void *p
= NULL
;
982 ExecContext
*c
= data
;
992 if (isempty(rvalue
)) {
993 /* Reset if the empty string is assigned */
994 c
->stdin_data
= mfree(c
->stdin_data
);
995 c
->stdin_data_size
= 0;
999 r
= unbase64mem(rvalue
, (size_t) -1, &p
, &sz
);
1001 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to decode base64 data, ignoring: %s", rvalue
);
1005 if (c
->stdin_data_size
+ sz
< c
->stdin_data_size
|| /* check for overflow */
1006 c
->stdin_data_size
+ sz
> EXEC_STDIN_DATA_MAX
) {
1007 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c
->stdin_data_size
+ sz
, (size_t) EXEC_STDIN_DATA_MAX
);
1011 q
= realloc(c
->stdin_data
, c
->stdin_data_size
+ sz
);
1015 memcpy((uint8_t*) q
+ c
->stdin_data_size
, p
, sz
);
1018 c
->stdin_data_size
+= sz
;
1023 int config_parse_exec_output(
1025 const char *filename
,
1027 const char *section
,
1028 unsigned section_line
,
1035 _cleanup_free_
char *resolved
= NULL
;
1037 ExecContext
*c
= data
;
1048 n
= startswith(rvalue
, "fd:");
1050 r
= unit_full_printf(u
, n
, &resolved
);
1052 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
1054 if (isempty(resolved
))
1055 resolved
= mfree(resolved
);
1056 else if (!fdname_is_valid(resolved
)) {
1057 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name: %s", resolved
);
1061 eo
= EXEC_OUTPUT_NAMED_FD
;
1063 } else if ((n
= startswith(rvalue
, "file:"))) {
1065 r
= unit_full_printf(u
, n
, &resolved
);
1067 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
1069 if (!path_is_absolute(resolved
)) {
1070 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires an absolute path name: %s", resolved
);
1074 if (!path_is_normalized(resolved
)) {
1075 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires a normalized path name, ignoring: %s", resolved
);
1079 eo
= EXEC_OUTPUT_FILE
;
1082 eo
= exec_output_from_string(rvalue
);
1084 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
1089 if (streq(lvalue
, "StandardOutput")) {
1090 if (eo
== EXEC_OUTPUT_NAMED_FD
)
1091 free_and_replace(c
->stdio_fdname
[STDOUT_FILENO
], resolved
);
1093 free_and_replace(c
->stdio_file
[STDOUT_FILENO
], resolved
);
1098 assert(streq(lvalue
, "StandardError"));
1100 if (eo
== EXEC_OUTPUT_NAMED_FD
)
1101 free_and_replace(c
->stdio_fdname
[STDERR_FILENO
], resolved
);
1103 free_and_replace(c
->stdio_file
[STDERR_FILENO
], resolved
);
1111 int config_parse_exec_io_class(const char *unit
,
1112 const char *filename
,
1114 const char *section
,
1115 unsigned section_line
,
1122 ExecContext
*c
= data
;
1130 x
= ioprio_class_from_string(rvalue
);
1132 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
1136 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
1137 c
->ioprio_set
= true;
1142 int config_parse_exec_io_priority(const char *unit
,
1143 const char *filename
,
1145 const char *section
,
1146 unsigned section_line
,
1153 ExecContext
*c
= data
;
1161 r
= ioprio_parse_priority(rvalue
, &i
);
1163 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
1167 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
1168 c
->ioprio_set
= true;
1173 int config_parse_exec_cpu_sched_policy(const char *unit
,
1174 const char *filename
,
1176 const char *section
,
1177 unsigned section_line
,
1185 ExecContext
*c
= data
;
1193 x
= sched_policy_from_string(rvalue
);
1195 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1199 c
->cpu_sched_policy
= x
;
1200 /* Moving to or from real-time policy? We need to adjust the priority */
1201 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
1202 c
->cpu_sched_set
= true;
1207 int config_parse_exec_cpu_sched_prio(const char *unit
,
1208 const char *filename
,
1210 const char *section
,
1211 unsigned section_line
,
1218 ExecContext
*c
= data
;
1226 r
= safe_atoi(rvalue
, &i
);
1228 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1232 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1233 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1234 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1236 if (i
< min
|| i
> max
) {
1237 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1241 c
->cpu_sched_priority
= i
;
1242 c
->cpu_sched_set
= true;
1247 int config_parse_exec_cpu_affinity(const char *unit
,
1248 const char *filename
,
1250 const char *section
,
1251 unsigned section_line
,
1258 ExecContext
*c
= data
;
1259 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1267 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1272 CPU_FREE(c
->cpuset
);
1275 /* An empty assignment resets the CPU list */
1281 c
->cpuset_ncpus
= ncpus
;
1286 int config_parse_exec_secure_bits(const char *unit
,
1287 const char *filename
,
1289 const char *section
,
1290 unsigned section_line
,
1297 ExecContext
*c
= data
;
1305 if (isempty(rvalue
)) {
1306 /* An empty assignment resets the field */
1311 r
= secure_bits_from_string(rvalue
);
1315 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1316 "Invalid syntax, ignoring: %s", rvalue
);
1325 int config_parse_capability_set(
1327 const char *filename
,
1329 const char *section
,
1330 unsigned section_line
,
1337 uint64_t *capability_set
= data
;
1338 uint64_t sum
= 0, initial
= 0;
1339 bool invert
= false;
1347 if (rvalue
[0] == '~') {
1352 if (streq(lvalue
, "CapabilityBoundingSet"))
1353 initial
= CAP_ALL
; /* initialized to all bits on */
1354 /* else "AmbientCapabilities" initialized to all bits off */
1356 r
= capability_set_from_string(rvalue
, &sum
);
1360 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word: %s", rvalue
);
1364 if (sum
== 0 || *capability_set
== initial
)
1365 /* "", "~" or uninitialized data -> replace */
1366 *capability_set
= invert
? ~sum
: sum
;
1368 /* previous data -> merge */
1370 *capability_set
&= ~sum
;
1372 *capability_set
|= sum
;
1378 int config_parse_limit(
1380 const char *filename
,
1382 const char *section
,
1383 unsigned section_line
,
1390 struct rlimit
**rl
= data
, d
= {};
1398 r
= rlimit_parse(ltype
, rvalue
, &d
);
1400 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1404 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1411 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1419 #if HAVE_SYSV_COMPAT
1420 int config_parse_sysv_priority(const char *unit
,
1421 const char *filename
,
1423 const char *section
,
1424 unsigned section_line
,
1431 int *priority
= data
;
1439 r
= safe_atoi(rvalue
, &i
);
1440 if (r
< 0 || i
< 0) {
1441 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1445 *priority
= (int) i
;
1450 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1451 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1453 int config_parse_exec_mount_flags(
1455 const char *filename
,
1457 const char *section
,
1458 unsigned section_line
,
1466 ExecContext
*c
= data
;
1474 r
= mount_propagation_flags_from_string(rvalue
, &c
->mount_flags
);
1476 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1481 int config_parse_exec_selinux_context(
1483 const char *filename
,
1485 const char *section
,
1486 unsigned section_line
,
1493 ExecContext
*c
= data
;
1504 if (isempty(rvalue
)) {
1505 c
->selinux_context
= mfree(c
->selinux_context
);
1506 c
->selinux_context_ignore
= false;
1510 if (rvalue
[0] == '-') {
1516 r
= unit_full_printf(u
, rvalue
, &k
);
1518 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1519 "Failed to resolve specifiers%s: %m",
1520 ignore
? ", ignoring" : "");
1521 return ignore
? 0 : -ENOEXEC
;
1524 free(c
->selinux_context
);
1525 c
->selinux_context
= k
;
1526 c
->selinux_context_ignore
= ignore
;
1531 int config_parse_exec_apparmor_profile(
1533 const char *filename
,
1535 const char *section
,
1536 unsigned section_line
,
1543 ExecContext
*c
= data
;
1554 if (isempty(rvalue
)) {
1555 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1556 c
->apparmor_profile_ignore
= false;
1560 if (rvalue
[0] == '-') {
1566 r
= unit_full_printf(u
, rvalue
, &k
);
1568 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1569 "Failed to resolve specifiers%s: %m",
1570 ignore
? ", ignoring" : "");
1571 return ignore
? 0 : -ENOEXEC
;
1574 free(c
->apparmor_profile
);
1575 c
->apparmor_profile
= k
;
1576 c
->apparmor_profile_ignore
= ignore
;
1581 int config_parse_exec_smack_process_label(
1583 const char *filename
,
1585 const char *section
,
1586 unsigned section_line
,
1593 ExecContext
*c
= data
;
1604 if (isempty(rvalue
)) {
1605 c
->smack_process_label
= mfree(c
->smack_process_label
);
1606 c
->smack_process_label_ignore
= false;
1610 if (rvalue
[0] == '-') {
1616 r
= unit_full_printf(u
, rvalue
, &k
);
1618 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1619 "Failed to resolve specifiers%s: %m",
1620 ignore
? ", ignoring" : "");
1621 return ignore
? 0 : -ENOEXEC
;
1624 free(c
->smack_process_label
);
1625 c
->smack_process_label
= k
;
1626 c
->smack_process_label_ignore
= ignore
;
1631 int config_parse_timer(const char *unit
,
1632 const char *filename
,
1634 const char *section
,
1635 unsigned section_line
,
1646 CalendarSpec
*c
= NULL
;
1648 _cleanup_free_
char *k
= NULL
;
1656 if (isempty(rvalue
)) {
1657 /* Empty assignment resets list */
1658 timer_free_values(t
);
1662 b
= timer_base_from_string(lvalue
);
1664 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1668 r
= unit_full_printf(u
, rvalue
, &k
);
1670 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1674 if (b
== TIMER_CALENDAR
) {
1675 if (calendar_spec_from_string(k
, &c
) < 0) {
1676 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1680 if (parse_sec(k
, &usec
) < 0) {
1681 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1686 v
= new0(TimerValue
, 1);
1688 calendar_spec_free(c
);
1694 v
->calendar_spec
= c
;
1696 LIST_PREPEND(value
, t
->values
, v
);
1701 int config_parse_trigger_unit(
1703 const char *filename
,
1705 const char *section
,
1706 unsigned section_line
,
1713 _cleanup_free_
char *p
= NULL
;
1723 if (!hashmap_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1724 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1728 r
= unit_name_printf(u
, rvalue
, &p
);
1730 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1734 type
= unit_name_to_type(p
);
1736 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1740 if (type
== u
->type
) {
1741 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1745 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1747 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1754 int config_parse_path_spec(const char *unit
,
1755 const char *filename
,
1757 const char *section
,
1758 unsigned section_line
,
1768 _cleanup_free_
char *k
= NULL
;
1776 if (isempty(rvalue
)) {
1777 /* Empty assignment clears list */
1782 b
= path_type_from_string(lvalue
);
1784 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1788 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1790 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1794 if (!path_is_absolute(k
)) {
1795 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1799 s
= new0(PathSpec
, 1);
1804 s
->path
= path_kill_slashes(k
);
1809 LIST_PREPEND(spec
, p
->specs
, s
);
1814 int config_parse_socket_service(
1816 const char *filename
,
1818 const char *section
,
1819 unsigned section_line
,
1826 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1827 _cleanup_free_
char *p
= NULL
;
1837 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1839 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", rvalue
);
1843 if (!endswith(p
, ".service")) {
1844 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service: %s", rvalue
);
1848 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1850 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s: %s", rvalue
, bus_error_message(&error
, r
));
1854 unit_ref_set(&s
->service
, x
);
1859 int config_parse_fdname(
1861 const char *filename
,
1863 const char *section
,
1864 unsigned section_line
,
1871 _cleanup_free_
char *p
= NULL
;
1880 if (isempty(rvalue
)) {
1881 s
->fdname
= mfree(s
->fdname
);
1885 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1887 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1891 if (!fdname_is_valid(p
)) {
1892 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1896 return free_and_replace(s
->fdname
, p
);
1899 int config_parse_service_sockets(
1901 const char *filename
,
1903 const char *section
,
1904 unsigned section_line
,
1922 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1924 r
= extract_first_word(&p
, &word
, NULL
, 0);
1930 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1934 r
= unit_name_printf(UNIT(s
), word
, &k
);
1936 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1940 if (!endswith(k
, ".socket")) {
1941 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1945 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1947 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1949 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1951 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1957 int config_parse_bus_name(
1959 const char *filename
,
1961 const char *section
,
1962 unsigned section_line
,
1969 _cleanup_free_
char *k
= NULL
;
1978 r
= unit_full_printf(u
, rvalue
, &k
);
1980 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1984 if (!service_name_is_valid(k
)) {
1985 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1989 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1992 int config_parse_service_timeout(
1994 const char *filename
,
1996 const char *section
,
1997 unsigned section_line
,
2004 Service
*s
= userdata
;
2013 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
2015 r
= parse_sec(rvalue
, &usec
);
2017 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
2021 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
2022 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
2023 * all other timeouts. */
2025 usec
= USEC_INFINITY
;
2027 if (!streq(lvalue
, "TimeoutStopSec")) {
2028 s
->start_timeout_defined
= true;
2029 s
->timeout_start_usec
= usec
;
2032 if (!streq(lvalue
, "TimeoutStartSec"))
2033 s
->timeout_stop_usec
= usec
;
2038 int config_parse_sec_fix_0(
2040 const char *filename
,
2042 const char *section
,
2043 unsigned section_line
,
2050 usec_t
*usec
= data
;
2058 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
2059 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
2062 r
= parse_sec_fix_0(rvalue
, usec
);
2064 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
2071 int config_parse_user_group(
2073 const char *filename
,
2075 const char *section
,
2076 unsigned section_line
,
2083 char **user
= data
, *n
;
2092 if (isempty(rvalue
))
2095 _cleanup_free_
char *k
= NULL
;
2097 r
= unit_full_printf(u
, rvalue
, &k
);
2099 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
2103 if (!valid_user_group_name_or_id(k
)) {
2104 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
2118 int config_parse_user_group_strv(
2120 const char *filename
,
2122 const char *section
,
2123 unsigned section_line
,
2130 char ***users
= data
;
2140 if (isempty(rvalue
)) {
2141 *users
= strv_free(*users
);
2147 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2149 r
= extract_first_word(&p
, &word
, NULL
, 0);
2155 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax: %s", rvalue
);
2159 r
= unit_full_printf(u
, word
, &k
);
2161 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", word
);
2165 if (!valid_user_group_name_or_id(k
)) {
2166 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
2170 r
= strv_push(users
, k
);
2180 int config_parse_working_directory(
2182 const char *filename
,
2184 const char *section
,
2185 unsigned section_line
,
2192 ExecContext
*c
= data
;
2203 if (rvalue
[0] == '-') {
2209 if (streq(rvalue
, "~")) {
2210 c
->working_directory_home
= true;
2211 c
->working_directory
= mfree(c
->working_directory
);
2213 _cleanup_free_
char *k
= NULL
;
2215 r
= unit_full_printf(u
, rvalue
, &k
);
2217 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2218 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2219 rvalue
, missing_ok
? ", ignoring" : "");
2220 return missing_ok
? 0 : -ENOEXEC
;
2223 path_kill_slashes(k
);
2225 if (!utf8_is_valid(k
)) {
2226 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2227 return missing_ok
? 0 : -ENOEXEC
;
2230 if (!path_is_absolute(k
)) {
2231 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2232 "Working directory path '%s' is not absolute%s.",
2233 rvalue
, missing_ok
? ", ignoring" : "");
2234 return missing_ok
? 0 : -ENOEXEC
;
2237 c
->working_directory_home
= false;
2238 free_and_replace(c
->working_directory
, k
);
2241 c
->working_directory_missing_ok
= missing_ok
;
2245 int config_parse_unit_env_file(const char *unit
,
2246 const char *filename
,
2248 const char *section
,
2249 unsigned section_line
,
2258 _cleanup_free_
char *n
= NULL
;
2266 if (isempty(rvalue
)) {
2267 /* Empty assignment frees the list */
2268 *env
= strv_free(*env
);
2272 r
= unit_full_printf(u
, rvalue
, &n
);
2274 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2278 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2279 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2283 r
= strv_extend(env
, n
);
2290 int config_parse_environ(
2292 const char *filename
,
2294 const char *section
,
2295 unsigned section_line
,
2312 if (isempty(rvalue
)) {
2313 /* Empty assignment resets the list */
2314 *env
= strv_free(*env
);
2318 for (p
= rvalue
;; ) {
2319 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2321 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2327 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2328 "Invalid syntax, ignoring: %s", rvalue
);
2333 r
= unit_full_printf(u
, word
, &k
);
2335 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2336 "Failed to resolve specifiers, ignoring: %s", word
);
2344 if (!env_assignment_is_valid(k
)) {
2345 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2346 "Invalid environment assignment, ignoring: %s", k
);
2350 r
= strv_env_replace(env
, k
);
2358 int config_parse_pass_environ(
2360 const char *filename
,
2362 const char *section
,
2363 unsigned section_line
,
2370 const char *whole_rvalue
= rvalue
;
2371 _cleanup_strv_free_
char **n
= NULL
;
2372 size_t nlen
= 0, nbufsize
= 0;
2373 char*** passenv
= data
;
2382 if (isempty(rvalue
)) {
2383 /* Empty assignment resets the list */
2384 *passenv
= strv_free(*passenv
);
2389 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2391 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2397 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2398 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2403 r
= unit_full_printf(u
, word
, &k
);
2405 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2406 "Failed to resolve specifiers, ignoring: %s", word
);
2414 if (!env_name_is_valid(k
)) {
2415 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2416 "Invalid environment name for %s, ignoring: %s", lvalue
, k
);
2420 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2429 r
= strv_extend_strv(passenv
, n
, true);
2437 int config_parse_unset_environ(
2439 const char *filename
,
2441 const char *section
,
2442 unsigned section_line
,
2449 _cleanup_strv_free_
char **n
= NULL
;
2450 const char *whole_rvalue
= rvalue
;
2451 size_t nlen
= 0, nbufsize
= 0;
2452 char*** unsetenv
= data
;
2461 if (isempty(rvalue
)) {
2462 /* Empty assignment resets the list */
2463 *unsetenv
= strv_free(*unsetenv
);
2468 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2470 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2476 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2477 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2482 r
= unit_full_printf(u
, word
, &k
);
2484 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2485 "Failed to resolve specifiers, ignoring: %s", word
);
2493 if (!env_assignment_is_valid(k
) && !env_name_is_valid(k
)) {
2494 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2495 "Invalid environment name or assignment %s, ignoring: %s", lvalue
, k
);
2499 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2508 r
= strv_extend_strv(unsetenv
, n
, true);
2516 int config_parse_log_extra_fields(
2518 const char *filename
,
2520 const char *section
,
2521 unsigned section_line
,
2528 ExecContext
*c
= data
;
2538 if (isempty(rvalue
)) {
2539 exec_context_free_log_extra_fields(c
);
2543 for (p
= rvalue
;; ) {
2544 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2548 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2554 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2558 r
= unit_full_printf(u
, word
, &k
);
2560 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring field: %m", word
);
2564 eq
= strchr(k
, '=');
2566 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field lacks '=' character, ignoring field: %s", k
);
2570 if (!journal_field_valid(k
, eq
-k
, false)) {
2571 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field name is invalid, ignoring field: %s", k
);
2575 t
= realloc_multiply(c
->log_extra_fields
, sizeof(struct iovec
), c
->n_log_extra_fields
+1);
2579 c
->log_extra_fields
= t
;
2580 c
->log_extra_fields
[c
->n_log_extra_fields
++] = IOVEC_MAKE_STRING(k
);
2588 int config_parse_ip_tos(const char *unit
,
2589 const char *filename
,
2591 const char *section
,
2592 unsigned section_line
,
2599 int *ip_tos
= data
, x
;
2606 x
= ip_tos_from_string(rvalue
);
2608 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2616 int config_parse_unit_condition_path(
2618 const char *filename
,
2620 const char *section
,
2621 unsigned section_line
,
2628 _cleanup_free_
char *p
= NULL
;
2629 Condition
**list
= data
, *c
;
2630 ConditionType t
= ltype
;
2631 bool trigger
, negate
;
2640 if (isempty(rvalue
)) {
2641 /* Empty assignment resets the list */
2642 *list
= condition_free_list(*list
);
2646 trigger
= rvalue
[0] == '|';
2650 negate
= rvalue
[0] == '!';
2654 r
= unit_full_printf(u
, rvalue
, &p
);
2656 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2660 if (!path_is_absolute(p
)) {
2661 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2665 c
= condition_new(t
, p
, trigger
, negate
);
2669 LIST_PREPEND(conditions
, *list
, c
);
2673 int config_parse_unit_condition_string(
2675 const char *filename
,
2677 const char *section
,
2678 unsigned section_line
,
2685 _cleanup_free_
char *s
= NULL
;
2686 Condition
**list
= data
, *c
;
2687 ConditionType t
= ltype
;
2688 bool trigger
, negate
;
2697 if (isempty(rvalue
)) {
2698 /* Empty assignment resets the list */
2699 *list
= condition_free_list(*list
);
2703 trigger
= rvalue
[0] == '|';
2707 negate
= rvalue
[0] == '!';
2711 r
= unit_full_printf(u
, rvalue
, &s
);
2713 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2717 c
= condition_new(t
, s
, trigger
, negate
);
2721 LIST_PREPEND(conditions
, *list
, c
);
2725 int config_parse_unit_condition_null(
2727 const char *filename
,
2729 const char *section
,
2730 unsigned section_line
,
2737 Condition
**list
= data
, *c
;
2738 bool trigger
, negate
;
2746 if (isempty(rvalue
)) {
2747 /* Empty assignment resets the list */
2748 *list
= condition_free_list(*list
);
2752 trigger
= rvalue
[0] == '|';
2756 negate
= rvalue
[0] == '!';
2760 b
= parse_boolean(rvalue
);
2762 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2769 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2773 LIST_PREPEND(conditions
, *list
, c
);
2777 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2778 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2780 int config_parse_unit_requires_mounts_for(
2782 const char *filename
,
2784 const char *section
,
2785 unsigned section_line
,
2801 for (p
= rvalue
;; ) {
2802 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2804 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2810 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2811 "Invalid syntax, ignoring: %s", rvalue
);
2815 if (!utf8_is_valid(word
)) {
2816 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2820 r
= unit_full_printf(u
, word
, &resolved
);
2822 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit name \"%s\", ignoring: %m", word
);
2826 r
= unit_require_mounts_for(u
, resolved
, UNIT_DEPENDENCY_FILE
);
2828 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", resolved
);
2834 int config_parse_documentation(const char *unit
,
2835 const char *filename
,
2837 const char *section
,
2838 unsigned section_line
,
2854 if (isempty(rvalue
)) {
2855 /* Empty assignment resets the list */
2856 u
->documentation
= strv_free(u
->documentation
);
2860 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2861 rvalue
, data
, userdata
);
2865 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2867 if (documentation_url_is_valid(*a
))
2870 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2882 static int syscall_filter_parse_one(
2884 const char *filename
,
2894 const SyscallFilterSet
*set
;
2897 set
= syscall_filter_set_find(t
);
2900 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Unknown system call group, ignoring: %s", t
);
2904 NULSTR_FOREACH(i
, set
->value
) {
2905 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, i
, false, errno_num
);
2912 id
= seccomp_syscall_resolve_name(t
);
2913 if (id
== __NR_SCMP_ERROR
) {
2915 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2919 /* If we previously wanted to forbid a syscall and now
2920 * we want to allow it, then remove it from the list.
2922 if (!invert
== c
->syscall_whitelist
) {
2923 r
= hashmap_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1), INT_TO_PTR(errno_num
));
2929 (void) hashmap_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2935 int config_parse_syscall_filter(
2937 const char *filename
,
2939 const char *section
,
2940 unsigned section_line
,
2947 ExecContext
*c
= data
;
2949 bool invert
= false;
2958 if (isempty(rvalue
)) {
2959 /* Empty assignment resets the list */
2960 c
->syscall_filter
= hashmap_free(c
->syscall_filter
);
2961 c
->syscall_whitelist
= false;
2965 if (rvalue
[0] == '~') {
2970 if (!c
->syscall_filter
) {
2971 c
->syscall_filter
= hashmap_new(NULL
);
2972 if (!c
->syscall_filter
)
2976 /* Allow everything but the ones listed */
2977 c
->syscall_whitelist
= false;
2979 /* Allow nothing but the ones listed */
2980 c
->syscall_whitelist
= true;
2982 /* Accept default syscalls if we are on a whitelist */
2983 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, false, "@default", false, -1);
2991 _cleanup_free_
char *word
= NULL
, *name
= NULL
;
2994 r
= extract_first_word(&p
, &word
, NULL
, 0);
3000 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
3004 r
= parse_syscall_and_errno(word
, &name
, &num
);
3006 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse syscall:errno, ignoring: %s", word
);
3010 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, name
, true, num
);
3018 int config_parse_syscall_archs(
3020 const char *filename
,
3022 const char *section
,
3023 unsigned section_line
,
3034 if (isempty(rvalue
)) {
3035 *archs
= set_free(*archs
);
3039 r
= set_ensure_allocated(archs
, NULL
);
3043 for (p
= rvalue
;;) {
3044 _cleanup_free_
char *word
= NULL
;
3047 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3053 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3054 "Invalid syntax, ignoring: %s", rvalue
);
3058 r
= seccomp_arch_from_string(word
, &a
);
3060 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3061 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
3065 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
3071 int config_parse_syscall_errno(
3073 const char *filename
,
3075 const char *section
,
3076 unsigned section_line
,
3083 ExecContext
*c
= data
;
3090 if (isempty(rvalue
)) {
3091 /* Empty assignment resets to KILL */
3092 c
->syscall_errno
= 0;
3096 e
= parse_errno(rvalue
);
3098 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
3102 c
->syscall_errno
= e
;
3106 int config_parse_address_families(
3108 const char *filename
,
3110 const char *section
,
3111 unsigned section_line
,
3118 ExecContext
*c
= data
;
3119 bool invert
= false;
3127 if (isempty(rvalue
)) {
3128 /* Empty assignment resets the list */
3129 c
->address_families
= set_free(c
->address_families
);
3130 c
->address_families_whitelist
= false;
3134 if (rvalue
[0] == '~') {
3139 if (!c
->address_families
) {
3140 c
->address_families
= set_new(NULL
);
3141 if (!c
->address_families
)
3144 c
->address_families_whitelist
= !invert
;
3147 for (p
= rvalue
;;) {
3148 _cleanup_free_
char *word
= NULL
;
3151 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3157 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3158 "Invalid syntax, ignoring: %s", rvalue
);
3162 af
= af_from_name(word
);
3164 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3165 "Failed to parse address family \"%s\", ignoring: %m", word
);
3169 /* If we previously wanted to forbid an address family and now
3170 * we want to allow it, then just remove it from the list.
3172 if (!invert
== c
->address_families_whitelist
) {
3173 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
3177 set_remove(c
->address_families
, INT_TO_PTR(af
));
3181 int config_parse_restrict_namespaces(
3183 const char *filename
,
3185 const char *section
,
3186 unsigned section_line
,
3193 ExecContext
*c
= data
;
3194 bool invert
= false;
3197 if (isempty(rvalue
)) {
3198 /* Reset to the default. */
3199 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3203 if (rvalue
[0] == '~') {
3208 r
= parse_boolean(rvalue
);
3210 c
->restrict_namespaces
= 0;
3212 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3214 /* Not a boolean argument, in this case it's a list of namespace types. */
3216 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
3218 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
3224 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
3230 int config_parse_unit_slice(
3232 const char *filename
,
3234 const char *section
,
3235 unsigned section_line
,
3242 _cleanup_free_
char *k
= NULL
;
3243 Unit
*u
= userdata
, *slice
= NULL
;
3251 r
= unit_name_printf(u
, rvalue
, &k
);
3253 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
3257 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
3259 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
3263 r
= unit_set_slice(u
, slice
);
3265 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
3272 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
3274 int config_parse_cpu_weight(
3276 const char *filename
,
3278 const char *section
,
3279 unsigned section_line
,
3286 uint64_t *weight
= data
;
3293 r
= cg_weight_parse(rvalue
, weight
);
3295 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
3302 int config_parse_cpu_shares(
3304 const char *filename
,
3306 const char *section
,
3307 unsigned section_line
,
3314 uint64_t *shares
= data
;
3321 r
= cg_cpu_shares_parse(rvalue
, shares
);
3323 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
3330 int config_parse_cpu_quota(
3332 const char *filename
,
3334 const char *section
,
3335 unsigned section_line
,
3342 CGroupContext
*c
= data
;
3349 if (isempty(rvalue
)) {
3350 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3354 r
= parse_percent_unbounded(rvalue
);
3356 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3360 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3364 int config_parse_memory_limit(
3366 const char *filename
,
3368 const char *section
,
3369 unsigned section_line
,
3376 CGroupContext
*c
= data
;
3377 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3380 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3382 r
= parse_percent(rvalue
);
3384 r
= parse_size(rvalue
, 1024, &bytes
);
3386 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3390 bytes
= physical_memory_scale(r
, 100U);
3392 if (bytes
<= 0 || bytes
>= UINT64_MAX
) {
3393 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3398 if (streq(lvalue
, "MemoryLow"))
3399 c
->memory_low
= bytes
;
3400 else if (streq(lvalue
, "MemoryHigh"))
3401 c
->memory_high
= bytes
;
3402 else if (streq(lvalue
, "MemoryMax"))
3403 c
->memory_max
= bytes
;
3404 else if (streq(lvalue
, "MemorySwapMax"))
3405 c
->memory_swap_max
= bytes
;
3406 else if (streq(lvalue
, "MemoryLimit"))
3407 c
->memory_limit
= bytes
;
3414 int config_parse_tasks_max(
3416 const char *filename
,
3418 const char *section
,
3419 unsigned section_line
,
3426 uint64_t *tasks_max
= data
, v
;
3430 if (isempty(rvalue
)) {
3431 *tasks_max
= u
->manager
->default_tasks_max
;
3435 if (streq(rvalue
, "infinity")) {
3436 *tasks_max
= CGROUP_LIMIT_MAX
;
3440 r
= parse_percent(rvalue
);
3442 r
= safe_atou64(rvalue
, &v
);
3444 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3448 v
= system_tasks_max_scale(r
, 100U);
3450 if (v
<= 0 || v
>= UINT64_MAX
) {
3451 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3459 int config_parse_delegate(
3461 const char *filename
,
3463 const char *section
,
3464 unsigned section_line
,
3471 CGroupContext
*c
= data
;
3474 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3475 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3476 * mask to delegate. */
3478 if (isempty(rvalue
)) {
3480 c
->delegate_controllers
= 0;
3484 r
= parse_boolean(rvalue
);
3486 const char *p
= rvalue
;
3487 CGroupMask mask
= 0;
3490 _cleanup_free_
char *word
= NULL
;
3491 CGroupController cc
;
3493 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3499 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
3503 cc
= cgroup_controller_from_string(word
);
3505 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid controller name '%s', ignoring", rvalue
);
3509 mask
|= CGROUP_CONTROLLER_TO_MASK(cc
);
3513 c
->delegate_controllers
|= mask
;
3517 c
->delegate_controllers
= _CGROUP_MASK_ALL
;
3519 c
->delegate
= false;
3520 c
->delegate_controllers
= 0;
3526 int config_parse_device_allow(
3528 const char *filename
,
3530 const char *section
,
3531 unsigned section_line
,
3538 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3539 CGroupContext
*c
= data
;
3540 CGroupDeviceAllow
*a
;
3541 const char *m
= NULL
;
3545 if (isempty(rvalue
)) {
3546 while (c
->device_allow
)
3547 cgroup_context_free_device_allow(c
, c
->device_allow
);
3552 r
= unit_full_printf(userdata
, rvalue
, &t
);
3554 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3555 "Failed to resolve specifiers in %s, ignoring: %m",
3559 n
= strcspn(t
, WHITESPACE
);
3561 path
= strndup(t
, n
);
3565 if (!is_deviceallow_pattern(path
)) {
3566 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3570 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3574 if (!in_charset(m
, "rwm")) {
3575 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3579 a
= new0(CGroupDeviceAllow
, 1);
3585 a
->r
= !!strchr(m
, 'r');
3586 a
->w
= !!strchr(m
, 'w');
3587 a
->m
= !!strchr(m
, 'm');
3589 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3593 int config_parse_io_weight(
3595 const char *filename
,
3597 const char *section
,
3598 unsigned section_line
,
3605 uint64_t *weight
= data
;
3612 r
= cg_weight_parse(rvalue
, weight
);
3614 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3621 int config_parse_io_device_weight(
3623 const char *filename
,
3625 const char *section
,
3626 unsigned section_line
,
3633 _cleanup_free_
char *path
= NULL
;
3634 CGroupIODeviceWeight
*w
;
3635 CGroupContext
*c
= data
;
3645 if (isempty(rvalue
)) {
3646 while (c
->io_device_weights
)
3647 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3652 n
= strcspn(rvalue
, WHITESPACE
);
3653 weight
= rvalue
+ n
;
3654 weight
+= strspn(weight
, WHITESPACE
);
3656 if (isempty(weight
)) {
3657 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3661 path
= strndup(rvalue
, n
);
3665 if (!path_startswith(path
, "/dev")) {
3666 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3670 r
= cg_weight_parse(weight
, &u
);
3672 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3676 assert(u
!= CGROUP_WEIGHT_INVALID
);
3678 w
= new0(CGroupIODeviceWeight
, 1);
3687 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3691 int config_parse_io_limit(
3693 const char *filename
,
3695 const char *section
,
3696 unsigned section_line
,
3703 _cleanup_free_
char *path
= NULL
;
3704 CGroupIODeviceLimit
*l
= NULL
, *t
;
3705 CGroupContext
*c
= data
;
3706 CGroupIOLimitType type
;
3716 type
= cgroup_io_limit_type_from_string(lvalue
);
3719 if (isempty(rvalue
)) {
3720 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3721 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3725 n
= strcspn(rvalue
, WHITESPACE
);
3727 limit
+= strspn(limit
, WHITESPACE
);
3730 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3734 path
= strndup(rvalue
, n
);
3738 if (!path_startswith(path
, "/dev")) {
3739 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3743 if (streq("infinity", limit
)) {
3744 num
= CGROUP_LIMIT_MAX
;
3746 r
= parse_size(limit
, 1000, &num
);
3747 if (r
< 0 || num
<= 0) {
3748 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3753 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3754 if (path_equal(path
, t
->path
)) {
3761 CGroupIOLimitType ttype
;
3763 l
= new0(CGroupIODeviceLimit
, 1);
3769 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3770 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3772 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3775 l
->limits
[type
] = num
;
3780 int config_parse_blockio_weight(
3782 const char *filename
,
3784 const char *section
,
3785 unsigned section_line
,
3792 uint64_t *weight
= data
;
3799 r
= cg_blkio_weight_parse(rvalue
, weight
);
3801 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3808 int config_parse_blockio_device_weight(
3810 const char *filename
,
3812 const char *section
,
3813 unsigned section_line
,
3820 _cleanup_free_
char *path
= NULL
;
3821 CGroupBlockIODeviceWeight
*w
;
3822 CGroupContext
*c
= data
;
3832 if (isempty(rvalue
)) {
3833 while (c
->blockio_device_weights
)
3834 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3839 n
= strcspn(rvalue
, WHITESPACE
);
3840 weight
= rvalue
+ n
;
3841 weight
+= strspn(weight
, WHITESPACE
);
3843 if (isempty(weight
)) {
3844 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3848 path
= strndup(rvalue
, n
);
3852 if (!path_startswith(path
, "/dev")) {
3853 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3857 r
= cg_blkio_weight_parse(weight
, &u
);
3859 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3863 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3865 w
= new0(CGroupBlockIODeviceWeight
, 1);
3874 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3878 int config_parse_blockio_bandwidth(
3880 const char *filename
,
3882 const char *section
,
3883 unsigned section_line
,
3890 _cleanup_free_
char *path
= NULL
;
3891 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3892 CGroupContext
*c
= data
;
3893 const char *bandwidth
;
3903 read
= streq("BlockIOReadBandwidth", lvalue
);
3905 if (isempty(rvalue
)) {
3906 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3907 b
->rbps
= CGROUP_LIMIT_MAX
;
3908 b
->wbps
= CGROUP_LIMIT_MAX
;
3913 n
= strcspn(rvalue
, WHITESPACE
);
3914 bandwidth
= rvalue
+ n
;
3915 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3918 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3922 path
= strndup(rvalue
, n
);
3926 if (!path_startswith(path
, "/dev")) {
3927 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3931 r
= parse_size(bandwidth
, 1000, &bytes
);
3932 if (r
< 0 || bytes
<= 0) {
3933 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3937 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3938 if (path_equal(path
, t
->path
)) {
3945 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3951 b
->rbps
= CGROUP_LIMIT_MAX
;
3952 b
->wbps
= CGROUP_LIMIT_MAX
;
3954 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3965 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3967 int config_parse_job_mode_isolate(
3969 const char *filename
,
3971 const char *section
,
3972 unsigned section_line
,
3986 r
= parse_boolean(rvalue
);
3988 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3992 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3996 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode
, exec_preserve_mode
, ExecPreserveMode
, "Failed to parse runtime directory preserve mode");
3998 int config_parse_exec_directories(
4000 const char *filename
,
4002 const char *section
,
4003 unsigned section_line
,
4020 if (isempty(rvalue
)) {
4021 /* Empty assignment resets the list */
4022 *rt
= strv_free(*rt
);
4026 for (p
= rvalue
;;) {
4027 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
4029 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
4033 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
4034 "Invalid syntax, ignoring: %s", rvalue
);
4040 r
= unit_full_printf(u
, word
, &k
);
4042 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4043 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
4047 if (!path_is_normalized(k
) || path_is_absolute(k
)) {
4048 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
4049 "%s= path is not valid, ignoring assignment: %s", lvalue
, rvalue
);
4053 r
= strv_push(rt
, k
);
4060 int config_parse_set_status(
4062 const char *filename
,
4064 const char *section
,
4065 unsigned section_line
,
4073 const char *word
, *state
;
4075 ExitStatusSet
*status_set
= data
;
4082 /* Empty assignment resets the list */
4083 if (isempty(rvalue
)) {
4084 exit_status_set_free(status_set
);
4088 FOREACH_WORD(word
, l
, rvalue
, state
) {
4089 _cleanup_free_
char *temp
;
4093 temp
= strndup(word
, l
);
4097 r
= safe_atoi(temp
, &val
);
4099 val
= signal_from_string_try_harder(temp
);
4102 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
4105 set
= &status_set
->signal
;
4107 if (val
< 0 || val
> 255) {
4108 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
4111 set
= &status_set
->status
;
4114 r
= set_ensure_allocated(set
, NULL
);
4118 r
= set_put(*set
, INT_TO_PTR(val
));
4120 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
4124 if (!isempty(state
))
4125 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
4130 int config_parse_namespace_path_strv(
4132 const char *filename
,
4134 const char *section
,
4135 unsigned section_line
,
4152 if (isempty(rvalue
)) {
4153 /* Empty assignment resets the list */
4154 *sv
= strv_free(*sv
);
4160 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
4162 bool ignore_enoent
= false, shall_prefix
= false;
4164 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
4170 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
4174 if (!utf8_is_valid(word
)) {
4175 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
4180 if (startswith(w
, "-")) {
4181 ignore_enoent
= true;
4184 if (startswith(w
, "+")) {
4185 shall_prefix
= true;
4189 r
= unit_full_printf(u
, w
, &resolved
);
4191 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s: %m", word
);
4195 if (!path_is_absolute(resolved
)) {
4196 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
4200 path_kill_slashes(resolved
);
4202 joined
= strjoin(ignore_enoent
? "-" : "",
4203 shall_prefix
? "+" : "",
4206 r
= strv_push(sv
, joined
);
4216 int config_parse_bind_paths(
4218 const char *filename
,
4220 const char *section
,
4221 unsigned section_line
,
4228 ExecContext
*c
= data
;
4238 if (isempty(rvalue
)) {
4239 /* Empty assignment resets the list */
4240 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
4241 c
->bind_mounts
= NULL
;
4242 c
->n_bind_mounts
= 0;
4248 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
4249 _cleanup_free_
char *sresolved
= NULL
, *dresolved
= NULL
;
4250 char *s
= NULL
, *d
= NULL
;
4251 bool rbind
= true, ignore_enoent
= false;
4253 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4259 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4263 r
= unit_full_printf(u
, source
, &sresolved
);
4265 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4266 "Failed to resolved specifiers in \"%s\", ignoring: %m", source
);
4272 ignore_enoent
= true;
4276 if (!utf8_is_valid(s
)) {
4277 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, s
);
4280 if (!path_is_absolute(s
)) {
4281 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute source path, ignoring: %s", s
);
4285 path_kill_slashes(s
);
4287 /* Optionally, the destination is specified. */
4288 if (p
&& p
[-1] == ':') {
4289 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4293 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4297 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':': %s", rvalue
);
4301 r
= unit_full_printf(u
, destination
, &dresolved
);
4303 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4304 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination
);
4308 if (!utf8_is_valid(dresolved
)) {
4309 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, dresolved
);
4312 if (!path_is_absolute(dresolved
)) {
4313 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute destination path, ignoring: %s", dresolved
);
4317 d
= path_kill_slashes(dresolved
);
4319 /* Optionally, there's also a short option string specified */
4320 if (p
&& p
[-1] == ':') {
4321 _cleanup_free_
char *options
= NULL
;
4323 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
4327 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4331 if (isempty(options
) || streq(options
, "rbind"))
4333 else if (streq(options
, "norbind"))
4336 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
4343 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
4347 .read_only
= !!strstr(lvalue
, "ReadOnly"),
4349 .ignore_enoent
= ignore_enoent
,
4358 int config_parse_no_new_privileges(
4360 const char *filename
,
4362 const char *section
,
4363 unsigned section_line
,
4370 ExecContext
*c
= data
;
4378 k
= parse_boolean(rvalue
);
4380 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
4384 c
->no_new_privileges
= k
;
4389 int config_parse_protect_home(
4391 const char *filename
,
4393 const char *section
,
4394 unsigned section_line
,
4401 ExecContext
*c
= data
;
4409 /* Our enum shall be a superset of booleans, hence first try
4410 * to parse as boolean, and then as enum */
4412 k
= parse_boolean(rvalue
);
4414 c
->protect_home
= PROTECT_HOME_YES
;
4416 c
->protect_home
= PROTECT_HOME_NO
;
4420 h
= protect_home_from_string(rvalue
);
4422 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
4426 c
->protect_home
= h
;
4432 int config_parse_protect_system(
4434 const char *filename
,
4436 const char *section
,
4437 unsigned section_line
,
4444 ExecContext
*c
= data
;
4452 /* Our enum shall be a superset of booleans, hence first try
4453 * to parse as boolean, and then as enum */
4455 k
= parse_boolean(rvalue
);
4457 c
->protect_system
= PROTECT_SYSTEM_YES
;
4459 c
->protect_system
= PROTECT_SYSTEM_NO
;
4463 s
= protect_system_from_string(rvalue
);
4465 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
4469 c
->protect_system
= s
;
4475 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode
, exec_keyring_mode
, ExecKeyringMode
, "Failed to parse keyring mode");
4477 int config_parse_job_timeout_sec(
4479 const char *filename
,
4481 const char *section
,
4482 unsigned section_line
,
4498 r
= parse_sec_fix_0(rvalue
, &usec
);
4500 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue
);
4504 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
4505 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
4508 if (!u
->job_running_timeout_set
)
4509 u
->job_running_timeout
= usec
;
4511 u
->job_timeout
= usec
;
4516 int config_parse_job_running_timeout_sec(
4518 const char *filename
,
4520 const char *section
,
4521 unsigned section_line
,
4537 r
= parse_sec_fix_0(rvalue
, &usec
);
4539 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue
);
4543 u
->job_running_timeout
= usec
;
4544 u
->job_running_timeout_set
= true;
4549 #define FOLLOW_MAX 8
4551 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4562 /* This will update the filename pointer if the loaded file is
4563 * reached by a symlink. The old string will be freed. */
4566 char *target
, *name
;
4568 if (c
++ >= FOLLOW_MAX
)
4571 path_kill_slashes(*filename
);
4573 /* Add the file name we are currently looking at to
4574 * the names of this unit, but only if it is a valid
4576 name
= basename(*filename
);
4577 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4579 id
= set_get(names
, name
);
4585 r
= set_consume(names
, id
);
4591 /* Try to open the file name, but don't if its a symlink */
4592 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4599 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4600 r
= readlink_and_make_absolute(*filename
, &target
);
4608 f
= fdopen(fd
, "re");
4620 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4628 /* Let's try to add in all symlink names we found */
4629 while ((k
= set_steal_first(names
))) {
4631 /* First try to merge in the other name into our
4633 r
= unit_merge_by_name(*u
, k
);
4637 /* Hmm, we couldn't merge the other unit into
4638 * ours? Then let's try it the other way
4641 /* If the symlink name we are looking at is unit template, then
4642 we must search for instance of this template */
4643 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4644 _cleanup_free_
char *instance
= NULL
;
4646 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4650 other
= manager_get_unit((*u
)->manager
, instance
);
4652 other
= manager_get_unit((*u
)->manager
, k
);
4657 r
= unit_merge(other
, *u
);
4660 return merge_by_names(u
, names
, NULL
);
4668 unit_choose_id(*u
, id
);
4676 static int load_from_path(Unit
*u
, const char *path
) {
4677 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4678 _cleanup_fclose_
FILE *f
= NULL
;
4679 _cleanup_free_
char *filename
= NULL
;
4688 symlink_names
= set_new(&string_hash_ops
);
4692 if (path_is_absolute(path
)) {
4694 filename
= strdup(path
);
4698 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4700 filename
= mfree(filename
);
4708 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4710 /* Instead of opening the path right away, we manually
4711 * follow all symlinks and add their name to our unit
4712 * name set while doing so */
4713 filename
= path_make_absolute(path
, *p
);
4717 if (u
->manager
->unit_path_cache
&&
4718 !set_get(u
->manager
->unit_path_cache
, filename
))
4721 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4724 filename
= mfree(filename
);
4726 /* ENOENT means that the file is missing or is a dangling symlink.
4727 * ENOTDIR means that one of paths we expect to be is a directory
4728 * is not a directory, we should just ignore that.
4729 * EACCES means that the directory or file permissions are wrong.
4732 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4733 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4736 /* Empty the symlink names for the next run */
4737 set_clear_free(symlink_names
);
4742 /* Hmm, no suitable file found? */
4745 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4746 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4751 r
= merge_by_names(&merged
, symlink_names
, id
);
4756 u
->load_state
= UNIT_MERGED
;
4760 if (fstat(fileno(f
), &st
) < 0)
4763 if (null_or_empty(&st
)) {
4764 u
->load_state
= UNIT_MASKED
;
4765 u
->fragment_mtime
= 0;
4767 u
->load_state
= UNIT_LOADED
;
4768 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4770 /* Now, parse the file contents */
4771 r
= config_parse(u
->id
, filename
, f
,
4772 UNIT_VTABLE(u
)->sections
,
4773 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4774 CONFIG_PARSE_ALLOW_INCLUDE
, u
);
4779 free(u
->fragment_path
);
4780 u
->fragment_path
= filename
;
4783 if (u
->source_path
) {
4784 if (stat(u
->source_path
, &st
) >= 0)
4785 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4787 u
->source_mtime
= 0;
4793 int unit_load_fragment(Unit
*u
) {
4799 assert(u
->load_state
== UNIT_STUB
);
4803 u
->load_state
= UNIT_LOADED
;
4807 /* First, try to find the unit under its id. We always look
4808 * for unit files in the default directories, to make it easy
4809 * to override things by placing things in /etc/systemd/system */
4810 r
= load_from_path(u
, u
->id
);
4814 /* Try to find an alias we can load this with */
4815 if (u
->load_state
== UNIT_STUB
) {
4816 SET_FOREACH(t
, u
->names
, i
) {
4821 r
= load_from_path(u
, t
);
4825 if (u
->load_state
!= UNIT_STUB
)
4830 /* And now, try looking for it under the suggested (originally linked) path */
4831 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4833 r
= load_from_path(u
, u
->fragment_path
);
4837 if (u
->load_state
== UNIT_STUB
)
4838 /* Hmm, this didn't work? Then let's get rid
4839 * of the fragment path stored for us, so that
4840 * we don't point to an invalid location. */
4841 u
->fragment_path
= mfree(u
->fragment_path
);
4844 /* Look for a template */
4845 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4846 _cleanup_free_
char *k
= NULL
;
4848 r
= unit_name_template(u
->id
, &k
);
4852 r
= load_from_path(u
, k
);
4855 log_unit_notice(u
, "Unit configuration has fatal error, unit will not be started.");
4859 if (u
->load_state
== UNIT_STUB
) {
4860 SET_FOREACH(t
, u
->names
, i
) {
4861 _cleanup_free_
char *z
= NULL
;
4866 r
= unit_name_template(t
, &z
);
4870 r
= load_from_path(u
, z
);
4874 if (u
->load_state
!= UNIT_STUB
)
4883 void unit_dump_config_items(FILE *f
) {
4884 static const struct {
4885 const ConfigParserCallback callback
;
4888 #if !HAVE_SYSV_COMPAT || !HAVE_SECCOMP || !HAVE_PAM || !HAVE_SELINUX || !ENABLE_SMACK || !HAVE_APPARMOR
4889 { config_parse_warn_compat
, "NOTSUPPORTED" },
4891 { config_parse_int
, "INTEGER" },
4892 { config_parse_unsigned
, "UNSIGNED" },
4893 { config_parse_iec_size
, "SIZE" },
4894 { config_parse_iec_uint64
, "SIZE" },
4895 { config_parse_si_size
, "SIZE" },
4896 { config_parse_bool
, "BOOLEAN" },
4897 { config_parse_string
, "STRING" },
4898 { config_parse_path
, "PATH" },
4899 { config_parse_unit_path_printf
, "PATH" },
4900 { config_parse_strv
, "STRING [...]" },
4901 { config_parse_exec_nice
, "NICE" },
4902 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4903 { config_parse_exec_io_class
, "IOCLASS" },
4904 { config_parse_exec_io_priority
, "IOPRIORITY" },
4905 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4906 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4907 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4908 { config_parse_mode
, "MODE" },
4909 { config_parse_unit_env_file
, "FILE" },
4910 { config_parse_exec_output
, "OUTPUT" },
4911 { config_parse_exec_input
, "INPUT" },
4912 { config_parse_log_facility
, "FACILITY" },
4913 { config_parse_log_level
, "LEVEL" },
4914 { config_parse_exec_secure_bits
, "SECUREBITS" },
4915 { config_parse_capability_set
, "BOUNDINGSET" },
4916 { config_parse_limit
, "LIMIT" },
4917 { config_parse_unit_deps
, "UNIT [...]" },
4918 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4919 { config_parse_service_type
, "SERVICETYPE" },
4920 { config_parse_service_restart
, "SERVICERESTART" },
4921 #if HAVE_SYSV_COMPAT
4922 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4924 { config_parse_kill_mode
, "KILLMODE" },
4925 { config_parse_signal
, "SIGNAL" },
4926 { config_parse_socket_listen
, "SOCKET [...]" },
4927 { config_parse_socket_bind
, "SOCKETBIND" },
4928 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4929 { config_parse_sec
, "SECONDS" },
4930 { config_parse_nsec
, "NANOSECONDS" },
4931 { config_parse_namespace_path_strv
, "PATH [...]" },
4932 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4933 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4934 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4935 { config_parse_unit_string_printf
, "STRING" },
4936 { config_parse_trigger_unit
, "UNIT" },
4937 { config_parse_timer
, "TIMER" },
4938 { config_parse_path_spec
, "PATH" },
4939 { config_parse_notify_access
, "ACCESS" },
4940 { config_parse_ip_tos
, "TOS" },
4941 { config_parse_unit_condition_path
, "CONDITION" },
4942 { config_parse_unit_condition_string
, "CONDITION" },
4943 { config_parse_unit_condition_null
, "CONDITION" },
4944 { config_parse_unit_slice
, "SLICE" },
4945 { config_parse_documentation
, "URL" },
4946 { config_parse_service_timeout
, "SECONDS" },
4947 { config_parse_emergency_action
, "ACTION" },
4948 { config_parse_set_status
, "STATUS" },
4949 { config_parse_service_sockets
, "SOCKETS" },
4950 { config_parse_environ
, "ENVIRON" },
4952 { config_parse_syscall_filter
, "SYSCALLS" },
4953 { config_parse_syscall_archs
, "ARCHS" },
4954 { config_parse_syscall_errno
, "ERRNO" },
4955 { config_parse_address_families
, "FAMILIES" },
4956 { config_parse_restrict_namespaces
, "NAMESPACES" },
4958 { config_parse_cpu_shares
, "SHARES" },
4959 { config_parse_cpu_weight
, "WEIGHT" },
4960 { config_parse_memory_limit
, "LIMIT" },
4961 { config_parse_device_allow
, "DEVICE" },
4962 { config_parse_device_policy
, "POLICY" },
4963 { config_parse_io_limit
, "LIMIT" },
4964 { config_parse_io_weight
, "WEIGHT" },
4965 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4966 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4967 { config_parse_blockio_weight
, "WEIGHT" },
4968 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4969 { config_parse_long
, "LONG" },
4970 { config_parse_socket_service
, "SERVICE" },
4972 { config_parse_exec_selinux_context
, "LABEL" },
4974 { config_parse_job_mode
, "MODE" },
4975 { config_parse_job_mode_isolate
, "BOOLEAN" },
4976 { config_parse_personality
, "PERSONALITY" },
4979 const char *prev
= NULL
;
4984 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4985 const char *rvalue
= "OTHER", *lvalue
;
4989 const ConfigPerfItem
*p
;
4991 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4993 dot
= strchr(i
, '.');
4994 lvalue
= dot
? dot
+ 1 : i
;
4998 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
5002 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
5005 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
5006 if (p
->parse
== table
[j
].callback
) {
5007 rvalue
= table
[j
].rvalue
;
5011 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);