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 int config_parse_warn_compat(
83 unsigned section_line
,
89 Disabled reason
= ltype
;
92 case DISABLED_CONFIGURATION
:
93 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
94 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
97 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
98 "Support for option %s= has been removed and it is ignored", lvalue
);
100 case DISABLED_EXPERIMENTAL
:
101 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
102 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
109 DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode
, collect_mode
, CollectMode
, "Failed to parse garbage collection mode");
111 int config_parse_unit_deps(
113 const char *filename
,
116 unsigned section_line
,
123 UnitDependency d
= ltype
;
133 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
136 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_RETAIN_ESCAPE
);
142 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
146 r
= unit_name_printf(u
, word
, &k
);
148 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
152 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
154 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
160 int config_parse_obsolete_unit_deps(
162 const char *filename
,
165 unsigned section_line
,
172 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
173 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue
, unit_dependency_to_string(ltype
));
175 return config_parse_unit_deps(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, data
, userdata
);
178 int config_parse_unit_string_printf(
180 const char *filename
,
183 unsigned section_line
,
190 _cleanup_free_
char *k
= NULL
;
199 r
= unit_full_printf(u
, rvalue
, &k
);
201 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
205 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
208 int config_parse_unit_strv_printf(
210 const char *filename
,
213 unsigned section_line
,
221 _cleanup_free_
char *k
= NULL
;
229 r
= unit_full_printf(u
, rvalue
, &k
);
231 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
235 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
238 int config_parse_unit_path_printf(
240 const char *filename
,
243 unsigned section_line
,
250 _cleanup_free_
char *k
= NULL
;
260 r
= unit_full_printf(u
, rvalue
, &k
);
262 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
263 "Failed to resolve unit specifiers on %s%s: %m",
264 fatal
? "" : ", ignoring", rvalue
);
265 return fatal
? -ENOEXEC
: 0;
268 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
271 int config_parse_unit_path_strv_printf(
273 const char *filename
,
276 unsigned section_line
,
293 if (isempty(rvalue
)) {
299 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
301 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
307 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
308 "Invalid syntax, ignoring: %s", rvalue
);
312 r
= unit_full_printf(u
, word
, &k
);
314 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
315 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
319 if (!utf8_is_valid(k
)) {
320 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
324 if (!path_is_absolute(k
)) {
325 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
326 "Symlink path is not absolute: %s", k
);
330 path_kill_slashes(k
);
339 int config_parse_socket_listen(const char *unit
,
340 const char *filename
,
343 unsigned section_line
,
350 _cleanup_free_ SocketPort
*p
= NULL
;
362 if (isempty(rvalue
)) {
363 /* An empty assignment removes all ports */
364 socket_free_ports(s
);
368 p
= new0(SocketPort
, 1);
372 if (ltype
!= SOCKET_SOCKET
) {
375 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
377 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
381 path_kill_slashes(p
->path
);
383 } else if (streq(lvalue
, "ListenNetlink")) {
384 _cleanup_free_
char *k
= NULL
;
386 p
->type
= SOCKET_SOCKET
;
387 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
389 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
393 r
= socket_address_parse_netlink(&p
->address
, k
);
395 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
400 _cleanup_free_
char *k
= NULL
;
402 p
->type
= SOCKET_SOCKET
;
403 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
405 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
409 r
= socket_address_parse_and_warn(&p
->address
, k
);
411 if (r
!= -EAFNOSUPPORT
)
412 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
417 if (streq(lvalue
, "ListenStream"))
418 p
->address
.type
= SOCK_STREAM
;
419 else if (streq(lvalue
, "ListenDatagram"))
420 p
->address
.type
= SOCK_DGRAM
;
422 assert(streq(lvalue
, "ListenSequentialPacket"));
423 p
->address
.type
= SOCK_SEQPACKET
;
426 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
427 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
433 p
->auxiliary_fds
= NULL
;
434 p
->n_auxiliary_fds
= 0;
437 LIST_FIND_TAIL(port
, s
->ports
, tail
);
438 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
445 int config_parse_socket_protocol(const char *unit
,
446 const char *filename
,
449 unsigned section_line
,
465 r
= socket_protocol_from_name(rvalue
);
467 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid socket protocol, ignoring: %s", rvalue
);
469 } else if (!IN_SET(r
, IPPROTO_UDPLITE
, IPPROTO_SCTP
)) {
470 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
474 s
->socket_protocol
= r
;
479 int config_parse_socket_bind(const char *unit
,
480 const char *filename
,
483 unsigned section_line
,
491 SocketAddressBindIPv6Only b
;
500 b
= parse_socket_address_bind_ipv6_only_or_bool(rvalue
);
502 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
506 s
->bind_ipv6_only
= b
;
511 int config_parse_exec_nice(
513 const char *filename
,
516 unsigned section_line
,
523 ExecContext
*c
= data
;
531 r
= parse_nice(rvalue
, &priority
);
534 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
536 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
547 int config_parse_exec_oom_score_adjust(const char* unit
,
548 const char *filename
,
551 unsigned section_line
,
558 ExecContext
*c
= data
;
566 r
= safe_atoi(rvalue
, &oa
);
568 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
572 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
573 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
577 c
->oom_score_adjust
= oa
;
578 c
->oom_score_adjust_set
= true;
583 int config_parse_exec(
585 const char *filename
,
588 unsigned section_line
,
595 ExecCommand
**e
= data
;
607 rvalue
+= strspn(rvalue
, WHITESPACE
);
609 if (isempty(rvalue
)) {
610 /* An empty assignment resets the list */
611 *e
= exec_command_free_list(*e
);
617 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
618 ExecCommandFlags flags
= 0;
619 bool ignore
= false, separate_argv0
= false;
620 _cleanup_free_ ExecCommand
*nce
= NULL
;
621 _cleanup_strv_free_
char **n
= NULL
;
622 size_t nlen
= 0, nbufsize
= 0;
627 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
633 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
634 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
635 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
636 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
637 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
638 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
639 * other sandboxing, with some special exceptions for changing UID.
641 * The idea is that '!!' may be used to write services that can take benefit of systemd's
642 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
643 * privilege dropping within the daemon if the kernel does not offer that. */
645 if (*f
== '-' && !(flags
& EXEC_COMMAND_IGNORE_FAILURE
)) {
646 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
648 } else if (*f
== '@' && !separate_argv0
)
649 separate_argv0
= true;
650 else if (*f
== '+' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
651 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
652 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
653 flags
|= EXEC_COMMAND_NO_SETUID
;
654 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
655 flags
&= ~EXEC_COMMAND_NO_SETUID
;
656 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
662 r
= unit_full_printf(u
, f
, &path
);
664 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
665 "Failed to resolve unit specifiers on %s%s: %m",
666 f
, ignore
? ", ignoring" : "");
667 return ignore
? 0 : -ENOEXEC
;
671 /* First word is either "-" or "@" with no command. */
672 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
673 "Empty path in command line%s: \"%s\"",
674 ignore
? ", ignoring" : "", rvalue
);
675 return ignore
? 0 : -ENOEXEC
;
677 if (!string_is_safe(path
)) {
678 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
679 "Executable path contains special characters%s: %s",
680 ignore
? ", ignoring" : "", rvalue
);
681 return ignore
? 0 : -ENOEXEC
;
683 if (!path_is_absolute(path
)) {
684 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
685 "Executable path is not absolute%s: %s",
686 ignore
? ", ignoring" : "", rvalue
);
687 return ignore
? 0 : -ENOEXEC
;
689 if (endswith(path
, "/")) {
690 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
691 "Executable path specifies a directory%s: %s",
692 ignore
? ", ignoring" : "", rvalue
);
693 return ignore
? 0 : -ENOEXEC
;
696 if (!separate_argv0
) {
699 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
709 path_kill_slashes(path
);
711 while (!isempty(p
)) {
712 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
714 /* Check explicitly for an unquoted semicolon as
715 * command separator token. */
716 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
718 p
+= strspn(p
, WHITESPACE
);
723 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
724 * extract_first_word() would return the same for all of those. */
725 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
729 p
+= strspn(p
, WHITESPACE
);
731 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
742 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
746 return ignore
? 0 : -ENOEXEC
;
748 r
= unit_full_printf(u
, word
, &resolved
);
750 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
751 "Failed to resolve unit specifiers on %s%s: %m",
752 word
, ignore
? ", ignoring" : "");
753 return ignore
? 0 : -ENOEXEC
;
756 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
758 n
[nlen
++] = resolved
;
764 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
765 "Empty executable name or zeroeth argument%s: %s",
766 ignore
? ", ignoring" : "", rvalue
);
767 return ignore
? 0 : -ENOEXEC
;
770 nce
= new0(ExecCommand
, 1);
778 exec_command_append_list(e
, nce
);
780 /* Do not _cleanup_free_ these. */
791 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
792 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
794 int config_parse_socket_bindtodevice(
796 const char *filename
,
799 unsigned section_line
,
814 if (rvalue
[0] && !streq(rvalue
, "*")) {
815 if (!ifname_valid(rvalue
)) {
816 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
826 free(s
->bind_to_device
);
827 s
->bind_to_device
= n
;
832 int config_parse_exec_input(
834 const char *filename
,
837 unsigned section_line
,
844 ExecContext
*c
= data
;
855 n
= startswith(rvalue
, "fd:");
857 _cleanup_free_
char *resolved
= NULL
;
859 r
= unit_full_printf(u
, n
, &resolved
);
861 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
863 if (isempty(resolved
))
864 resolved
= mfree(resolved
);
865 else if (!fdname_is_valid(resolved
)) {
866 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name: %s", resolved
);
870 free_and_replace(c
->stdio_fdname
[STDIN_FILENO
], resolved
);
872 ei
= EXEC_INPUT_NAMED_FD
;
874 } else if ((n
= startswith(rvalue
, "file:"))) {
875 _cleanup_free_
char *resolved
= NULL
;
877 r
= unit_full_printf(u
, n
, &resolved
);
879 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
881 if (!path_is_absolute(resolved
)) {
882 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires an absolute path name: %s", resolved
);
886 if (!path_is_normalized(resolved
)) {
887 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires a normalized path name: %s", resolved
);
891 free_and_replace(c
->stdio_file
[STDIN_FILENO
], resolved
);
893 ei
= EXEC_INPUT_FILE
;
896 ei
= exec_input_from_string(rvalue
);
898 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
907 int config_parse_exec_input_text(
909 const char *filename
,
912 unsigned section_line
,
919 _cleanup_free_
char *unescaped
= NULL
, *resolved
= NULL
;
920 ExecContext
*c
= data
;
931 if (isempty(rvalue
)) {
932 /* Reset if the empty string is assigned */
933 c
->stdin_data
= mfree(c
->stdin_data
);
934 c
->stdin_data_size
= 0;
938 r
= cunescape(rvalue
, 0, &unescaped
);
940 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to decode C escaped text: %s", rvalue
);
942 r
= unit_full_printf(u
, unescaped
, &resolved
);
944 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", unescaped
);
946 sz
= strlen(resolved
);
947 if (c
->stdin_data_size
+ sz
+ 1 < c
->stdin_data_size
|| /* check for overflow */
948 c
->stdin_data_size
+ sz
+ 1 > EXEC_STDIN_DATA_MAX
) {
949 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
);
953 p
= realloc(c
->stdin_data
, c
->stdin_data_size
+ sz
+ 1);
957 *((char*) mempcpy((char*) p
+ c
->stdin_data_size
, resolved
, sz
)) = '\n';
960 c
->stdin_data_size
+= sz
+ 1;
965 int config_parse_exec_input_data(
967 const char *filename
,
970 unsigned section_line
,
977 _cleanup_free_
void *p
= NULL
;
978 ExecContext
*c
= data
;
988 if (isempty(rvalue
)) {
989 /* Reset if the empty string is assigned */
990 c
->stdin_data
= mfree(c
->stdin_data
);
991 c
->stdin_data_size
= 0;
995 r
= unbase64mem(rvalue
, (size_t) -1, &p
, &sz
);
997 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to decode base64 data, ignoring: %s", rvalue
);
1001 if (c
->stdin_data_size
+ sz
< c
->stdin_data_size
|| /* check for overflow */
1002 c
->stdin_data_size
+ sz
> EXEC_STDIN_DATA_MAX
) {
1003 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
);
1007 q
= realloc(c
->stdin_data
, c
->stdin_data_size
+ sz
);
1011 memcpy((uint8_t*) q
+ c
->stdin_data_size
, p
, sz
);
1014 c
->stdin_data_size
+= sz
;
1019 int config_parse_exec_output(
1021 const char *filename
,
1023 const char *section
,
1024 unsigned section_line
,
1031 _cleanup_free_
char *resolved
= NULL
;
1033 ExecContext
*c
= data
;
1044 n
= startswith(rvalue
, "fd:");
1046 r
= unit_full_printf(u
, n
, &resolved
);
1048 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
1050 if (isempty(resolved
))
1051 resolved
= mfree(resolved
);
1052 else if (!fdname_is_valid(resolved
)) {
1053 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name: %s", resolved
);
1057 eo
= EXEC_OUTPUT_NAMED_FD
;
1059 } else if ((n
= startswith(rvalue
, "file:"))) {
1061 r
= unit_full_printf(u
, n
, &resolved
);
1063 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
1065 if (!path_is_absolute(resolved
)) {
1066 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires an absolute path name: %s", resolved
);
1070 if (!path_is_normalized(resolved
)) {
1071 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires a normalized path name, ignoring: %s", resolved
);
1075 eo
= EXEC_OUTPUT_FILE
;
1078 eo
= exec_output_from_string(rvalue
);
1080 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
1085 if (streq(lvalue
, "StandardOutput")) {
1086 if (eo
== EXEC_OUTPUT_NAMED_FD
)
1087 free_and_replace(c
->stdio_fdname
[STDOUT_FILENO
], resolved
);
1089 free_and_replace(c
->stdio_file
[STDOUT_FILENO
], resolved
);
1094 assert(streq(lvalue
, "StandardError"));
1096 if (eo
== EXEC_OUTPUT_NAMED_FD
)
1097 free_and_replace(c
->stdio_fdname
[STDERR_FILENO
], resolved
);
1099 free_and_replace(c
->stdio_file
[STDERR_FILENO
], resolved
);
1107 int config_parse_exec_io_class(const char *unit
,
1108 const char *filename
,
1110 const char *section
,
1111 unsigned section_line
,
1118 ExecContext
*c
= data
;
1126 x
= ioprio_class_from_string(rvalue
);
1128 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
1132 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
1133 c
->ioprio_set
= true;
1138 int config_parse_exec_io_priority(const char *unit
,
1139 const char *filename
,
1141 const char *section
,
1142 unsigned section_line
,
1149 ExecContext
*c
= data
;
1157 r
= ioprio_parse_priority(rvalue
, &i
);
1159 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
1163 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
1164 c
->ioprio_set
= true;
1169 int config_parse_exec_cpu_sched_policy(const char *unit
,
1170 const char *filename
,
1172 const char *section
,
1173 unsigned section_line
,
1181 ExecContext
*c
= data
;
1189 x
= sched_policy_from_string(rvalue
);
1191 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1195 c
->cpu_sched_policy
= x
;
1196 /* Moving to or from real-time policy? We need to adjust the priority */
1197 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
1198 c
->cpu_sched_set
= true;
1203 int config_parse_exec_cpu_sched_prio(const char *unit
,
1204 const char *filename
,
1206 const char *section
,
1207 unsigned section_line
,
1214 ExecContext
*c
= data
;
1222 r
= safe_atoi(rvalue
, &i
);
1224 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1228 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1229 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1230 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1232 if (i
< min
|| i
> max
) {
1233 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1237 c
->cpu_sched_priority
= i
;
1238 c
->cpu_sched_set
= true;
1243 int config_parse_exec_cpu_affinity(const char *unit
,
1244 const char *filename
,
1246 const char *section
,
1247 unsigned section_line
,
1254 ExecContext
*c
= data
;
1255 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1263 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1268 /* An empty assignment resets the CPU list */
1269 c
->cpuset
= cpu_set_mfree(c
->cpuset
);
1270 c
->cpuset_ncpus
= 0;
1277 c
->cpuset_ncpus
= (unsigned) ncpus
;
1281 if (c
->cpuset_ncpus
< (unsigned) ncpus
) {
1282 CPU_OR_S(CPU_ALLOC_SIZE(c
->cpuset_ncpus
), cpuset
, c
->cpuset
, cpuset
);
1283 CPU_FREE(c
->cpuset
);
1286 c
->cpuset_ncpus
= (unsigned) ncpus
;
1290 CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus
), c
->cpuset
, c
->cpuset
, cpuset
);
1295 int config_parse_exec_secure_bits(const char *unit
,
1296 const char *filename
,
1298 const char *section
,
1299 unsigned section_line
,
1306 ExecContext
*c
= data
;
1314 if (isempty(rvalue
)) {
1315 /* An empty assignment resets the field */
1320 r
= secure_bits_from_string(rvalue
);
1324 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1325 "Invalid syntax, ignoring: %s", rvalue
);
1334 int config_parse_capability_set(
1336 const char *filename
,
1338 const char *section
,
1339 unsigned section_line
,
1346 uint64_t *capability_set
= data
;
1347 uint64_t sum
= 0, initial
= 0;
1348 bool invert
= false;
1356 if (rvalue
[0] == '~') {
1361 if (streq(lvalue
, "CapabilityBoundingSet"))
1362 initial
= CAP_ALL
; /* initialized to all bits on */
1363 /* else "AmbientCapabilities" initialized to all bits off */
1365 r
= capability_set_from_string(rvalue
, &sum
);
1369 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word: %s", rvalue
);
1373 if (sum
== 0 || *capability_set
== initial
)
1374 /* "", "~" or uninitialized data -> replace */
1375 *capability_set
= invert
? ~sum
: sum
;
1377 /* previous data -> merge */
1379 *capability_set
&= ~sum
;
1381 *capability_set
|= sum
;
1387 int config_parse_limit(
1389 const char *filename
,
1391 const char *section
,
1392 unsigned section_line
,
1399 struct rlimit
**rl
= data
, d
= {};
1407 r
= rlimit_parse(ltype
, rvalue
, &d
);
1409 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1413 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1420 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1428 #if HAVE_SYSV_COMPAT
1429 int config_parse_sysv_priority(const char *unit
,
1430 const char *filename
,
1432 const char *section
,
1433 unsigned section_line
,
1440 int *priority
= data
;
1448 r
= safe_atoi(rvalue
, &i
);
1449 if (r
< 0 || i
< 0) {
1450 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1454 *priority
= (int) i
;
1459 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1460 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1462 int config_parse_exec_mount_flags(
1464 const char *filename
,
1466 const char *section
,
1467 unsigned section_line
,
1475 ExecContext
*c
= data
;
1483 r
= mount_propagation_flags_from_string(rvalue
, &c
->mount_flags
);
1485 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1490 int config_parse_exec_selinux_context(
1492 const char *filename
,
1494 const char *section
,
1495 unsigned section_line
,
1502 ExecContext
*c
= data
;
1513 if (isempty(rvalue
)) {
1514 c
->selinux_context
= mfree(c
->selinux_context
);
1515 c
->selinux_context_ignore
= false;
1519 if (rvalue
[0] == '-') {
1525 r
= unit_full_printf(u
, rvalue
, &k
);
1527 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1528 "Failed to resolve specifiers%s: %m",
1529 ignore
? ", ignoring" : "");
1530 return ignore
? 0 : -ENOEXEC
;
1533 free(c
->selinux_context
);
1534 c
->selinux_context
= k
;
1535 c
->selinux_context_ignore
= ignore
;
1540 int config_parse_exec_apparmor_profile(
1542 const char *filename
,
1544 const char *section
,
1545 unsigned section_line
,
1552 ExecContext
*c
= data
;
1563 if (isempty(rvalue
)) {
1564 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1565 c
->apparmor_profile_ignore
= false;
1569 if (rvalue
[0] == '-') {
1575 r
= unit_full_printf(u
, rvalue
, &k
);
1577 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1578 "Failed to resolve specifiers%s: %m",
1579 ignore
? ", ignoring" : "");
1580 return ignore
? 0 : -ENOEXEC
;
1583 free(c
->apparmor_profile
);
1584 c
->apparmor_profile
= k
;
1585 c
->apparmor_profile_ignore
= ignore
;
1590 int config_parse_exec_smack_process_label(
1592 const char *filename
,
1594 const char *section
,
1595 unsigned section_line
,
1602 ExecContext
*c
= data
;
1613 if (isempty(rvalue
)) {
1614 c
->smack_process_label
= mfree(c
->smack_process_label
);
1615 c
->smack_process_label_ignore
= false;
1619 if (rvalue
[0] == '-') {
1625 r
= unit_full_printf(u
, rvalue
, &k
);
1627 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1628 "Failed to resolve specifiers%s: %m",
1629 ignore
? ", ignoring" : "");
1630 return ignore
? 0 : -ENOEXEC
;
1633 free(c
->smack_process_label
);
1634 c
->smack_process_label
= k
;
1635 c
->smack_process_label_ignore
= ignore
;
1640 int config_parse_timer(const char *unit
,
1641 const char *filename
,
1643 const char *section
,
1644 unsigned section_line
,
1655 CalendarSpec
*c
= NULL
;
1657 _cleanup_free_
char *k
= NULL
;
1665 if (isempty(rvalue
)) {
1666 /* Empty assignment resets list */
1667 timer_free_values(t
);
1671 b
= timer_base_from_string(lvalue
);
1673 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1677 r
= unit_full_printf(u
, rvalue
, &k
);
1679 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1683 if (b
== TIMER_CALENDAR
) {
1684 if (calendar_spec_from_string(k
, &c
) < 0) {
1685 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1689 if (parse_sec(k
, &usec
) < 0) {
1690 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1695 v
= new0(TimerValue
, 1);
1697 calendar_spec_free(c
);
1703 v
->calendar_spec
= c
;
1705 LIST_PREPEND(value
, t
->values
, v
);
1710 int config_parse_trigger_unit(
1712 const char *filename
,
1714 const char *section
,
1715 unsigned section_line
,
1722 _cleanup_free_
char *p
= NULL
;
1732 if (!hashmap_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1733 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1737 r
= unit_name_printf(u
, rvalue
, &p
);
1739 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1743 type
= unit_name_to_type(p
);
1745 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1748 if (unit_has_name(u
, p
)) {
1749 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Units cannot trigger themselves, ignoring: %s", rvalue
);
1753 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1755 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1762 int config_parse_path_spec(const char *unit
,
1763 const char *filename
,
1765 const char *section
,
1766 unsigned section_line
,
1776 _cleanup_free_
char *k
= NULL
;
1784 if (isempty(rvalue
)) {
1785 /* Empty assignment clears list */
1790 b
= path_type_from_string(lvalue
);
1792 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1796 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1798 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1802 if (!path_is_absolute(k
)) {
1803 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1807 s
= new0(PathSpec
, 1);
1812 s
->path
= path_kill_slashes(k
);
1817 LIST_PREPEND(spec
, p
->specs
, s
);
1822 int config_parse_socket_service(
1824 const char *filename
,
1826 const char *section
,
1827 unsigned section_line
,
1834 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1835 _cleanup_free_
char *p
= NULL
;
1845 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1847 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", rvalue
);
1851 if (!endswith(p
, ".service")) {
1852 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service: %s", rvalue
);
1856 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1858 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s: %s", rvalue
, bus_error_message(&error
, r
));
1862 unit_ref_set(&s
->service
, UNIT(s
), x
);
1867 int config_parse_fdname(
1869 const char *filename
,
1871 const char *section
,
1872 unsigned section_line
,
1879 _cleanup_free_
char *p
= NULL
;
1888 if (isempty(rvalue
)) {
1889 s
->fdname
= mfree(s
->fdname
);
1893 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1895 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1899 if (!fdname_is_valid(p
)) {
1900 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1904 return free_and_replace(s
->fdname
, p
);
1907 int config_parse_service_sockets(
1909 const char *filename
,
1911 const char *section
,
1912 unsigned section_line
,
1930 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1932 r
= extract_first_word(&p
, &word
, NULL
, 0);
1938 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1942 r
= unit_name_printf(UNIT(s
), word
, &k
);
1944 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1948 if (!endswith(k
, ".socket")) {
1949 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1953 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1955 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1957 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1959 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1965 int config_parse_bus_name(
1967 const char *filename
,
1969 const char *section
,
1970 unsigned section_line
,
1977 _cleanup_free_
char *k
= NULL
;
1986 r
= unit_full_printf(u
, rvalue
, &k
);
1988 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1992 if (!service_name_is_valid(k
)) {
1993 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1997 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
2000 int config_parse_service_timeout(
2002 const char *filename
,
2004 const char *section
,
2005 unsigned section_line
,
2012 Service
*s
= userdata
;
2021 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
2023 r
= parse_sec(rvalue
, &usec
);
2025 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
2029 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
2030 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
2031 * all other timeouts. */
2033 usec
= USEC_INFINITY
;
2035 if (!streq(lvalue
, "TimeoutStopSec")) {
2036 s
->start_timeout_defined
= true;
2037 s
->timeout_start_usec
= usec
;
2040 if (!streq(lvalue
, "TimeoutStartSec"))
2041 s
->timeout_stop_usec
= usec
;
2046 int config_parse_sec_fix_0(
2048 const char *filename
,
2050 const char *section
,
2051 unsigned section_line
,
2058 usec_t
*usec
= data
;
2066 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
2067 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
2070 r
= parse_sec_fix_0(rvalue
, usec
);
2072 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
2079 int config_parse_user_group(
2081 const char *filename
,
2083 const char *section
,
2084 unsigned section_line
,
2091 char **user
= data
, *n
;
2100 if (isempty(rvalue
))
2103 _cleanup_free_
char *k
= NULL
;
2105 r
= unit_full_printf(u
, rvalue
, &k
);
2107 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
2111 if (!valid_user_group_name_or_id(k
)) {
2112 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
2126 int config_parse_user_group_strv(
2128 const char *filename
,
2130 const char *section
,
2131 unsigned section_line
,
2138 char ***users
= data
;
2148 if (isempty(rvalue
)) {
2149 *users
= strv_free(*users
);
2155 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2157 r
= extract_first_word(&p
, &word
, NULL
, 0);
2163 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax: %s", rvalue
);
2167 r
= unit_full_printf(u
, word
, &k
);
2169 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", word
);
2173 if (!valid_user_group_name_or_id(k
)) {
2174 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
2178 r
= strv_push(users
, k
);
2188 int config_parse_working_directory(
2190 const char *filename
,
2192 const char *section
,
2193 unsigned section_line
,
2200 ExecContext
*c
= data
;
2211 if (rvalue
[0] == '-') {
2217 if (streq(rvalue
, "~")) {
2218 c
->working_directory_home
= true;
2219 c
->working_directory
= mfree(c
->working_directory
);
2221 _cleanup_free_
char *k
= NULL
;
2223 r
= unit_full_printf(u
, rvalue
, &k
);
2225 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2226 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2227 rvalue
, missing_ok
? ", ignoring" : "");
2228 return missing_ok
? 0 : -ENOEXEC
;
2231 path_kill_slashes(k
);
2233 if (!utf8_is_valid(k
)) {
2234 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2235 return missing_ok
? 0 : -ENOEXEC
;
2238 if (!path_is_absolute(k
)) {
2239 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2240 "Working directory path '%s' is not absolute%s.",
2241 rvalue
, missing_ok
? ", ignoring" : "");
2242 return missing_ok
? 0 : -ENOEXEC
;
2245 c
->working_directory_home
= false;
2246 free_and_replace(c
->working_directory
, k
);
2249 c
->working_directory_missing_ok
= missing_ok
;
2253 int config_parse_unit_env_file(const char *unit
,
2254 const char *filename
,
2256 const char *section
,
2257 unsigned section_line
,
2266 _cleanup_free_
char *n
= NULL
;
2274 if (isempty(rvalue
)) {
2275 /* Empty assignment frees the list */
2276 *env
= strv_free(*env
);
2280 r
= unit_full_printf(u
, rvalue
, &n
);
2282 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2286 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2287 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2291 r
= strv_extend(env
, n
);
2298 int config_parse_environ(
2300 const char *filename
,
2302 const char *section
,
2303 unsigned section_line
,
2320 if (isempty(rvalue
)) {
2321 /* Empty assignment resets the list */
2322 *env
= strv_free(*env
);
2326 for (p
= rvalue
;; ) {
2327 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2329 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2335 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2336 "Invalid syntax, ignoring: %s", rvalue
);
2341 r
= unit_full_printf(u
, word
, &k
);
2343 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2344 "Failed to resolve specifiers, ignoring: %s", word
);
2352 if (!env_assignment_is_valid(k
)) {
2353 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2354 "Invalid environment assignment, ignoring: %s", k
);
2358 r
= strv_env_replace(env
, k
);
2366 int config_parse_pass_environ(
2368 const char *filename
,
2370 const char *section
,
2371 unsigned section_line
,
2378 const char *whole_rvalue
= rvalue
;
2379 _cleanup_strv_free_
char **n
= NULL
;
2380 size_t nlen
= 0, nbufsize
= 0;
2381 char*** passenv
= data
;
2390 if (isempty(rvalue
)) {
2391 /* Empty assignment resets the list */
2392 *passenv
= strv_free(*passenv
);
2397 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2399 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2405 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2406 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2411 r
= unit_full_printf(u
, word
, &k
);
2413 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2414 "Failed to resolve specifiers, ignoring: %s", word
);
2422 if (!env_name_is_valid(k
)) {
2423 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2424 "Invalid environment name for %s, ignoring: %s", lvalue
, k
);
2428 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2437 r
= strv_extend_strv(passenv
, n
, true);
2445 int config_parse_unset_environ(
2447 const char *filename
,
2449 const char *section
,
2450 unsigned section_line
,
2457 _cleanup_strv_free_
char **n
= NULL
;
2458 const char *whole_rvalue
= rvalue
;
2459 size_t nlen
= 0, nbufsize
= 0;
2460 char*** unsetenv
= data
;
2469 if (isempty(rvalue
)) {
2470 /* Empty assignment resets the list */
2471 *unsetenv
= strv_free(*unsetenv
);
2476 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2478 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2484 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2485 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2490 r
= unit_full_printf(u
, word
, &k
);
2492 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2493 "Failed to resolve specifiers, ignoring: %s", word
);
2501 if (!env_assignment_is_valid(k
) && !env_name_is_valid(k
)) {
2502 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2503 "Invalid environment name or assignment %s, ignoring: %s", lvalue
, k
);
2507 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2516 r
= strv_extend_strv(unsetenv
, n
, true);
2524 int config_parse_log_extra_fields(
2526 const char *filename
,
2528 const char *section
,
2529 unsigned section_line
,
2536 ExecContext
*c
= data
;
2546 if (isempty(rvalue
)) {
2547 exec_context_free_log_extra_fields(c
);
2551 for (p
= rvalue
;; ) {
2552 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2556 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2562 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2566 r
= unit_full_printf(u
, word
, &k
);
2568 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring field: %m", word
);
2572 eq
= strchr(k
, '=');
2574 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field lacks '=' character, ignoring field: %s", k
);
2578 if (!journal_field_valid(k
, eq
-k
, false)) {
2579 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field name is invalid, ignoring field: %s", k
);
2583 t
= reallocarray(c
->log_extra_fields
, c
->n_log_extra_fields
+1, sizeof(struct iovec
));
2587 c
->log_extra_fields
= t
;
2588 c
->log_extra_fields
[c
->n_log_extra_fields
++] = IOVEC_MAKE_STRING(k
);
2596 int config_parse_ip_tos(const char *unit
,
2597 const char *filename
,
2599 const char *section
,
2600 unsigned section_line
,
2607 int *ip_tos
= data
, x
;
2614 x
= ip_tos_from_string(rvalue
);
2616 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2624 int config_parse_unit_condition_path(
2626 const char *filename
,
2628 const char *section
,
2629 unsigned section_line
,
2636 _cleanup_free_
char *p
= NULL
;
2637 Condition
**list
= data
, *c
;
2638 ConditionType t
= ltype
;
2639 bool trigger
, negate
;
2648 if (isempty(rvalue
)) {
2649 /* Empty assignment resets the list */
2650 *list
= condition_free_list(*list
);
2654 trigger
= rvalue
[0] == '|';
2658 negate
= rvalue
[0] == '!';
2662 r
= unit_full_printf(u
, rvalue
, &p
);
2664 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2668 if (!path_is_absolute(p
)) {
2669 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2673 c
= condition_new(t
, p
, trigger
, negate
);
2677 LIST_PREPEND(conditions
, *list
, c
);
2681 int config_parse_unit_condition_string(
2683 const char *filename
,
2685 const char *section
,
2686 unsigned section_line
,
2693 _cleanup_free_
char *s
= NULL
;
2694 Condition
**list
= data
, *c
;
2695 ConditionType t
= ltype
;
2696 bool trigger
, negate
;
2705 if (isempty(rvalue
)) {
2706 /* Empty assignment resets the list */
2707 *list
= condition_free_list(*list
);
2711 trigger
= rvalue
[0] == '|';
2715 negate
= rvalue
[0] == '!';
2719 r
= unit_full_printf(u
, rvalue
, &s
);
2721 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2725 c
= condition_new(t
, s
, trigger
, negate
);
2729 LIST_PREPEND(conditions
, *list
, c
);
2733 int config_parse_unit_condition_null(
2735 const char *filename
,
2737 const char *section
,
2738 unsigned section_line
,
2745 Condition
**list
= data
, *c
;
2746 bool trigger
, negate
;
2754 if (isempty(rvalue
)) {
2755 /* Empty assignment resets the list */
2756 *list
= condition_free_list(*list
);
2760 trigger
= rvalue
[0] == '|';
2764 negate
= rvalue
[0] == '!';
2768 b
= parse_boolean(rvalue
);
2770 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2777 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2781 LIST_PREPEND(conditions
, *list
, c
);
2785 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2786 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2788 int config_parse_unit_requires_mounts_for(
2790 const char *filename
,
2792 const char *section
,
2793 unsigned section_line
,
2809 for (p
= rvalue
;; ) {
2810 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2812 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2818 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2819 "Invalid syntax, ignoring: %s", rvalue
);
2823 if (!utf8_is_valid(word
)) {
2824 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2828 r
= unit_full_printf(u
, word
, &resolved
);
2830 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit name \"%s\", ignoring: %m", word
);
2834 r
= unit_require_mounts_for(u
, resolved
, UNIT_DEPENDENCY_FILE
);
2836 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", resolved
);
2842 int config_parse_documentation(const char *unit
,
2843 const char *filename
,
2845 const char *section
,
2846 unsigned section_line
,
2862 if (isempty(rvalue
)) {
2863 /* Empty assignment resets the list */
2864 u
->documentation
= strv_free(u
->documentation
);
2868 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2869 rvalue
, data
, userdata
);
2873 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2875 if (documentation_url_is_valid(*a
))
2878 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2889 int config_parse_syscall_filter(
2891 const char *filename
,
2893 const char *section
,
2894 unsigned section_line
,
2901 ExecContext
*c
= data
;
2903 bool invert
= false;
2912 if (isempty(rvalue
)) {
2913 /* Empty assignment resets the list */
2914 c
->syscall_filter
= hashmap_free(c
->syscall_filter
);
2915 c
->syscall_whitelist
= false;
2919 if (rvalue
[0] == '~') {
2924 if (!c
->syscall_filter
) {
2925 c
->syscall_filter
= hashmap_new(NULL
);
2926 if (!c
->syscall_filter
)
2930 /* Allow everything but the ones listed */
2931 c
->syscall_whitelist
= false;
2933 /* Allow nothing but the ones listed */
2934 c
->syscall_whitelist
= true;
2936 /* Accept default syscalls if we are on a whitelist */
2937 r
= seccomp_parse_syscall_filter("@default", -1, c
->syscall_filter
, SECCOMP_PARSE_WHITELIST
);
2945 _cleanup_free_
char *word
= NULL
, *name
= NULL
;
2948 r
= extract_first_word(&p
, &word
, NULL
, 0);
2954 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2958 r
= parse_syscall_and_errno(word
, &name
, &num
);
2960 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse syscall:errno, ignoring: %s", word
);
2964 r
= seccomp_parse_syscall_filter_full(name
, num
, c
->syscall_filter
,
2965 SECCOMP_PARSE_LOG
|SECCOMP_PARSE_PERMISSIVE
|(invert
? SECCOMP_PARSE_INVERT
: 0)|(c
->syscall_whitelist
? SECCOMP_PARSE_WHITELIST
: 0),
2966 unit
, filename
, line
);
2974 int config_parse_syscall_archs(
2976 const char *filename
,
2978 const char *section
,
2979 unsigned section_line
,
2990 if (isempty(rvalue
)) {
2991 *archs
= set_free(*archs
);
2995 r
= set_ensure_allocated(archs
, NULL
);
2999 for (p
= rvalue
;;) {
3000 _cleanup_free_
char *word
= NULL
;
3003 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3009 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3010 "Invalid syntax, ignoring: %s", rvalue
);
3014 r
= seccomp_arch_from_string(word
, &a
);
3016 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3017 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
3021 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
3027 int config_parse_syscall_errno(
3029 const char *filename
,
3031 const char *section
,
3032 unsigned section_line
,
3039 ExecContext
*c
= data
;
3046 if (isempty(rvalue
)) {
3047 /* Empty assignment resets to KILL */
3048 c
->syscall_errno
= 0;
3052 e
= parse_errno(rvalue
);
3054 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
3058 c
->syscall_errno
= e
;
3062 int config_parse_address_families(
3064 const char *filename
,
3066 const char *section
,
3067 unsigned section_line
,
3074 ExecContext
*c
= data
;
3075 bool invert
= false;
3083 if (isempty(rvalue
)) {
3084 /* Empty assignment resets the list */
3085 c
->address_families
= set_free(c
->address_families
);
3086 c
->address_families_whitelist
= false;
3090 if (rvalue
[0] == '~') {
3095 if (!c
->address_families
) {
3096 c
->address_families
= set_new(NULL
);
3097 if (!c
->address_families
)
3100 c
->address_families_whitelist
= !invert
;
3103 for (p
= rvalue
;;) {
3104 _cleanup_free_
char *word
= NULL
;
3107 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3113 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3114 "Invalid syntax, ignoring: %s", rvalue
);
3118 af
= af_from_name(word
);
3120 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3121 "Failed to parse address family \"%s\", ignoring: %m", word
);
3125 /* If we previously wanted to forbid an address family and now
3126 * we want to allow it, then just remove it from the list.
3128 if (!invert
== c
->address_families_whitelist
) {
3129 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
3133 set_remove(c
->address_families
, INT_TO_PTR(af
));
3137 int config_parse_restrict_namespaces(
3139 const char *filename
,
3141 const char *section
,
3142 unsigned section_line
,
3149 ExecContext
*c
= data
;
3150 bool invert
= false;
3153 if (isempty(rvalue
)) {
3154 /* Reset to the default. */
3155 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3159 if (rvalue
[0] == '~') {
3164 r
= parse_boolean(rvalue
);
3166 c
->restrict_namespaces
= 0;
3168 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3170 /* Not a boolean argument, in this case it's a list of namespace types. */
3172 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
3174 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
3180 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
3186 int config_parse_unit_slice(
3188 const char *filename
,
3190 const char *section
,
3191 unsigned section_line
,
3198 _cleanup_free_
char *k
= NULL
;
3199 Unit
*u
= userdata
, *slice
= NULL
;
3207 r
= unit_name_printf(u
, rvalue
, &k
);
3209 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
3213 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
3215 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
3219 r
= unit_set_slice(u
, slice
);
3221 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
3228 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
3230 int config_parse_cpu_weight(
3232 const char *filename
,
3234 const char *section
,
3235 unsigned section_line
,
3242 uint64_t *weight
= data
;
3249 r
= cg_weight_parse(rvalue
, weight
);
3251 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
3258 int config_parse_cpu_shares(
3260 const char *filename
,
3262 const char *section
,
3263 unsigned section_line
,
3270 uint64_t *shares
= data
;
3277 r
= cg_cpu_shares_parse(rvalue
, shares
);
3279 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
3286 int config_parse_cpu_quota(
3288 const char *filename
,
3290 const char *section
,
3291 unsigned section_line
,
3298 CGroupContext
*c
= data
;
3305 if (isempty(rvalue
)) {
3306 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3310 r
= parse_percent_unbounded(rvalue
);
3312 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3316 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3320 int config_parse_memory_limit(
3322 const char *filename
,
3324 const char *section
,
3325 unsigned section_line
,
3332 CGroupContext
*c
= data
;
3333 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3336 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3338 r
= parse_percent(rvalue
);
3340 r
= parse_size(rvalue
, 1024, &bytes
);
3342 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3346 bytes
= physical_memory_scale(r
, 100U);
3348 if (bytes
<= 0 || bytes
>= UINT64_MAX
) {
3349 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3354 if (streq(lvalue
, "MemoryLow"))
3355 c
->memory_low
= bytes
;
3356 else if (streq(lvalue
, "MemoryHigh"))
3357 c
->memory_high
= bytes
;
3358 else if (streq(lvalue
, "MemoryMax"))
3359 c
->memory_max
= bytes
;
3360 else if (streq(lvalue
, "MemorySwapMax"))
3361 c
->memory_swap_max
= bytes
;
3362 else if (streq(lvalue
, "MemoryLimit"))
3363 c
->memory_limit
= bytes
;
3370 int config_parse_tasks_max(
3372 const char *filename
,
3374 const char *section
,
3375 unsigned section_line
,
3382 uint64_t *tasks_max
= data
, v
;
3386 if (isempty(rvalue
)) {
3387 *tasks_max
= u
->manager
->default_tasks_max
;
3391 if (streq(rvalue
, "infinity")) {
3392 *tasks_max
= CGROUP_LIMIT_MAX
;
3396 r
= parse_percent(rvalue
);
3398 r
= safe_atou64(rvalue
, &v
);
3400 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3404 v
= system_tasks_max_scale(r
, 100U);
3406 if (v
<= 0 || v
>= UINT64_MAX
) {
3407 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3415 int config_parse_delegate(
3417 const char *filename
,
3419 const char *section
,
3420 unsigned section_line
,
3427 CGroupContext
*c
= data
;
3430 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3431 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3432 * mask to delegate. */
3434 if (isempty(rvalue
)) {
3436 c
->delegate_controllers
= 0;
3440 r
= parse_boolean(rvalue
);
3442 const char *p
= rvalue
;
3443 CGroupMask mask
= 0;
3446 _cleanup_free_
char *word
= NULL
;
3447 CGroupController cc
;
3449 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3455 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
3459 cc
= cgroup_controller_from_string(word
);
3461 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid controller name '%s', ignoring", rvalue
);
3465 mask
|= CGROUP_CONTROLLER_TO_MASK(cc
);
3469 c
->delegate_controllers
|= mask
;
3473 c
->delegate_controllers
= _CGROUP_MASK_ALL
;
3475 c
->delegate
= false;
3476 c
->delegate_controllers
= 0;
3482 int config_parse_device_allow(
3484 const char *filename
,
3486 const char *section
,
3487 unsigned section_line
,
3494 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3495 CGroupContext
*c
= data
;
3496 CGroupDeviceAllow
*a
;
3497 const char *m
= NULL
;
3501 if (isempty(rvalue
)) {
3502 while (c
->device_allow
)
3503 cgroup_context_free_device_allow(c
, c
->device_allow
);
3508 r
= unit_full_printf(userdata
, rvalue
, &t
);
3510 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3511 "Failed to resolve specifiers in %s, ignoring: %m",
3515 n
= strcspn(t
, WHITESPACE
);
3517 path
= strndup(t
, n
);
3521 if (!is_deviceallow_pattern(path
) &&
3522 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3523 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3527 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3531 if (!in_charset(m
, "rwm")) {
3532 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3536 a
= new0(CGroupDeviceAllow
, 1);
3542 a
->r
= !!strchr(m
, 'r');
3543 a
->w
= !!strchr(m
, 'w');
3544 a
->m
= !!strchr(m
, 'm');
3546 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3550 int config_parse_io_weight(
3552 const char *filename
,
3554 const char *section
,
3555 unsigned section_line
,
3562 uint64_t *weight
= data
;
3569 r
= cg_weight_parse(rvalue
, weight
);
3571 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3578 int config_parse_io_device_weight(
3580 const char *filename
,
3582 const char *section
,
3583 unsigned section_line
,
3590 _cleanup_free_
char *path
= NULL
;
3591 CGroupIODeviceWeight
*w
;
3592 CGroupContext
*c
= data
;
3602 if (isempty(rvalue
)) {
3603 while (c
->io_device_weights
)
3604 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3609 n
= strcspn(rvalue
, WHITESPACE
);
3610 weight
= rvalue
+ n
;
3611 weight
+= strspn(weight
, WHITESPACE
);
3613 if (isempty(weight
)) {
3614 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3618 path
= strndup(rvalue
, n
);
3622 if (!path_startswith(path
, "/dev") &&
3623 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3624 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3628 r
= cg_weight_parse(weight
, &u
);
3630 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3634 assert(u
!= CGROUP_WEIGHT_INVALID
);
3636 w
= new0(CGroupIODeviceWeight
, 1);
3645 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3649 int config_parse_io_limit(
3651 const char *filename
,
3653 const char *section
,
3654 unsigned section_line
,
3661 _cleanup_free_
char *path
= NULL
;
3662 CGroupIODeviceLimit
*l
= NULL
, *t
;
3663 CGroupContext
*c
= data
;
3664 CGroupIOLimitType type
;
3674 type
= cgroup_io_limit_type_from_string(lvalue
);
3677 if (isempty(rvalue
)) {
3678 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3679 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3683 n
= strcspn(rvalue
, WHITESPACE
);
3685 limit
+= strspn(limit
, WHITESPACE
);
3688 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3692 path
= strndup(rvalue
, n
);
3696 if (!path_startswith(path
, "/dev") &&
3697 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3698 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3702 if (streq("infinity", limit
)) {
3703 num
= CGROUP_LIMIT_MAX
;
3705 r
= parse_size(limit
, 1000, &num
);
3706 if (r
< 0 || num
<= 0) {
3707 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3712 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3713 if (path_equal(path
, t
->path
)) {
3720 CGroupIOLimitType ttype
;
3722 l
= new0(CGroupIODeviceLimit
, 1);
3728 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3729 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3731 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3734 l
->limits
[type
] = num
;
3739 int config_parse_blockio_weight(
3741 const char *filename
,
3743 const char *section
,
3744 unsigned section_line
,
3751 uint64_t *weight
= data
;
3758 r
= cg_blkio_weight_parse(rvalue
, weight
);
3760 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3767 int config_parse_blockio_device_weight(
3769 const char *filename
,
3771 const char *section
,
3772 unsigned section_line
,
3779 _cleanup_free_
char *path
= NULL
;
3780 CGroupBlockIODeviceWeight
*w
;
3781 CGroupContext
*c
= data
;
3791 if (isempty(rvalue
)) {
3792 while (c
->blockio_device_weights
)
3793 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3798 n
= strcspn(rvalue
, WHITESPACE
);
3799 weight
= rvalue
+ n
;
3800 weight
+= strspn(weight
, WHITESPACE
);
3802 if (isempty(weight
)) {
3803 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3807 path
= strndup(rvalue
, n
);
3811 if (!path_startswith(path
, "/dev") &&
3812 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3813 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3817 r
= cg_blkio_weight_parse(weight
, &u
);
3819 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3823 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3825 w
= new0(CGroupBlockIODeviceWeight
, 1);
3834 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3838 int config_parse_blockio_bandwidth(
3840 const char *filename
,
3842 const char *section
,
3843 unsigned section_line
,
3850 _cleanup_free_
char *path
= NULL
;
3851 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3852 CGroupContext
*c
= data
;
3853 const char *bandwidth
;
3863 read
= streq("BlockIOReadBandwidth", lvalue
);
3865 if (isempty(rvalue
)) {
3866 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3867 b
->rbps
= CGROUP_LIMIT_MAX
;
3868 b
->wbps
= CGROUP_LIMIT_MAX
;
3873 n
= strcspn(rvalue
, WHITESPACE
);
3874 bandwidth
= rvalue
+ n
;
3875 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3878 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3882 path
= strndup(rvalue
, n
);
3886 if (!path_startswith(path
, "/dev") &&
3887 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3888 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3892 r
= parse_size(bandwidth
, 1000, &bytes
);
3893 if (r
< 0 || bytes
<= 0) {
3894 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3898 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3899 if (path_equal(path
, t
->path
)) {
3906 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3912 b
->rbps
= CGROUP_LIMIT_MAX
;
3913 b
->wbps
= CGROUP_LIMIT_MAX
;
3915 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3926 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3928 int config_parse_job_mode_isolate(
3930 const char *filename
,
3932 const char *section
,
3933 unsigned section_line
,
3947 r
= parse_boolean(rvalue
);
3949 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3953 log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue
);
3955 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3959 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode
, exec_preserve_mode
, ExecPreserveMode
, "Failed to parse runtime directory preserve mode");
3961 int config_parse_exec_directories(
3963 const char *filename
,
3965 const char *section
,
3966 unsigned section_line
,
3983 if (isempty(rvalue
)) {
3984 /* Empty assignment resets the list */
3985 *rt
= strv_free(*rt
);
3989 for (p
= rvalue
;;) {
3990 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3992 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3996 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3997 "Invalid syntax, ignoring: %s", rvalue
);
4003 r
= unit_full_printf(u
, word
, &k
);
4005 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4006 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
4010 if (!path_is_normalized(k
) || path_is_absolute(k
)) {
4011 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
4012 "%s= path is not valid, ignoring assignment: %s", lvalue
, rvalue
);
4016 r
= strv_push(rt
, k
);
4023 int config_parse_set_status(
4025 const char *filename
,
4027 const char *section
,
4028 unsigned section_line
,
4036 const char *word
, *state
;
4038 ExitStatusSet
*status_set
= data
;
4045 /* Empty assignment resets the list */
4046 if (isempty(rvalue
)) {
4047 exit_status_set_free(status_set
);
4051 FOREACH_WORD(word
, l
, rvalue
, state
) {
4052 _cleanup_free_
char *temp
;
4056 temp
= strndup(word
, l
);
4060 r
= safe_atoi(temp
, &val
);
4062 val
= signal_from_string_try_harder(temp
);
4065 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
4068 set
= &status_set
->signal
;
4070 if (val
< 0 || val
> 255) {
4071 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
4074 set
= &status_set
->status
;
4077 r
= set_ensure_allocated(set
, NULL
);
4081 r
= set_put(*set
, INT_TO_PTR(val
));
4083 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
4087 if (!isempty(state
))
4088 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
4093 int config_parse_namespace_path_strv(
4095 const char *filename
,
4097 const char *section
,
4098 unsigned section_line
,
4115 if (isempty(rvalue
)) {
4116 /* Empty assignment resets the list */
4117 *sv
= strv_free(*sv
);
4123 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
4125 bool ignore_enoent
= false, shall_prefix
= false;
4127 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
4133 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
4137 if (!utf8_is_valid(word
)) {
4138 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
4143 if (startswith(w
, "-")) {
4144 ignore_enoent
= true;
4147 if (startswith(w
, "+")) {
4148 shall_prefix
= true;
4152 r
= unit_full_printf(u
, w
, &resolved
);
4154 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s: %m", word
);
4158 if (!path_is_absolute(resolved
)) {
4159 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
4163 path_kill_slashes(resolved
);
4165 joined
= strjoin(ignore_enoent
? "-" : "",
4166 shall_prefix
? "+" : "",
4169 r
= strv_push(sv
, joined
);
4179 int config_parse_temporary_filesystems(
4181 const char *filename
,
4183 const char *section
,
4184 unsigned section_line
,
4192 ExecContext
*c
= data
;
4201 if (isempty(rvalue
)) {
4202 /* Empty assignment resets the list */
4203 temporary_filesystem_free_many(c
->temporary_filesystems
, c
->n_temporary_filesystems
);
4204 c
->temporary_filesystems
= NULL
;
4205 c
->n_temporary_filesystems
= 0;
4211 _cleanup_free_
char *word
= NULL
, *path
= NULL
, *resolved
= NULL
;
4214 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
4220 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
4225 r
= extract_first_word(&w
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
4231 r
= unit_full_printf(u
, path
, &resolved
);
4233 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s, ignoring: %m", word
);
4237 if (!path_is_absolute(resolved
)) {
4238 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
4242 path_kill_slashes(resolved
);
4244 r
= temporary_filesystem_add(&c
->temporary_filesystems
, &c
->n_temporary_filesystems
, path
, w
);
4248 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse mount options, ignoring: %s", word
);
4256 int config_parse_bind_paths(
4258 const char *filename
,
4260 const char *section
,
4261 unsigned section_line
,
4268 ExecContext
*c
= data
;
4278 if (isempty(rvalue
)) {
4279 /* Empty assignment resets the list */
4280 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
4281 c
->bind_mounts
= NULL
;
4282 c
->n_bind_mounts
= 0;
4288 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
4289 _cleanup_free_
char *sresolved
= NULL
, *dresolved
= NULL
;
4290 char *s
= NULL
, *d
= NULL
;
4291 bool rbind
= true, ignore_enoent
= false;
4293 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4299 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4303 r
= unit_full_printf(u
, source
, &sresolved
);
4305 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4306 "Failed to resolved specifiers in \"%s\", ignoring: %m", source
);
4312 ignore_enoent
= true;
4316 if (!utf8_is_valid(s
)) {
4317 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, s
);
4320 if (!path_is_absolute(s
)) {
4321 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute source path, ignoring: %s", s
);
4325 path_kill_slashes(s
);
4327 /* Optionally, the destination is specified. */
4328 if (p
&& p
[-1] == ':') {
4329 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4333 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4337 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':': %s", rvalue
);
4341 r
= unit_full_printf(u
, destination
, &dresolved
);
4343 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4344 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination
);
4348 if (!utf8_is_valid(dresolved
)) {
4349 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, dresolved
);
4352 if (!path_is_absolute(dresolved
)) {
4353 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute destination path, ignoring: %s", dresolved
);
4357 d
= path_kill_slashes(dresolved
);
4359 /* Optionally, there's also a short option string specified */
4360 if (p
&& p
[-1] == ':') {
4361 _cleanup_free_
char *options
= NULL
;
4363 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
4367 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4371 if (isempty(options
) || streq(options
, "rbind"))
4373 else if (streq(options
, "norbind"))
4376 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
4383 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
4387 .read_only
= !!strstr(lvalue
, "ReadOnly"),
4389 .ignore_enoent
= ignore_enoent
,
4398 int config_parse_no_new_privileges(
4400 const char *filename
,
4402 const char *section
,
4403 unsigned section_line
,
4410 ExecContext
*c
= data
;
4418 k
= parse_boolean(rvalue
);
4420 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
4424 c
->no_new_privileges
= k
;
4429 int config_parse_protect_home(
4431 const char *filename
,
4433 const char *section
,
4434 unsigned section_line
,
4441 ExecContext
*c
= data
;
4449 /* Our enum shall be a superset of booleans, hence first try
4450 * to parse as boolean, and then as enum */
4452 h
= parse_protect_home_or_bool(rvalue
);
4454 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
4458 c
->protect_home
= h
;
4463 int config_parse_protect_system(
4465 const char *filename
,
4467 const char *section
,
4468 unsigned section_line
,
4475 ExecContext
*c
= data
;
4483 /* Our enum shall be a superset of booleans, hence first try
4484 * to parse as boolean, and then as enum */
4486 s
= parse_protect_system_or_bool(rvalue
);
4488 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
4492 c
->protect_system
= s
;
4497 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode
, exec_keyring_mode
, ExecKeyringMode
, "Failed to parse keyring mode");
4499 int config_parse_job_timeout_sec(
4501 const char *filename
,
4503 const char *section
,
4504 unsigned section_line
,
4520 r
= parse_sec_fix_0(rvalue
, &usec
);
4522 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue
);
4526 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
4527 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
4530 if (!u
->job_running_timeout_set
)
4531 u
->job_running_timeout
= usec
;
4533 u
->job_timeout
= usec
;
4538 int config_parse_job_running_timeout_sec(
4540 const char *filename
,
4542 const char *section
,
4543 unsigned section_line
,
4559 r
= parse_sec_fix_0(rvalue
, &usec
);
4561 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue
);
4565 u
->job_running_timeout
= usec
;
4566 u
->job_running_timeout_set
= true;
4571 #define FOLLOW_MAX 8
4573 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4584 /* This will update the filename pointer if the loaded file is
4585 * reached by a symlink. The old string will be freed. */
4588 char *target
, *name
;
4590 if (c
++ >= FOLLOW_MAX
)
4593 path_kill_slashes(*filename
);
4595 /* Add the file name we are currently looking at to
4596 * the names of this unit, but only if it is a valid
4598 name
= basename(*filename
);
4599 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4601 id
= set_get(names
, name
);
4607 r
= set_consume(names
, id
);
4613 /* Try to open the file name, but don't if its a symlink */
4614 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4621 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4622 r
= readlink_and_make_absolute(*filename
, &target
);
4630 f
= fdopen(fd
, "re");
4642 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4650 /* Let's try to add in all symlink names we found */
4651 while ((k
= set_steal_first(names
))) {
4653 /* First try to merge in the other name into our
4655 r
= unit_merge_by_name(*u
, k
);
4659 /* Hmm, we couldn't merge the other unit into
4660 * ours? Then let's try it the other way
4663 /* If the symlink name we are looking at is unit template, then
4664 we must search for instance of this template */
4665 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4666 _cleanup_free_
char *instance
= NULL
;
4668 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4672 other
= manager_get_unit((*u
)->manager
, instance
);
4674 other
= manager_get_unit((*u
)->manager
, k
);
4679 r
= unit_merge(other
, *u
);
4682 return merge_by_names(u
, names
, NULL
);
4690 unit_choose_id(*u
, id
);
4698 static int load_from_path(Unit
*u
, const char *path
) {
4699 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4700 _cleanup_fclose_
FILE *f
= NULL
;
4701 _cleanup_free_
char *filename
= NULL
;
4710 symlink_names
= set_new(&string_hash_ops
);
4714 if (path_is_absolute(path
)) {
4716 filename
= strdup(path
);
4720 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4722 filename
= mfree(filename
);
4730 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4732 /* Instead of opening the path right away, we manually
4733 * follow all symlinks and add their name to our unit
4734 * name set while doing so */
4735 filename
= path_make_absolute(path
, *p
);
4739 if (u
->manager
->unit_path_cache
&&
4740 !set_get(u
->manager
->unit_path_cache
, filename
))
4743 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4746 filename
= mfree(filename
);
4748 /* ENOENT means that the file is missing or is a dangling symlink.
4749 * ENOTDIR means that one of paths we expect to be is a directory
4750 * is not a directory, we should just ignore that.
4751 * EACCES means that the directory or file permissions are wrong.
4754 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4755 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4758 /* Empty the symlink names for the next run */
4759 set_clear_free(symlink_names
);
4764 /* Hmm, no suitable file found? */
4767 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4768 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4773 r
= merge_by_names(&merged
, symlink_names
, id
);
4778 u
->load_state
= UNIT_MERGED
;
4782 if (fstat(fileno(f
), &st
) < 0)
4785 if (null_or_empty(&st
)) {
4786 u
->load_state
= UNIT_MASKED
;
4787 u
->fragment_mtime
= 0;
4789 u
->load_state
= UNIT_LOADED
;
4790 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4792 /* Now, parse the file contents */
4793 r
= config_parse(u
->id
, filename
, f
,
4794 UNIT_VTABLE(u
)->sections
,
4795 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4796 CONFIG_PARSE_ALLOW_INCLUDE
, u
);
4801 free(u
->fragment_path
);
4802 u
->fragment_path
= filename
;
4805 if (u
->source_path
) {
4806 if (stat(u
->source_path
, &st
) >= 0)
4807 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4809 u
->source_mtime
= 0;
4815 int unit_load_fragment(Unit
*u
) {
4821 assert(u
->load_state
== UNIT_STUB
);
4825 u
->load_state
= UNIT_LOADED
;
4829 /* First, try to find the unit under its id. We always look
4830 * for unit files in the default directories, to make it easy
4831 * to override things by placing things in /etc/systemd/system */
4832 r
= load_from_path(u
, u
->id
);
4836 /* Try to find an alias we can load this with */
4837 if (u
->load_state
== UNIT_STUB
) {
4838 SET_FOREACH(t
, u
->names
, i
) {
4843 r
= load_from_path(u
, t
);
4847 if (u
->load_state
!= UNIT_STUB
)
4852 /* And now, try looking for it under the suggested (originally linked) path */
4853 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4855 r
= load_from_path(u
, u
->fragment_path
);
4859 if (u
->load_state
== UNIT_STUB
)
4860 /* Hmm, this didn't work? Then let's get rid
4861 * of the fragment path stored for us, so that
4862 * we don't point to an invalid location. */
4863 u
->fragment_path
= mfree(u
->fragment_path
);
4866 /* Look for a template */
4867 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4868 _cleanup_free_
char *k
= NULL
;
4870 r
= unit_name_template(u
->id
, &k
);
4874 r
= load_from_path(u
, k
);
4877 log_unit_notice(u
, "Unit configuration has fatal error, unit will not be started.");
4881 if (u
->load_state
== UNIT_STUB
) {
4882 SET_FOREACH(t
, u
->names
, i
) {
4883 _cleanup_free_
char *z
= NULL
;
4888 r
= unit_name_template(t
, &z
);
4892 r
= load_from_path(u
, z
);
4896 if (u
->load_state
!= UNIT_STUB
)
4905 void unit_dump_config_items(FILE *f
) {
4906 static const struct {
4907 const ConfigParserCallback callback
;
4910 #if !HAVE_SYSV_COMPAT || !HAVE_SECCOMP || !HAVE_PAM || !HAVE_SELINUX || !ENABLE_SMACK || !HAVE_APPARMOR
4911 { config_parse_warn_compat
, "NOTSUPPORTED" },
4913 { config_parse_int
, "INTEGER" },
4914 { config_parse_unsigned
, "UNSIGNED" },
4915 { config_parse_iec_size
, "SIZE" },
4916 { config_parse_iec_uint64
, "SIZE" },
4917 { config_parse_si_size
, "SIZE" },
4918 { config_parse_bool
, "BOOLEAN" },
4919 { config_parse_string
, "STRING" },
4920 { config_parse_path
, "PATH" },
4921 { config_parse_unit_path_printf
, "PATH" },
4922 { config_parse_strv
, "STRING [...]" },
4923 { config_parse_exec_nice
, "NICE" },
4924 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4925 { config_parse_exec_io_class
, "IOCLASS" },
4926 { config_parse_exec_io_priority
, "IOPRIORITY" },
4927 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4928 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4929 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4930 { config_parse_mode
, "MODE" },
4931 { config_parse_unit_env_file
, "FILE" },
4932 { config_parse_exec_output
, "OUTPUT" },
4933 { config_parse_exec_input
, "INPUT" },
4934 { config_parse_log_facility
, "FACILITY" },
4935 { config_parse_log_level
, "LEVEL" },
4936 { config_parse_exec_secure_bits
, "SECUREBITS" },
4937 { config_parse_capability_set
, "BOUNDINGSET" },
4938 { config_parse_limit
, "LIMIT" },
4939 { config_parse_unit_deps
, "UNIT [...]" },
4940 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4941 { config_parse_service_type
, "SERVICETYPE" },
4942 { config_parse_service_restart
, "SERVICERESTART" },
4943 #if HAVE_SYSV_COMPAT
4944 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4946 { config_parse_kill_mode
, "KILLMODE" },
4947 { config_parse_signal
, "SIGNAL" },
4948 { config_parse_socket_listen
, "SOCKET [...]" },
4949 { config_parse_socket_bind
, "SOCKETBIND" },
4950 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4951 { config_parse_sec
, "SECONDS" },
4952 { config_parse_nsec
, "NANOSECONDS" },
4953 { config_parse_namespace_path_strv
, "PATH [...]" },
4954 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4955 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4956 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4957 { config_parse_unit_string_printf
, "STRING" },
4958 { config_parse_trigger_unit
, "UNIT" },
4959 { config_parse_timer
, "TIMER" },
4960 { config_parse_path_spec
, "PATH" },
4961 { config_parse_notify_access
, "ACCESS" },
4962 { config_parse_ip_tos
, "TOS" },
4963 { config_parse_unit_condition_path
, "CONDITION" },
4964 { config_parse_unit_condition_string
, "CONDITION" },
4965 { config_parse_unit_condition_null
, "CONDITION" },
4966 { config_parse_unit_slice
, "SLICE" },
4967 { config_parse_documentation
, "URL" },
4968 { config_parse_service_timeout
, "SECONDS" },
4969 { config_parse_emergency_action
, "ACTION" },
4970 { config_parse_set_status
, "STATUS" },
4971 { config_parse_service_sockets
, "SOCKETS" },
4972 { config_parse_environ
, "ENVIRON" },
4974 { config_parse_syscall_filter
, "SYSCALLS" },
4975 { config_parse_syscall_archs
, "ARCHS" },
4976 { config_parse_syscall_errno
, "ERRNO" },
4977 { config_parse_address_families
, "FAMILIES" },
4978 { config_parse_restrict_namespaces
, "NAMESPACES" },
4980 { config_parse_cpu_shares
, "SHARES" },
4981 { config_parse_cpu_weight
, "WEIGHT" },
4982 { config_parse_memory_limit
, "LIMIT" },
4983 { config_parse_device_allow
, "DEVICE" },
4984 { config_parse_device_policy
, "POLICY" },
4985 { config_parse_io_limit
, "LIMIT" },
4986 { config_parse_io_weight
, "WEIGHT" },
4987 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4988 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4989 { config_parse_blockio_weight
, "WEIGHT" },
4990 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4991 { config_parse_long
, "LONG" },
4992 { config_parse_socket_service
, "SERVICE" },
4994 { config_parse_exec_selinux_context
, "LABEL" },
4996 { config_parse_job_mode
, "MODE" },
4997 { config_parse_job_mode_isolate
, "BOOLEAN" },
4998 { config_parse_personality
, "PERSONALITY" },
5001 const char *prev
= NULL
;
5006 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
5007 const char *rvalue
= "OTHER", *lvalue
;
5011 const ConfigPerfItem
*p
;
5013 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
5015 dot
= strchr(i
, '.');
5016 lvalue
= dot
? dot
+ 1 : i
;
5020 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
5024 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
5027 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
5028 if (p
->parse
== table
[j
].callback
) {
5029 rvalue
= table
[j
].rvalue
;
5033 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);