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 "socket-protocol-list.h"
68 #include "stat-util.h"
69 #include "string-util.h"
71 #include "unit-name.h"
72 #include "unit-printf.h"
74 #include "user-util.h"
78 DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode
, collect_mode
, CollectMode
, "Failed to parse garbage collection mode");
80 int config_parse_unit_deps(
85 unsigned section_line
,
92 UnitDependency d
= ltype
;
102 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
105 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_RETAIN_ESCAPE
);
111 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
115 r
= unit_name_printf(u
, word
, &k
);
117 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
121 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
123 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
129 int config_parse_obsolete_unit_deps(
131 const char *filename
,
134 unsigned section_line
,
141 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
142 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue
, unit_dependency_to_string(ltype
));
144 return config_parse_unit_deps(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, data
, userdata
);
147 int config_parse_unit_string_printf(
149 const char *filename
,
152 unsigned section_line
,
159 _cleanup_free_
char *k
= NULL
;
168 r
= unit_full_printf(u
, rvalue
, &k
);
170 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
174 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
177 int config_parse_unit_strv_printf(
179 const char *filename
,
182 unsigned section_line
,
190 _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_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
207 int config_parse_unit_path_printf(
209 const char *filename
,
212 unsigned section_line
,
219 _cleanup_free_
char *k
= NULL
;
229 /* Let's not bother with anything that is too long */
230 if (strlen(rvalue
) >= PATH_MAX
) {
231 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
232 "%s value too long%s.",
233 lvalue
, fatal
? "" : ", ignoring");
234 return fatal
? -ENAMETOOLONG
: 0;
237 r
= unit_full_printf(u
, rvalue
, &k
);
239 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
240 "Failed to resolve unit specifiers in \"%s\"%s: %m",
241 rvalue
, fatal
? "" : ", ignoring");
242 return fatal
? -ENOEXEC
: 0;
245 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
248 int config_parse_unit_path_strv_printf(
250 const char *filename
,
253 unsigned section_line
,
270 if (isempty(rvalue
)) {
276 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
278 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
284 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
285 "Invalid syntax, ignoring: %s", rvalue
);
289 r
= unit_full_printf(u
, word
, &k
);
291 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
292 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
296 if (!utf8_is_valid(k
)) {
297 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
301 if (!path_is_absolute(k
)) {
302 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
303 "Symlink path is not absolute: %s", k
);
307 path_kill_slashes(k
);
316 int config_parse_socket_listen(const char *unit
,
317 const char *filename
,
320 unsigned section_line
,
327 _cleanup_free_ SocketPort
*p
= NULL
;
339 if (isempty(rvalue
)) {
340 /* An empty assignment removes all ports */
341 socket_free_ports(s
);
345 p
= new0(SocketPort
, 1);
349 if (ltype
!= SOCKET_SOCKET
) {
352 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
354 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
358 path_kill_slashes(p
->path
);
360 } else if (streq(lvalue
, "ListenNetlink")) {
361 _cleanup_free_
char *k
= NULL
;
363 p
->type
= SOCKET_SOCKET
;
364 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
366 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
370 r
= socket_address_parse_netlink(&p
->address
, k
);
372 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
377 _cleanup_free_
char *k
= NULL
;
379 p
->type
= SOCKET_SOCKET
;
380 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
382 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
386 r
= socket_address_parse_and_warn(&p
->address
, k
);
388 if (r
!= -EAFNOSUPPORT
)
389 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
393 if (streq(lvalue
, "ListenStream"))
394 p
->address
.type
= SOCK_STREAM
;
395 else if (streq(lvalue
, "ListenDatagram"))
396 p
->address
.type
= SOCK_DGRAM
;
398 assert(streq(lvalue
, "ListenSequentialPacket"));
399 p
->address
.type
= SOCK_SEQPACKET
;
402 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
403 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
409 p
->auxiliary_fds
= NULL
;
410 p
->n_auxiliary_fds
= 0;
413 LIST_FIND_TAIL(port
, s
->ports
, tail
);
414 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
421 int config_parse_socket_protocol(const char *unit
,
422 const char *filename
,
425 unsigned section_line
,
441 r
= socket_protocol_from_name(rvalue
);
443 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid socket protocol, ignoring: %s", rvalue
);
445 } else if (!IN_SET(r
, IPPROTO_UDPLITE
, IPPROTO_SCTP
)) {
446 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
450 s
->socket_protocol
= r
;
455 int config_parse_socket_bind(const char *unit
,
456 const char *filename
,
459 unsigned section_line
,
467 SocketAddressBindIPv6Only b
;
476 b
= parse_socket_address_bind_ipv6_only_or_bool(rvalue
);
478 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
482 s
->bind_ipv6_only
= b
;
487 int config_parse_exec_nice(
489 const char *filename
,
492 unsigned section_line
,
499 ExecContext
*c
= data
;
507 r
= parse_nice(rvalue
, &priority
);
510 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
512 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
523 int config_parse_exec_oom_score_adjust(const char* unit
,
524 const char *filename
,
527 unsigned section_line
,
534 ExecContext
*c
= data
;
542 r
= safe_atoi(rvalue
, &oa
);
544 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
548 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
549 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
553 c
->oom_score_adjust
= oa
;
554 c
->oom_score_adjust_set
= true;
559 int config_parse_exec(
561 const char *filename
,
564 unsigned section_line
,
571 ExecCommand
**e
= data
;
583 rvalue
+= strspn(rvalue
, WHITESPACE
);
585 if (isempty(rvalue
)) {
586 /* An empty assignment resets the list */
587 *e
= exec_command_free_list(*e
);
593 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
594 ExecCommandFlags flags
= 0;
595 bool ignore
= false, separate_argv0
= false;
596 _cleanup_free_ ExecCommand
*nce
= NULL
;
597 _cleanup_strv_free_
char **n
= NULL
;
598 size_t nlen
= 0, nbufsize
= 0;
603 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
609 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
610 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
611 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
612 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
613 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
614 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
615 * other sandboxing, with some special exceptions for changing UID.
617 * The idea is that '!!' may be used to write services that can take benefit of systemd's
618 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
619 * privilege dropping within the daemon if the kernel does not offer that. */
621 if (*f
== '-' && !(flags
& EXEC_COMMAND_IGNORE_FAILURE
)) {
622 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
624 } else if (*f
== '@' && !separate_argv0
)
625 separate_argv0
= true;
626 else if (*f
== '+' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
627 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
628 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
629 flags
|= EXEC_COMMAND_NO_SETUID
;
630 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
631 flags
&= ~EXEC_COMMAND_NO_SETUID
;
632 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
638 r
= unit_full_printf(u
, f
, &path
);
640 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
641 "Failed to resolve unit specifiers on %s%s: %m",
642 f
, ignore
? ", ignoring" : "");
643 return ignore
? 0 : -ENOEXEC
;
647 /* First word is either "-" or "@" with no command. */
648 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
649 "Empty path in command line%s: \"%s\"",
650 ignore
? ", ignoring" : "", rvalue
);
651 return ignore
? 0 : -ENOEXEC
;
653 if (!string_is_safe(path
)) {
654 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
655 "Executable path contains special characters%s: %s",
656 ignore
? ", ignoring" : "", rvalue
);
657 return ignore
? 0 : -ENOEXEC
;
659 if (!path_is_absolute(path
)) {
660 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
661 "Executable path is not absolute%s: %s",
662 ignore
? ", ignoring" : "", rvalue
);
663 return ignore
? 0 : -ENOEXEC
;
665 if (endswith(path
, "/")) {
666 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
667 "Executable path specifies a directory%s: %s",
668 ignore
? ", ignoring" : "", rvalue
);
669 return ignore
? 0 : -ENOEXEC
;
672 if (!separate_argv0
) {
675 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
685 path_kill_slashes(path
);
687 while (!isempty(p
)) {
688 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
690 /* Check explicitly for an unquoted semicolon as
691 * command separator token. */
692 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
694 p
+= strspn(p
, WHITESPACE
);
699 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
700 * extract_first_word() would return the same for all of those. */
701 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
705 p
+= strspn(p
, WHITESPACE
);
707 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
718 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
722 return ignore
? 0 : -ENOEXEC
;
724 r
= unit_full_printf(u
, word
, &resolved
);
726 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
727 "Failed to resolve unit specifiers on %s%s: %m",
728 word
, ignore
? ", ignoring" : "");
729 return ignore
? 0 : -ENOEXEC
;
732 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
734 n
[nlen
++] = resolved
;
740 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
741 "Empty executable name or zeroeth argument%s: %s",
742 ignore
? ", ignoring" : "", rvalue
);
743 return ignore
? 0 : -ENOEXEC
;
746 nce
= new0(ExecCommand
, 1);
754 exec_command_append_list(e
, nce
);
756 /* Do not _cleanup_free_ these. */
767 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
768 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
770 int config_parse_socket_bindtodevice(
772 const char *filename
,
775 unsigned section_line
,
790 if (rvalue
[0] && !streq(rvalue
, "*")) {
791 if (!ifname_valid(rvalue
)) {
792 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
802 free(s
->bind_to_device
);
803 s
->bind_to_device
= n
;
808 int config_parse_exec_input(
810 const char *filename
,
813 unsigned section_line
,
820 ExecContext
*c
= data
;
831 n
= startswith(rvalue
, "fd:");
833 _cleanup_free_
char *resolved
= NULL
;
835 r
= unit_full_printf(u
, n
, &resolved
);
837 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
839 if (isempty(resolved
))
840 resolved
= mfree(resolved
);
841 else if (!fdname_is_valid(resolved
)) {
842 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name: %s", resolved
);
846 free_and_replace(c
->stdio_fdname
[STDIN_FILENO
], resolved
);
848 ei
= EXEC_INPUT_NAMED_FD
;
850 } else if ((n
= startswith(rvalue
, "file:"))) {
851 _cleanup_free_
char *resolved
= NULL
;
853 r
= unit_full_printf(u
, n
, &resolved
);
855 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
857 if (!path_is_absolute(resolved
)) {
858 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires an absolute path name: %s", resolved
);
862 if (!path_is_normalized(resolved
)) {
863 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires a normalized path name: %s", resolved
);
867 free_and_replace(c
->stdio_file
[STDIN_FILENO
], resolved
);
869 ei
= EXEC_INPUT_FILE
;
872 ei
= exec_input_from_string(rvalue
);
874 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
883 int config_parse_exec_input_text(
885 const char *filename
,
888 unsigned section_line
,
895 _cleanup_free_
char *unescaped
= NULL
, *resolved
= NULL
;
896 ExecContext
*c
= data
;
907 if (isempty(rvalue
)) {
908 /* Reset if the empty string is assigned */
909 c
->stdin_data
= mfree(c
->stdin_data
);
910 c
->stdin_data_size
= 0;
914 r
= cunescape(rvalue
, 0, &unescaped
);
916 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to decode C escaped text: %s", rvalue
);
918 r
= unit_full_printf(u
, unescaped
, &resolved
);
920 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", unescaped
);
922 sz
= strlen(resolved
);
923 if (c
->stdin_data_size
+ sz
+ 1 < c
->stdin_data_size
|| /* check for overflow */
924 c
->stdin_data_size
+ sz
+ 1 > EXEC_STDIN_DATA_MAX
) {
925 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
);
929 p
= realloc(c
->stdin_data
, c
->stdin_data_size
+ sz
+ 1);
933 *((char*) mempcpy((char*) p
+ c
->stdin_data_size
, resolved
, sz
)) = '\n';
936 c
->stdin_data_size
+= sz
+ 1;
941 int config_parse_exec_input_data(
943 const char *filename
,
946 unsigned section_line
,
953 _cleanup_free_
void *p
= NULL
;
954 ExecContext
*c
= data
;
964 if (isempty(rvalue
)) {
965 /* Reset if the empty string is assigned */
966 c
->stdin_data
= mfree(c
->stdin_data
);
967 c
->stdin_data_size
= 0;
971 r
= unbase64mem(rvalue
, (size_t) -1, &p
, &sz
);
973 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to decode base64 data, ignoring: %s", rvalue
);
977 if (c
->stdin_data_size
+ sz
< c
->stdin_data_size
|| /* check for overflow */
978 c
->stdin_data_size
+ sz
> EXEC_STDIN_DATA_MAX
) {
979 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
);
983 q
= realloc(c
->stdin_data
, c
->stdin_data_size
+ sz
);
987 memcpy((uint8_t*) q
+ c
->stdin_data_size
, p
, sz
);
990 c
->stdin_data_size
+= sz
;
995 int config_parse_exec_output(
997 const char *filename
,
1000 unsigned section_line
,
1007 _cleanup_free_
char *resolved
= NULL
;
1009 ExecContext
*c
= data
;
1020 n
= startswith(rvalue
, "fd:");
1022 r
= unit_full_printf(u
, n
, &resolved
);
1024 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
1026 if (isempty(resolved
))
1027 resolved
= mfree(resolved
);
1028 else if (!fdname_is_valid(resolved
)) {
1029 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name: %s", resolved
);
1033 eo
= EXEC_OUTPUT_NAMED_FD
;
1035 } else if ((n
= startswith(rvalue
, "file:"))) {
1037 r
= unit_full_printf(u
, n
, &resolved
);
1039 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
1041 if (!path_is_absolute(resolved
)) {
1042 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires an absolute path name: %s", resolved
);
1046 if (!path_is_normalized(resolved
)) {
1047 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires a normalized path name, ignoring: %s", resolved
);
1051 eo
= EXEC_OUTPUT_FILE
;
1054 eo
= exec_output_from_string(rvalue
);
1056 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
1061 if (streq(lvalue
, "StandardOutput")) {
1062 if (eo
== EXEC_OUTPUT_NAMED_FD
)
1063 free_and_replace(c
->stdio_fdname
[STDOUT_FILENO
], resolved
);
1065 free_and_replace(c
->stdio_file
[STDOUT_FILENO
], resolved
);
1070 assert(streq(lvalue
, "StandardError"));
1072 if (eo
== EXEC_OUTPUT_NAMED_FD
)
1073 free_and_replace(c
->stdio_fdname
[STDERR_FILENO
], resolved
);
1075 free_and_replace(c
->stdio_file
[STDERR_FILENO
], resolved
);
1083 int config_parse_exec_io_class(const char *unit
,
1084 const char *filename
,
1086 const char *section
,
1087 unsigned section_line
,
1094 ExecContext
*c
= data
;
1102 x
= ioprio_class_from_string(rvalue
);
1104 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
1108 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
1109 c
->ioprio_set
= true;
1114 int config_parse_exec_io_priority(const char *unit
,
1115 const char *filename
,
1117 const char *section
,
1118 unsigned section_line
,
1125 ExecContext
*c
= data
;
1133 r
= ioprio_parse_priority(rvalue
, &i
);
1135 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
1139 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
1140 c
->ioprio_set
= true;
1145 int config_parse_exec_cpu_sched_policy(const char *unit
,
1146 const char *filename
,
1148 const char *section
,
1149 unsigned section_line
,
1157 ExecContext
*c
= data
;
1165 x
= sched_policy_from_string(rvalue
);
1167 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1171 c
->cpu_sched_policy
= x
;
1172 /* Moving to or from real-time policy? We need to adjust the priority */
1173 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
1174 c
->cpu_sched_set
= true;
1179 int config_parse_exec_cpu_sched_prio(const char *unit
,
1180 const char *filename
,
1182 const char *section
,
1183 unsigned section_line
,
1190 ExecContext
*c
= data
;
1198 r
= safe_atoi(rvalue
, &i
);
1200 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1204 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1205 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1206 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1208 if (i
< min
|| i
> max
) {
1209 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1213 c
->cpu_sched_priority
= i
;
1214 c
->cpu_sched_set
= true;
1219 int config_parse_exec_cpu_affinity(const char *unit
,
1220 const char *filename
,
1222 const char *section
,
1223 unsigned section_line
,
1230 ExecContext
*c
= data
;
1231 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1239 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1244 /* An empty assignment resets the CPU list */
1245 c
->cpuset
= cpu_set_mfree(c
->cpuset
);
1246 c
->cpuset_ncpus
= 0;
1251 c
->cpuset
= TAKE_PTR(cpuset
);
1252 c
->cpuset_ncpus
= (unsigned) ncpus
;
1256 if (c
->cpuset_ncpus
< (unsigned) ncpus
) {
1257 CPU_OR_S(CPU_ALLOC_SIZE(c
->cpuset_ncpus
), cpuset
, c
->cpuset
, cpuset
);
1258 CPU_FREE(c
->cpuset
);
1259 c
->cpuset
= TAKE_PTR(cpuset
);
1260 c
->cpuset_ncpus
= (unsigned) ncpus
;
1264 CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus
), c
->cpuset
, c
->cpuset
, cpuset
);
1269 int config_parse_exec_secure_bits(const char *unit
,
1270 const char *filename
,
1272 const char *section
,
1273 unsigned section_line
,
1280 ExecContext
*c
= data
;
1288 if (isempty(rvalue
)) {
1289 /* An empty assignment resets the field */
1294 r
= secure_bits_from_string(rvalue
);
1298 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1299 "Invalid syntax, ignoring: %s", rvalue
);
1308 int config_parse_capability_set(
1310 const char *filename
,
1312 const char *section
,
1313 unsigned section_line
,
1320 uint64_t *capability_set
= data
;
1321 uint64_t sum
= 0, initial
= 0;
1322 bool invert
= false;
1330 if (rvalue
[0] == '~') {
1335 if (streq(lvalue
, "CapabilityBoundingSet"))
1336 initial
= CAP_ALL
; /* initialized to all bits on */
1337 /* else "AmbientCapabilities" initialized to all bits off */
1339 r
= capability_set_from_string(rvalue
, &sum
);
1343 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word: %s", rvalue
);
1347 if (sum
== 0 || *capability_set
== initial
)
1348 /* "", "~" or uninitialized data -> replace */
1349 *capability_set
= invert
? ~sum
: sum
;
1351 /* previous data -> merge */
1353 *capability_set
&= ~sum
;
1355 *capability_set
|= sum
;
1361 int config_parse_limit(
1363 const char *filename
,
1365 const char *section
,
1366 unsigned section_line
,
1373 struct rlimit
**rl
= data
, d
= {};
1381 r
= rlimit_parse(ltype
, rvalue
, &d
);
1383 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1387 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1394 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1402 #if HAVE_SYSV_COMPAT
1403 int config_parse_sysv_priority(const char *unit
,
1404 const char *filename
,
1406 const char *section
,
1407 unsigned section_line
,
1414 int *priority
= data
;
1422 r
= safe_atoi(rvalue
, &i
);
1423 if (r
< 0 || i
< 0) {
1424 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1428 *priority
= (int) i
;
1433 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1434 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1436 int config_parse_exec_mount_flags(
1438 const char *filename
,
1440 const char *section
,
1441 unsigned section_line
,
1449 ExecContext
*c
= data
;
1457 r
= mount_propagation_flags_from_string(rvalue
, &c
->mount_flags
);
1459 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1464 int config_parse_exec_selinux_context(
1466 const char *filename
,
1468 const char *section
,
1469 unsigned section_line
,
1476 ExecContext
*c
= data
;
1487 if (isempty(rvalue
)) {
1488 c
->selinux_context
= mfree(c
->selinux_context
);
1489 c
->selinux_context_ignore
= false;
1493 if (rvalue
[0] == '-') {
1499 r
= unit_full_printf(u
, rvalue
, &k
);
1501 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1502 "Failed to resolve specifiers%s: %m",
1503 ignore
? ", ignoring" : "");
1504 return ignore
? 0 : -ENOEXEC
;
1507 free(c
->selinux_context
);
1508 c
->selinux_context
= k
;
1509 c
->selinux_context_ignore
= ignore
;
1514 int config_parse_exec_apparmor_profile(
1516 const char *filename
,
1518 const char *section
,
1519 unsigned section_line
,
1526 ExecContext
*c
= data
;
1537 if (isempty(rvalue
)) {
1538 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1539 c
->apparmor_profile_ignore
= false;
1543 if (rvalue
[0] == '-') {
1549 r
= unit_full_printf(u
, rvalue
, &k
);
1551 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1552 "Failed to resolve specifiers%s: %m",
1553 ignore
? ", ignoring" : "");
1554 return ignore
? 0 : -ENOEXEC
;
1557 free(c
->apparmor_profile
);
1558 c
->apparmor_profile
= k
;
1559 c
->apparmor_profile_ignore
= ignore
;
1564 int config_parse_exec_smack_process_label(
1566 const char *filename
,
1568 const char *section
,
1569 unsigned section_line
,
1576 ExecContext
*c
= data
;
1587 if (isempty(rvalue
)) {
1588 c
->smack_process_label
= mfree(c
->smack_process_label
);
1589 c
->smack_process_label_ignore
= false;
1593 if (rvalue
[0] == '-') {
1599 r
= unit_full_printf(u
, rvalue
, &k
);
1601 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1602 "Failed to resolve specifiers%s: %m",
1603 ignore
? ", ignoring" : "");
1604 return ignore
? 0 : -ENOEXEC
;
1607 free(c
->smack_process_label
);
1608 c
->smack_process_label
= k
;
1609 c
->smack_process_label_ignore
= ignore
;
1614 int config_parse_timer(const char *unit
,
1615 const char *filename
,
1617 const char *section
,
1618 unsigned section_line
,
1629 CalendarSpec
*c
= NULL
;
1631 _cleanup_free_
char *k
= NULL
;
1639 if (isempty(rvalue
)) {
1640 /* Empty assignment resets list */
1641 timer_free_values(t
);
1645 b
= timer_base_from_string(lvalue
);
1647 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1651 r
= unit_full_printf(u
, rvalue
, &k
);
1653 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1657 if (b
== TIMER_CALENDAR
) {
1658 if (calendar_spec_from_string(k
, &c
) < 0) {
1659 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1663 if (parse_sec(k
, &usec
) < 0) {
1664 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1669 v
= new0(TimerValue
, 1);
1671 calendar_spec_free(c
);
1677 v
->calendar_spec
= c
;
1679 LIST_PREPEND(value
, t
->values
, v
);
1684 int config_parse_trigger_unit(
1686 const char *filename
,
1688 const char *section
,
1689 unsigned section_line
,
1696 _cleanup_free_
char *p
= NULL
;
1706 if (!hashmap_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1707 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1711 r
= unit_name_printf(u
, rvalue
, &p
);
1713 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1717 type
= unit_name_to_type(p
);
1719 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1722 if (unit_has_name(u
, p
)) {
1723 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Units cannot trigger themselves, ignoring: %s", rvalue
);
1727 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1729 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1736 int config_parse_path_spec(const char *unit
,
1737 const char *filename
,
1739 const char *section
,
1740 unsigned section_line
,
1750 _cleanup_free_
char *k
= NULL
;
1758 if (isempty(rvalue
)) {
1759 /* Empty assignment clears list */
1764 b
= path_type_from_string(lvalue
);
1766 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1770 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1772 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1776 if (!path_is_absolute(k
)) {
1777 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1781 s
= new0(PathSpec
, 1);
1786 s
->path
= path_kill_slashes(k
);
1791 LIST_PREPEND(spec
, p
->specs
, s
);
1796 int config_parse_socket_service(
1798 const char *filename
,
1800 const char *section
,
1801 unsigned section_line
,
1808 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1809 _cleanup_free_
char *p
= NULL
;
1819 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1821 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", rvalue
);
1825 if (!endswith(p
, ".service")) {
1826 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service: %s", rvalue
);
1830 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1832 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s: %s", rvalue
, bus_error_message(&error
, r
));
1836 unit_ref_set(&s
->service
, UNIT(s
), x
);
1841 int config_parse_fdname(
1843 const char *filename
,
1845 const char *section
,
1846 unsigned section_line
,
1853 _cleanup_free_
char *p
= NULL
;
1862 if (isempty(rvalue
)) {
1863 s
->fdname
= mfree(s
->fdname
);
1867 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1869 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1873 if (!fdname_is_valid(p
)) {
1874 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1878 return free_and_replace(s
->fdname
, p
);
1881 int config_parse_service_sockets(
1883 const char *filename
,
1885 const char *section
,
1886 unsigned section_line
,
1904 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1906 r
= extract_first_word(&p
, &word
, NULL
, 0);
1912 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1916 r
= unit_name_printf(UNIT(s
), word
, &k
);
1918 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1922 if (!endswith(k
, ".socket")) {
1923 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1927 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1929 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1931 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1933 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1939 int config_parse_bus_name(
1941 const char *filename
,
1943 const char *section
,
1944 unsigned section_line
,
1951 _cleanup_free_
char *k
= NULL
;
1960 r
= unit_full_printf(u
, rvalue
, &k
);
1962 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1966 if (!service_name_is_valid(k
)) {
1967 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1971 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1974 int config_parse_service_timeout(
1976 const char *filename
,
1978 const char *section
,
1979 unsigned section_line
,
1986 Service
*s
= userdata
;
1995 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1997 r
= parse_sec(rvalue
, &usec
);
1999 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
2003 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
2004 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
2005 * all other timeouts. */
2007 usec
= USEC_INFINITY
;
2009 if (!streq(lvalue
, "TimeoutStopSec")) {
2010 s
->start_timeout_defined
= true;
2011 s
->timeout_start_usec
= usec
;
2014 if (!streq(lvalue
, "TimeoutStartSec"))
2015 s
->timeout_stop_usec
= usec
;
2020 int config_parse_sec_fix_0(
2022 const char *filename
,
2024 const char *section
,
2025 unsigned section_line
,
2032 usec_t
*usec
= data
;
2040 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
2041 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
2044 r
= parse_sec_fix_0(rvalue
, usec
);
2046 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
2053 int config_parse_user_group(
2055 const char *filename
,
2057 const char *section
,
2058 unsigned section_line
,
2065 char **user
= data
, *n
;
2074 if (isempty(rvalue
))
2077 _cleanup_free_
char *k
= NULL
;
2079 r
= unit_full_printf(u
, rvalue
, &k
);
2081 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
2085 if (!valid_user_group_name_or_id(k
)) {
2086 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
2099 int config_parse_user_group_strv(
2101 const char *filename
,
2103 const char *section
,
2104 unsigned section_line
,
2111 char ***users
= data
;
2121 if (isempty(rvalue
)) {
2122 *users
= strv_free(*users
);
2128 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2130 r
= extract_first_word(&p
, &word
, NULL
, 0);
2136 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax: %s", rvalue
);
2140 r
= unit_full_printf(u
, word
, &k
);
2142 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", word
);
2146 if (!valid_user_group_name_or_id(k
)) {
2147 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
2151 r
= strv_push(users
, k
);
2161 int config_parse_working_directory(
2163 const char *filename
,
2165 const char *section
,
2166 unsigned section_line
,
2173 ExecContext
*c
= data
;
2184 if (rvalue
[0] == '-') {
2190 if (streq(rvalue
, "~")) {
2191 c
->working_directory_home
= true;
2192 c
->working_directory
= mfree(c
->working_directory
);
2194 _cleanup_free_
char *k
= NULL
;
2196 r
= unit_full_printf(u
, rvalue
, &k
);
2198 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2199 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2200 rvalue
, missing_ok
? ", ignoring" : "");
2201 return missing_ok
? 0 : -ENOEXEC
;
2204 path_kill_slashes(k
);
2206 if (!utf8_is_valid(k
)) {
2207 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2208 return missing_ok
? 0 : -ENOEXEC
;
2211 if (!path_is_absolute(k
)) {
2212 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2213 "Working directory path '%s' is not absolute%s.",
2214 rvalue
, missing_ok
? ", ignoring" : "");
2215 return missing_ok
? 0 : -ENOEXEC
;
2218 c
->working_directory_home
= false;
2219 free_and_replace(c
->working_directory
, k
);
2222 c
->working_directory_missing_ok
= missing_ok
;
2226 int config_parse_unit_env_file(const char *unit
,
2227 const char *filename
,
2229 const char *section
,
2230 unsigned section_line
,
2239 _cleanup_free_
char *n
= NULL
;
2247 if (isempty(rvalue
)) {
2248 /* Empty assignment frees the list */
2249 *env
= strv_free(*env
);
2253 r
= unit_full_printf(u
, rvalue
, &n
);
2255 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2259 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2260 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2264 r
= strv_extend(env
, n
);
2271 int config_parse_environ(
2273 const char *filename
,
2275 const char *section
,
2276 unsigned section_line
,
2293 if (isempty(rvalue
)) {
2294 /* Empty assignment resets the list */
2295 *env
= strv_free(*env
);
2299 for (p
= rvalue
;; ) {
2300 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2302 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2308 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2309 "Invalid syntax, ignoring: %s", rvalue
);
2314 r
= unit_full_printf(u
, word
, &k
);
2316 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2317 "Failed to resolve specifiers, ignoring: %s", word
);
2323 if (!env_assignment_is_valid(k
)) {
2324 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2325 "Invalid environment assignment, ignoring: %s", k
);
2329 r
= strv_env_replace(env
, k
);
2337 int config_parse_pass_environ(
2339 const char *filename
,
2341 const char *section
,
2342 unsigned section_line
,
2349 const char *whole_rvalue
= rvalue
;
2350 _cleanup_strv_free_
char **n
= NULL
;
2351 size_t nlen
= 0, nbufsize
= 0;
2352 char*** passenv
= data
;
2361 if (isempty(rvalue
)) {
2362 /* Empty assignment resets the list */
2363 *passenv
= strv_free(*passenv
);
2368 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2370 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2376 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2377 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2382 r
= unit_full_printf(u
, word
, &k
);
2384 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2385 "Failed to resolve specifiers, ignoring: %s", word
);
2391 if (!env_name_is_valid(k
)) {
2392 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2393 "Invalid environment name for %s, ignoring: %s", lvalue
, k
);
2397 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2406 r
= strv_extend_strv(passenv
, n
, true);
2414 int config_parse_unset_environ(
2416 const char *filename
,
2418 const char *section
,
2419 unsigned section_line
,
2426 _cleanup_strv_free_
char **n
= NULL
;
2427 const char *whole_rvalue
= rvalue
;
2428 size_t nlen
= 0, nbufsize
= 0;
2429 char*** unsetenv
= data
;
2438 if (isempty(rvalue
)) {
2439 /* Empty assignment resets the list */
2440 *unsetenv
= strv_free(*unsetenv
);
2445 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2447 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2453 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2454 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2459 r
= unit_full_printf(u
, word
, &k
);
2461 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2462 "Failed to resolve specifiers, ignoring: %s", word
);
2468 if (!env_assignment_is_valid(k
) && !env_name_is_valid(k
)) {
2469 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2470 "Invalid environment name or assignment %s, ignoring: %s", lvalue
, k
);
2474 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2483 r
= strv_extend_strv(unsetenv
, n
, true);
2491 int config_parse_log_extra_fields(
2493 const char *filename
,
2495 const char *section
,
2496 unsigned section_line
,
2503 ExecContext
*c
= data
;
2513 if (isempty(rvalue
)) {
2514 exec_context_free_log_extra_fields(c
);
2518 for (p
= rvalue
;; ) {
2519 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2523 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2529 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2533 r
= unit_full_printf(u
, word
, &k
);
2535 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring field: %m", word
);
2539 eq
= strchr(k
, '=');
2541 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field lacks '=' character, ignoring field: %s", k
);
2545 if (!journal_field_valid(k
, eq
-k
, false)) {
2546 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field name is invalid, ignoring field: %s", k
);
2550 t
= reallocarray(c
->log_extra_fields
, c
->n_log_extra_fields
+1, sizeof(struct iovec
));
2554 c
->log_extra_fields
= t
;
2555 c
->log_extra_fields
[c
->n_log_extra_fields
++] = IOVEC_MAKE_STRING(k
);
2563 int config_parse_ip_tos(const char *unit
,
2564 const char *filename
,
2566 const char *section
,
2567 unsigned section_line
,
2574 int *ip_tos
= data
, x
;
2581 x
= ip_tos_from_string(rvalue
);
2583 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2591 int config_parse_unit_condition_path(
2593 const char *filename
,
2595 const char *section
,
2596 unsigned section_line
,
2603 _cleanup_free_
char *p
= NULL
;
2604 Condition
**list
= data
, *c
;
2605 ConditionType t
= ltype
;
2606 bool trigger
, negate
;
2615 if (isempty(rvalue
)) {
2616 /* Empty assignment resets the list */
2617 *list
= condition_free_list(*list
);
2621 trigger
= rvalue
[0] == '|';
2625 negate
= rvalue
[0] == '!';
2629 r
= unit_full_printf(u
, rvalue
, &p
);
2631 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2635 if (!path_is_absolute(p
)) {
2636 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2640 c
= condition_new(t
, p
, trigger
, negate
);
2644 LIST_PREPEND(conditions
, *list
, c
);
2648 int config_parse_unit_condition_string(
2650 const char *filename
,
2652 const char *section
,
2653 unsigned section_line
,
2660 _cleanup_free_
char *s
= NULL
;
2661 Condition
**list
= data
, *c
;
2662 ConditionType t
= ltype
;
2663 bool trigger
, negate
;
2672 if (isempty(rvalue
)) {
2673 /* Empty assignment resets the list */
2674 *list
= condition_free_list(*list
);
2678 trigger
= rvalue
[0] == '|';
2682 negate
= rvalue
[0] == '!';
2686 r
= unit_full_printf(u
, rvalue
, &s
);
2688 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2692 c
= condition_new(t
, s
, trigger
, negate
);
2696 LIST_PREPEND(conditions
, *list
, c
);
2700 int config_parse_unit_condition_null(
2702 const char *filename
,
2704 const char *section
,
2705 unsigned section_line
,
2712 Condition
**list
= data
, *c
;
2713 bool trigger
, negate
;
2721 if (isempty(rvalue
)) {
2722 /* Empty assignment resets the list */
2723 *list
= condition_free_list(*list
);
2727 trigger
= rvalue
[0] == '|';
2731 negate
= rvalue
[0] == '!';
2735 b
= parse_boolean(rvalue
);
2737 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2744 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2748 LIST_PREPEND(conditions
, *list
, c
);
2752 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2753 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2755 int config_parse_unit_requires_mounts_for(
2757 const char *filename
,
2759 const char *section
,
2760 unsigned section_line
,
2776 for (p
= rvalue
;; ) {
2777 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2779 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2785 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2786 "Invalid syntax, ignoring: %s", rvalue
);
2790 if (!utf8_is_valid(word
)) {
2791 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2795 r
= unit_full_printf(u
, word
, &resolved
);
2797 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit name \"%s\", ignoring: %m", word
);
2801 r
= unit_require_mounts_for(u
, resolved
, UNIT_DEPENDENCY_FILE
);
2803 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", resolved
);
2809 int config_parse_documentation(const char *unit
,
2810 const char *filename
,
2812 const char *section
,
2813 unsigned section_line
,
2829 if (isempty(rvalue
)) {
2830 /* Empty assignment resets the list */
2831 u
->documentation
= strv_free(u
->documentation
);
2835 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2836 rvalue
, data
, userdata
);
2840 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2842 if (documentation_url_is_valid(*a
))
2845 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2856 int config_parse_syscall_filter(
2858 const char *filename
,
2860 const char *section
,
2861 unsigned section_line
,
2868 ExecContext
*c
= data
;
2870 bool invert
= false;
2879 if (isempty(rvalue
)) {
2880 /* Empty assignment resets the list */
2881 c
->syscall_filter
= hashmap_free(c
->syscall_filter
);
2882 c
->syscall_whitelist
= false;
2886 if (rvalue
[0] == '~') {
2891 if (!c
->syscall_filter
) {
2892 c
->syscall_filter
= hashmap_new(NULL
);
2893 if (!c
->syscall_filter
)
2897 /* Allow everything but the ones listed */
2898 c
->syscall_whitelist
= false;
2900 /* Allow nothing but the ones listed */
2901 c
->syscall_whitelist
= true;
2903 /* Accept default syscalls if we are on a whitelist */
2904 r
= seccomp_parse_syscall_filter("@default", -1, c
->syscall_filter
, SECCOMP_PARSE_WHITELIST
);
2912 _cleanup_free_
char *word
= NULL
, *name
= NULL
;
2915 r
= extract_first_word(&p
, &word
, NULL
, 0);
2921 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2925 r
= parse_syscall_and_errno(word
, &name
, &num
);
2927 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse syscall:errno, ignoring: %s", word
);
2931 r
= seccomp_parse_syscall_filter_full(name
, num
, c
->syscall_filter
,
2932 SECCOMP_PARSE_LOG
|SECCOMP_PARSE_PERMISSIVE
|(invert
? SECCOMP_PARSE_INVERT
: 0)|(c
->syscall_whitelist
? SECCOMP_PARSE_WHITELIST
: 0),
2933 unit
, filename
, line
);
2941 int config_parse_syscall_archs(
2943 const char *filename
,
2945 const char *section
,
2946 unsigned section_line
,
2957 if (isempty(rvalue
)) {
2958 *archs
= set_free(*archs
);
2962 r
= set_ensure_allocated(archs
, NULL
);
2966 for (p
= rvalue
;;) {
2967 _cleanup_free_
char *word
= NULL
;
2970 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2976 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2977 "Invalid syntax, ignoring: %s", rvalue
);
2981 r
= seccomp_arch_from_string(word
, &a
);
2983 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2984 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
2988 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2994 int config_parse_syscall_errno(
2996 const char *filename
,
2998 const char *section
,
2999 unsigned section_line
,
3006 ExecContext
*c
= data
;
3013 if (isempty(rvalue
)) {
3014 /* Empty assignment resets to KILL */
3015 c
->syscall_errno
= 0;
3019 e
= parse_errno(rvalue
);
3021 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
3025 c
->syscall_errno
= e
;
3029 int config_parse_address_families(
3031 const char *filename
,
3033 const char *section
,
3034 unsigned section_line
,
3041 ExecContext
*c
= data
;
3042 bool invert
= false;
3050 if (isempty(rvalue
)) {
3051 /* Empty assignment resets the list */
3052 c
->address_families
= set_free(c
->address_families
);
3053 c
->address_families_whitelist
= false;
3057 if (rvalue
[0] == '~') {
3062 if (!c
->address_families
) {
3063 c
->address_families
= set_new(NULL
);
3064 if (!c
->address_families
)
3067 c
->address_families_whitelist
= !invert
;
3070 for (p
= rvalue
;;) {
3071 _cleanup_free_
char *word
= NULL
;
3074 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3080 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3081 "Invalid syntax, ignoring: %s", rvalue
);
3085 af
= af_from_name(word
);
3087 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3088 "Failed to parse address family \"%s\", ignoring: %m", word
);
3092 /* If we previously wanted to forbid an address family and now
3093 * we want to allow it, then just remove it from the list.
3095 if (!invert
== c
->address_families_whitelist
) {
3096 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
3100 set_remove(c
->address_families
, INT_TO_PTR(af
));
3104 int config_parse_restrict_namespaces(
3106 const char *filename
,
3108 const char *section
,
3109 unsigned section_line
,
3116 ExecContext
*c
= data
;
3117 bool invert
= false;
3120 if (isempty(rvalue
)) {
3121 /* Reset to the default. */
3122 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3126 if (rvalue
[0] == '~') {
3131 r
= parse_boolean(rvalue
);
3133 c
->restrict_namespaces
= 0;
3135 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3137 /* Not a boolean argument, in this case it's a list of namespace types. */
3139 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
3141 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
3147 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
3153 int config_parse_unit_slice(
3155 const char *filename
,
3157 const char *section
,
3158 unsigned section_line
,
3165 _cleanup_free_
char *k
= NULL
;
3166 Unit
*u
= userdata
, *slice
= NULL
;
3174 r
= unit_name_printf(u
, rvalue
, &k
);
3176 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
3180 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
3182 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
3186 r
= unit_set_slice(u
, slice
);
3188 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
3195 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
3197 int config_parse_cpu_weight(
3199 const char *filename
,
3201 const char *section
,
3202 unsigned section_line
,
3209 uint64_t *weight
= data
;
3216 r
= cg_weight_parse(rvalue
, weight
);
3218 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
3225 int config_parse_cpu_shares(
3227 const char *filename
,
3229 const char *section
,
3230 unsigned section_line
,
3237 uint64_t *shares
= data
;
3244 r
= cg_cpu_shares_parse(rvalue
, shares
);
3246 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
3253 int config_parse_cpu_quota(
3255 const char *filename
,
3257 const char *section
,
3258 unsigned section_line
,
3265 CGroupContext
*c
= data
;
3272 if (isempty(rvalue
)) {
3273 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3277 r
= parse_percent_unbounded(rvalue
);
3279 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3283 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3287 int config_parse_memory_limit(
3289 const char *filename
,
3291 const char *section
,
3292 unsigned section_line
,
3299 CGroupContext
*c
= data
;
3300 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3303 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3305 r
= parse_percent(rvalue
);
3307 r
= parse_size(rvalue
, 1024, &bytes
);
3309 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3313 bytes
= physical_memory_scale(r
, 100U);
3315 if (bytes
>= UINT64_MAX
||
3316 (bytes
<= 0 && !streq(lvalue
, "MemorySwapMax"))) {
3317 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3322 if (streq(lvalue
, "MemoryLow"))
3323 c
->memory_low
= bytes
;
3324 else if (streq(lvalue
, "MemoryHigh"))
3325 c
->memory_high
= bytes
;
3326 else if (streq(lvalue
, "MemoryMax"))
3327 c
->memory_max
= bytes
;
3328 else if (streq(lvalue
, "MemorySwapMax"))
3329 c
->memory_swap_max
= bytes
;
3330 else if (streq(lvalue
, "MemoryLimit"))
3331 c
->memory_limit
= bytes
;
3338 int config_parse_tasks_max(
3340 const char *filename
,
3342 const char *section
,
3343 unsigned section_line
,
3350 uint64_t *tasks_max
= data
, v
;
3354 if (isempty(rvalue
)) {
3355 *tasks_max
= u
->manager
->default_tasks_max
;
3359 if (streq(rvalue
, "infinity")) {
3360 *tasks_max
= CGROUP_LIMIT_MAX
;
3364 r
= parse_percent(rvalue
);
3366 r
= safe_atou64(rvalue
, &v
);
3368 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3372 v
= system_tasks_max_scale(r
, 100U);
3374 if (v
<= 0 || v
>= UINT64_MAX
) {
3375 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3383 int config_parse_delegate(
3385 const char *filename
,
3387 const char *section
,
3388 unsigned section_line
,
3395 CGroupContext
*c
= data
;
3398 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3399 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3400 * mask to delegate. */
3402 if (isempty(rvalue
)) {
3404 c
->delegate_controllers
= 0;
3408 r
= parse_boolean(rvalue
);
3410 const char *p
= rvalue
;
3411 CGroupMask mask
= 0;
3414 _cleanup_free_
char *word
= NULL
;
3415 CGroupController cc
;
3417 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3423 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
3427 cc
= cgroup_controller_from_string(word
);
3429 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid controller name '%s', ignoring", rvalue
);
3433 mask
|= CGROUP_CONTROLLER_TO_MASK(cc
);
3437 c
->delegate_controllers
|= mask
;
3441 c
->delegate_controllers
= _CGROUP_MASK_ALL
;
3443 c
->delegate
= false;
3444 c
->delegate_controllers
= 0;
3450 int config_parse_device_allow(
3452 const char *filename
,
3454 const char *section
,
3455 unsigned section_line
,
3462 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3463 CGroupContext
*c
= data
;
3464 CGroupDeviceAllow
*a
;
3465 const char *m
= NULL
;
3469 if (isempty(rvalue
)) {
3470 while (c
->device_allow
)
3471 cgroup_context_free_device_allow(c
, c
->device_allow
);
3476 r
= unit_full_printf(userdata
, rvalue
, &t
);
3478 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3479 "Failed to resolve specifiers in %s, ignoring: %m",
3484 n
= strcspn(t
, WHITESPACE
);
3486 path
= strndup(t
, n
);
3490 if (!is_deviceallow_pattern(path
) &&
3491 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3492 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3496 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3500 if (!in_charset(m
, "rwm")) {
3501 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3505 a
= new0(CGroupDeviceAllow
, 1);
3509 a
->path
= TAKE_PTR(path
);
3510 a
->r
= !!strchr(m
, 'r');
3511 a
->w
= !!strchr(m
, 'w');
3512 a
->m
= !!strchr(m
, 'm');
3514 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3518 int config_parse_io_weight(
3520 const char *filename
,
3522 const char *section
,
3523 unsigned section_line
,
3530 uint64_t *weight
= data
;
3537 r
= cg_weight_parse(rvalue
, weight
);
3539 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3546 int config_parse_io_device_weight(
3548 const char *filename
,
3550 const char *section
,
3551 unsigned section_line
,
3558 _cleanup_free_
char *path
= NULL
;
3559 CGroupIODeviceWeight
*w
;
3560 CGroupContext
*c
= data
;
3570 if (isempty(rvalue
)) {
3571 while (c
->io_device_weights
)
3572 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3577 n
= strcspn(rvalue
, WHITESPACE
);
3578 weight
= rvalue
+ n
;
3579 weight
+= strspn(weight
, WHITESPACE
);
3581 if (isempty(weight
)) {
3582 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3586 path
= strndup(rvalue
, n
);
3590 if (!path_startswith(path
, "/dev") &&
3591 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3592 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3596 r
= cg_weight_parse(weight
, &u
);
3598 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3602 assert(u
!= CGROUP_WEIGHT_INVALID
);
3604 w
= new0(CGroupIODeviceWeight
, 1);
3608 w
->path
= TAKE_PTR(path
);
3612 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3616 int config_parse_io_limit(
3618 const char *filename
,
3620 const char *section
,
3621 unsigned section_line
,
3628 _cleanup_free_
char *path
= NULL
;
3629 CGroupIODeviceLimit
*l
= NULL
, *t
;
3630 CGroupContext
*c
= data
;
3631 CGroupIOLimitType type
;
3641 type
= cgroup_io_limit_type_from_string(lvalue
);
3644 if (isempty(rvalue
)) {
3645 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3646 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3650 n
= strcspn(rvalue
, WHITESPACE
);
3652 limit
+= strspn(limit
, WHITESPACE
);
3655 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3659 path
= strndup(rvalue
, n
);
3663 if (!path_startswith(path
, "/dev") &&
3664 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3665 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3669 if (streq("infinity", limit
)) {
3670 num
= CGROUP_LIMIT_MAX
;
3672 r
= parse_size(limit
, 1000, &num
);
3673 if (r
< 0 || num
<= 0) {
3674 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3679 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3680 if (path_equal(path
, t
->path
)) {
3687 CGroupIOLimitType ttype
;
3689 l
= new0(CGroupIODeviceLimit
, 1);
3693 l
->path
= TAKE_PTR(path
);
3694 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3695 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3697 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3700 l
->limits
[type
] = num
;
3705 int config_parse_blockio_weight(
3707 const char *filename
,
3709 const char *section
,
3710 unsigned section_line
,
3717 uint64_t *weight
= data
;
3724 r
= cg_blkio_weight_parse(rvalue
, weight
);
3726 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3733 int config_parse_blockio_device_weight(
3735 const char *filename
,
3737 const char *section
,
3738 unsigned section_line
,
3745 _cleanup_free_
char *path
= NULL
;
3746 CGroupBlockIODeviceWeight
*w
;
3747 CGroupContext
*c
= data
;
3757 if (isempty(rvalue
)) {
3758 while (c
->blockio_device_weights
)
3759 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3764 n
= strcspn(rvalue
, WHITESPACE
);
3765 weight
= rvalue
+ n
;
3766 weight
+= strspn(weight
, WHITESPACE
);
3768 if (isempty(weight
)) {
3769 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3773 path
= strndup(rvalue
, n
);
3777 if (!path_startswith(path
, "/dev") &&
3778 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3779 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3783 r
= cg_blkio_weight_parse(weight
, &u
);
3785 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3789 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3791 w
= new0(CGroupBlockIODeviceWeight
, 1);
3795 w
->path
= TAKE_PTR(path
);
3799 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3803 int config_parse_blockio_bandwidth(
3805 const char *filename
,
3807 const char *section
,
3808 unsigned section_line
,
3815 _cleanup_free_
char *path
= NULL
;
3816 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3817 CGroupContext
*c
= data
;
3818 const char *bandwidth
;
3828 read
= streq("BlockIOReadBandwidth", lvalue
);
3830 if (isempty(rvalue
)) {
3831 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3832 b
->rbps
= CGROUP_LIMIT_MAX
;
3833 b
->wbps
= CGROUP_LIMIT_MAX
;
3838 n
= strcspn(rvalue
, WHITESPACE
);
3839 bandwidth
= rvalue
+ n
;
3840 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3843 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3847 path
= strndup(rvalue
, n
);
3851 if (!path_startswith(path
, "/dev") &&
3852 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3853 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3857 r
= parse_size(bandwidth
, 1000, &bytes
);
3858 if (r
< 0 || bytes
<= 0) {
3859 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3863 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3864 if (path_equal(path
, t
->path
)) {
3871 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3875 b
->path
= TAKE_PTR(path
);
3876 b
->rbps
= CGROUP_LIMIT_MAX
;
3877 b
->wbps
= CGROUP_LIMIT_MAX
;
3879 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3890 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3892 int config_parse_job_mode_isolate(
3894 const char *filename
,
3896 const char *section
,
3897 unsigned section_line
,
3911 r
= parse_boolean(rvalue
);
3913 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3917 log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue
);
3919 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3923 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode
, exec_preserve_mode
, ExecPreserveMode
, "Failed to parse runtime directory preserve mode");
3925 int config_parse_exec_directories(
3927 const char *filename
,
3929 const char *section
,
3930 unsigned section_line
,
3947 if (isempty(rvalue
)) {
3948 /* Empty assignment resets the list */
3949 *rt
= strv_free(*rt
);
3953 for (p
= rvalue
;;) {
3954 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3956 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3960 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3961 "Invalid syntax, ignoring: %s", rvalue
);
3967 r
= unit_full_printf(u
, word
, &k
);
3969 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3970 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
3974 if (!path_is_normalized(k
) || path_is_absolute(k
)) {
3975 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3976 "%s= path is not valid, ignoring assignment: %s", lvalue
, rvalue
);
3980 r
= strv_push(rt
, k
);
3987 int config_parse_set_status(
3989 const char *filename
,
3991 const char *section
,
3992 unsigned section_line
,
4000 const char *word
, *state
;
4002 ExitStatusSet
*status_set
= data
;
4009 /* Empty assignment resets the list */
4010 if (isempty(rvalue
)) {
4011 exit_status_set_free(status_set
);
4015 FOREACH_WORD(word
, l
, rvalue
, state
) {
4016 _cleanup_free_
char *temp
;
4020 temp
= strndup(word
, l
);
4024 r
= safe_atoi(temp
, &val
);
4026 val
= signal_from_string_try_harder(temp
);
4029 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
4032 set
= &status_set
->signal
;
4034 if (val
< 0 || val
> 255) {
4035 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
4038 set
= &status_set
->status
;
4041 r
= set_ensure_allocated(set
, NULL
);
4045 r
= set_put(*set
, INT_TO_PTR(val
));
4047 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
4051 if (!isempty(state
))
4052 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
4057 int config_parse_namespace_path_strv(
4059 const char *filename
,
4061 const char *section
,
4062 unsigned section_line
,
4079 if (isempty(rvalue
)) {
4080 /* Empty assignment resets the list */
4081 *sv
= strv_free(*sv
);
4087 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
4089 bool ignore_enoent
= false, shall_prefix
= false;
4091 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
4097 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
4101 if (!utf8_is_valid(word
)) {
4102 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
4107 if (startswith(w
, "-")) {
4108 ignore_enoent
= true;
4111 if (startswith(w
, "+")) {
4112 shall_prefix
= true;
4116 r
= unit_full_printf(u
, w
, &resolved
);
4118 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s: %m", word
);
4122 if (!path_is_absolute(resolved
)) {
4123 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
4127 path_kill_slashes(resolved
);
4129 joined
= strjoin(ignore_enoent
? "-" : "",
4130 shall_prefix
? "+" : "",
4133 r
= strv_push(sv
, joined
);
4143 int config_parse_temporary_filesystems(
4145 const char *filename
,
4147 const char *section
,
4148 unsigned section_line
,
4156 ExecContext
*c
= data
;
4165 if (isempty(rvalue
)) {
4166 /* Empty assignment resets the list */
4167 temporary_filesystem_free_many(c
->temporary_filesystems
, c
->n_temporary_filesystems
);
4168 c
->temporary_filesystems
= NULL
;
4169 c
->n_temporary_filesystems
= 0;
4175 _cleanup_free_
char *word
= NULL
, *path
= NULL
, *resolved
= NULL
;
4178 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
4184 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
4189 r
= extract_first_word(&w
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
4195 r
= unit_full_printf(u
, path
, &resolved
);
4197 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s, ignoring: %m", word
);
4201 if (!path_is_absolute(resolved
)) {
4202 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
4206 path_kill_slashes(resolved
);
4208 r
= temporary_filesystem_add(&c
->temporary_filesystems
, &c
->n_temporary_filesystems
, path
, w
);
4212 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse mount options, ignoring: %s", word
);
4220 int config_parse_bind_paths(
4222 const char *filename
,
4224 const char *section
,
4225 unsigned section_line
,
4232 ExecContext
*c
= data
;
4242 if (isempty(rvalue
)) {
4243 /* Empty assignment resets the list */
4244 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
4245 c
->bind_mounts
= NULL
;
4246 c
->n_bind_mounts
= 0;
4252 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
4253 _cleanup_free_
char *sresolved
= NULL
, *dresolved
= NULL
;
4254 char *s
= NULL
, *d
= NULL
;
4255 bool rbind
= true, ignore_enoent
= false;
4257 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4263 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4267 r
= unit_full_printf(u
, source
, &sresolved
);
4269 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4270 "Failed to resolved specifiers in \"%s\", ignoring: %m", source
);
4276 ignore_enoent
= true;
4280 if (!utf8_is_valid(s
)) {
4281 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, s
);
4284 if (!path_is_absolute(s
)) {
4285 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute source path, ignoring: %s", s
);
4289 path_kill_slashes(s
);
4291 /* Optionally, the destination is specified. */
4292 if (p
&& p
[-1] == ':') {
4293 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4297 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4301 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':': %s", rvalue
);
4305 r
= unit_full_printf(u
, destination
, &dresolved
);
4307 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4308 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination
);
4312 if (!utf8_is_valid(dresolved
)) {
4313 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, dresolved
);
4316 if (!path_is_absolute(dresolved
)) {
4317 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute destination path, ignoring: %s", dresolved
);
4321 d
= path_kill_slashes(dresolved
);
4323 /* Optionally, there's also a short option string specified */
4324 if (p
&& p
[-1] == ':') {
4325 _cleanup_free_
char *options
= NULL
;
4327 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
4331 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4335 if (isempty(options
) || streq(options
, "rbind"))
4337 else if (streq(options
, "norbind"))
4340 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
4347 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
4351 .read_only
= !!strstr(lvalue
, "ReadOnly"),
4353 .ignore_enoent
= ignore_enoent
,
4362 int config_parse_no_new_privileges(
4364 const char *filename
,
4366 const char *section
,
4367 unsigned section_line
,
4374 ExecContext
*c
= data
;
4382 k
= parse_boolean(rvalue
);
4384 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
4388 c
->no_new_privileges
= k
;
4393 int config_parse_protect_home(
4395 const char *filename
,
4397 const char *section
,
4398 unsigned section_line
,
4405 ExecContext
*c
= data
;
4413 /* Our enum shall be a superset of booleans, hence first try
4414 * to parse as boolean, and then as enum */
4416 h
= parse_protect_home_or_bool(rvalue
);
4418 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
4422 c
->protect_home
= h
;
4427 int config_parse_protect_system(
4429 const char *filename
,
4431 const char *section
,
4432 unsigned section_line
,
4439 ExecContext
*c
= data
;
4447 /* Our enum shall be a superset of booleans, hence first try
4448 * to parse as boolean, and then as enum */
4450 s
= parse_protect_system_or_bool(rvalue
);
4452 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
4456 c
->protect_system
= s
;
4461 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode
, exec_keyring_mode
, ExecKeyringMode
, "Failed to parse keyring mode");
4463 int config_parse_job_timeout_sec(
4465 const char *filename
,
4467 const char *section
,
4468 unsigned section_line
,
4484 r
= parse_sec_fix_0(rvalue
, &usec
);
4486 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue
);
4490 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
4491 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
4494 if (!u
->job_running_timeout_set
)
4495 u
->job_running_timeout
= usec
;
4497 u
->job_timeout
= usec
;
4502 int config_parse_job_running_timeout_sec(
4504 const char *filename
,
4506 const char *section
,
4507 unsigned section_line
,
4523 r
= parse_sec_fix_0(rvalue
, &usec
);
4525 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue
);
4529 u
->job_running_timeout
= usec
;
4530 u
->job_running_timeout_set
= true;
4535 #define FOLLOW_MAX 8
4537 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4548 /* This will update the filename pointer if the loaded file is
4549 * reached by a symlink. The old string will be freed. */
4552 char *target
, *name
;
4554 if (c
++ >= FOLLOW_MAX
)
4557 path_kill_slashes(*filename
);
4559 /* Add the file name we are currently looking at to
4560 * the names of this unit, but only if it is a valid
4562 name
= basename(*filename
);
4563 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4565 id
= set_get(names
, name
);
4571 r
= set_consume(names
, id
);
4577 /* Try to open the file name, but don't if its a symlink */
4578 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4585 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4586 r
= readlink_and_make_absolute(*filename
, &target
);
4594 f
= fdopen(fd
, "re");
4606 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4614 /* Let's try to add in all symlink names we found */
4615 while ((k
= set_steal_first(names
))) {
4617 /* First try to merge in the other name into our
4619 r
= unit_merge_by_name(*u
, k
);
4623 /* Hmm, we couldn't merge the other unit into
4624 * ours? Then let's try it the other way
4627 /* If the symlink name we are looking at is unit template, then
4628 we must search for instance of this template */
4629 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4630 _cleanup_free_
char *instance
= NULL
;
4632 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4636 other
= manager_get_unit((*u
)->manager
, instance
);
4638 other
= manager_get_unit((*u
)->manager
, k
);
4643 r
= unit_merge(other
, *u
);
4646 return merge_by_names(u
, names
, NULL
);
4654 unit_choose_id(*u
, id
);
4662 static int load_from_path(Unit
*u
, const char *path
) {
4663 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4664 _cleanup_fclose_
FILE *f
= NULL
;
4665 _cleanup_free_
char *filename
= NULL
;
4674 symlink_names
= set_new(&string_hash_ops
);
4678 if (path_is_absolute(path
)) {
4680 filename
= strdup(path
);
4684 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4686 filename
= mfree(filename
);
4694 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4696 /* Instead of opening the path right away, we manually
4697 * follow all symlinks and add their name to our unit
4698 * name set while doing so */
4699 filename
= path_make_absolute(path
, *p
);
4703 if (u
->manager
->unit_path_cache
&&
4704 !set_get(u
->manager
->unit_path_cache
, filename
))
4707 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4710 filename
= mfree(filename
);
4712 /* ENOENT means that the file is missing or is a dangling symlink.
4713 * ENOTDIR means that one of paths we expect to be is a directory
4714 * is not a directory, we should just ignore that.
4715 * EACCES means that the directory or file permissions are wrong.
4718 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4719 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4722 /* Empty the symlink names for the next run */
4723 set_clear_free(symlink_names
);
4728 /* Hmm, no suitable file found? */
4731 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4732 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4737 r
= merge_by_names(&merged
, symlink_names
, id
);
4742 u
->load_state
= UNIT_MERGED
;
4746 if (fstat(fileno(f
), &st
) < 0)
4749 if (null_or_empty(&st
)) {
4750 u
->load_state
= UNIT_MASKED
;
4751 u
->fragment_mtime
= 0;
4753 u
->load_state
= UNIT_LOADED
;
4754 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4756 /* Now, parse the file contents */
4757 r
= config_parse(u
->id
, filename
, f
,
4758 UNIT_VTABLE(u
)->sections
,
4759 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4760 CONFIG_PARSE_ALLOW_INCLUDE
, u
);
4765 free(u
->fragment_path
);
4766 u
->fragment_path
= filename
;
4769 if (u
->source_path
) {
4770 if (stat(u
->source_path
, &st
) >= 0)
4771 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4773 u
->source_mtime
= 0;
4779 int unit_load_fragment(Unit
*u
) {
4785 assert(u
->load_state
== UNIT_STUB
);
4789 u
->load_state
= UNIT_LOADED
;
4793 /* First, try to find the unit under its id. We always look
4794 * for unit files in the default directories, to make it easy
4795 * to override things by placing things in /etc/systemd/system */
4796 r
= load_from_path(u
, u
->id
);
4800 /* Try to find an alias we can load this with */
4801 if (u
->load_state
== UNIT_STUB
) {
4802 SET_FOREACH(t
, u
->names
, i
) {
4807 r
= load_from_path(u
, t
);
4811 if (u
->load_state
!= UNIT_STUB
)
4816 /* And now, try looking for it under the suggested (originally linked) path */
4817 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4819 r
= load_from_path(u
, u
->fragment_path
);
4823 if (u
->load_state
== UNIT_STUB
)
4824 /* Hmm, this didn't work? Then let's get rid
4825 * of the fragment path stored for us, so that
4826 * we don't point to an invalid location. */
4827 u
->fragment_path
= mfree(u
->fragment_path
);
4830 /* Look for a template */
4831 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4832 _cleanup_free_
char *k
= NULL
;
4834 r
= unit_name_template(u
->id
, &k
);
4838 r
= load_from_path(u
, k
);
4841 log_unit_notice(u
, "Unit configuration has fatal error, unit will not be started.");
4845 if (u
->load_state
== UNIT_STUB
) {
4846 SET_FOREACH(t
, u
->names
, i
) {
4847 _cleanup_free_
char *z
= NULL
;
4852 r
= unit_name_template(t
, &z
);
4856 r
= load_from_path(u
, z
);
4860 if (u
->load_state
!= UNIT_STUB
)
4869 void unit_dump_config_items(FILE *f
) {
4870 static const struct {
4871 const ConfigParserCallback callback
;
4874 #if !HAVE_SYSV_COMPAT || !HAVE_SECCOMP || !HAVE_PAM || !HAVE_SELINUX || !ENABLE_SMACK || !HAVE_APPARMOR
4875 { config_parse_warn_compat
, "NOTSUPPORTED" },
4877 { config_parse_int
, "INTEGER" },
4878 { config_parse_unsigned
, "UNSIGNED" },
4879 { config_parse_iec_size
, "SIZE" },
4880 { config_parse_iec_uint64
, "SIZE" },
4881 { config_parse_si_size
, "SIZE" },
4882 { config_parse_bool
, "BOOLEAN" },
4883 { config_parse_string
, "STRING" },
4884 { config_parse_path
, "PATH" },
4885 { config_parse_unit_path_printf
, "PATH" },
4886 { config_parse_strv
, "STRING [...]" },
4887 { config_parse_exec_nice
, "NICE" },
4888 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4889 { config_parse_exec_io_class
, "IOCLASS" },
4890 { config_parse_exec_io_priority
, "IOPRIORITY" },
4891 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4892 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4893 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4894 { config_parse_mode
, "MODE" },
4895 { config_parse_unit_env_file
, "FILE" },
4896 { config_parse_exec_output
, "OUTPUT" },
4897 { config_parse_exec_input
, "INPUT" },
4898 { config_parse_log_facility
, "FACILITY" },
4899 { config_parse_log_level
, "LEVEL" },
4900 { config_parse_exec_secure_bits
, "SECUREBITS" },
4901 { config_parse_capability_set
, "BOUNDINGSET" },
4902 { config_parse_limit
, "LIMIT" },
4903 { config_parse_unit_deps
, "UNIT [...]" },
4904 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4905 { config_parse_service_type
, "SERVICETYPE" },
4906 { config_parse_service_restart
, "SERVICERESTART" },
4907 #if HAVE_SYSV_COMPAT
4908 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4910 { config_parse_kill_mode
, "KILLMODE" },
4911 { config_parse_signal
, "SIGNAL" },
4912 { config_parse_socket_listen
, "SOCKET [...]" },
4913 { config_parse_socket_bind
, "SOCKETBIND" },
4914 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4915 { config_parse_sec
, "SECONDS" },
4916 { config_parse_nsec
, "NANOSECONDS" },
4917 { config_parse_namespace_path_strv
, "PATH [...]" },
4918 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4919 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4920 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4921 { config_parse_unit_string_printf
, "STRING" },
4922 { config_parse_trigger_unit
, "UNIT" },
4923 { config_parse_timer
, "TIMER" },
4924 { config_parse_path_spec
, "PATH" },
4925 { config_parse_notify_access
, "ACCESS" },
4926 { config_parse_ip_tos
, "TOS" },
4927 { config_parse_unit_condition_path
, "CONDITION" },
4928 { config_parse_unit_condition_string
, "CONDITION" },
4929 { config_parse_unit_condition_null
, "CONDITION" },
4930 { config_parse_unit_slice
, "SLICE" },
4931 { config_parse_documentation
, "URL" },
4932 { config_parse_service_timeout
, "SECONDS" },
4933 { config_parse_emergency_action
, "ACTION" },
4934 { config_parse_set_status
, "STATUS" },
4935 { config_parse_service_sockets
, "SOCKETS" },
4936 { config_parse_environ
, "ENVIRON" },
4938 { config_parse_syscall_filter
, "SYSCALLS" },
4939 { config_parse_syscall_archs
, "ARCHS" },
4940 { config_parse_syscall_errno
, "ERRNO" },
4941 { config_parse_address_families
, "FAMILIES" },
4942 { config_parse_restrict_namespaces
, "NAMESPACES" },
4944 { config_parse_cpu_shares
, "SHARES" },
4945 { config_parse_cpu_weight
, "WEIGHT" },
4946 { config_parse_memory_limit
, "LIMIT" },
4947 { config_parse_device_allow
, "DEVICE" },
4948 { config_parse_device_policy
, "POLICY" },
4949 { config_parse_io_limit
, "LIMIT" },
4950 { config_parse_io_weight
, "WEIGHT" },
4951 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4952 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4953 { config_parse_blockio_weight
, "WEIGHT" },
4954 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4955 { config_parse_long
, "LONG" },
4956 { config_parse_socket_service
, "SERVICE" },
4958 { config_parse_exec_selinux_context
, "LABEL" },
4960 { config_parse_job_mode
, "MODE" },
4961 { config_parse_job_mode_isolate
, "BOOLEAN" },
4962 { config_parse_personality
, "PERSONALITY" },
4965 const char *prev
= NULL
;
4970 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4971 const char *rvalue
= "OTHER", *lvalue
;
4975 const ConfigPerfItem
*p
;
4977 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4979 dot
= strchr(i
, '.');
4980 lvalue
= dot
? dot
+ 1 : i
;
4984 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
4988 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
4991 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
4992 if (p
->parse
== table
[j
].callback
) {
4993 rvalue
= table
[j
].rvalue
;
4997 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);