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 r
= unit_full_printf(u
, rvalue
, &k
);
231 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
232 "Failed to resolve unit specifiers on %s%s: %m",
233 fatal
? "" : ", ignoring", rvalue
);
234 return fatal
? -ENOEXEC
: 0;
237 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
240 int config_parse_unit_path_strv_printf(
242 const char *filename
,
245 unsigned section_line
,
262 if (isempty(rvalue
)) {
268 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
270 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
276 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
277 "Invalid syntax, ignoring: %s", rvalue
);
281 r
= unit_full_printf(u
, word
, &k
);
283 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
284 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
288 if (!utf8_is_valid(k
)) {
289 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
293 if (!path_is_absolute(k
)) {
294 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
295 "Symlink path is not absolute: %s", k
);
299 path_kill_slashes(k
);
308 int config_parse_socket_listen(const char *unit
,
309 const char *filename
,
312 unsigned section_line
,
319 _cleanup_free_ SocketPort
*p
= NULL
;
331 if (isempty(rvalue
)) {
332 /* An empty assignment removes all ports */
333 socket_free_ports(s
);
337 p
= new0(SocketPort
, 1);
341 if (ltype
!= SOCKET_SOCKET
) {
344 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
346 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
350 path_kill_slashes(p
->path
);
352 } else if (streq(lvalue
, "ListenNetlink")) {
353 _cleanup_free_
char *k
= NULL
;
355 p
->type
= SOCKET_SOCKET
;
356 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
358 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
362 r
= socket_address_parse_netlink(&p
->address
, k
);
364 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
369 _cleanup_free_
char *k
= NULL
;
371 p
->type
= SOCKET_SOCKET
;
372 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
374 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
378 r
= socket_address_parse_and_warn(&p
->address
, k
);
380 if (r
!= -EAFNOSUPPORT
)
381 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
385 if (streq(lvalue
, "ListenStream"))
386 p
->address
.type
= SOCK_STREAM
;
387 else if (streq(lvalue
, "ListenDatagram"))
388 p
->address
.type
= SOCK_DGRAM
;
390 assert(streq(lvalue
, "ListenSequentialPacket"));
391 p
->address
.type
= SOCK_SEQPACKET
;
394 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
395 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
401 p
->auxiliary_fds
= NULL
;
402 p
->n_auxiliary_fds
= 0;
405 LIST_FIND_TAIL(port
, s
->ports
, tail
);
406 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
413 int config_parse_socket_protocol(const char *unit
,
414 const char *filename
,
417 unsigned section_line
,
433 r
= socket_protocol_from_name(rvalue
);
435 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid socket protocol, ignoring: %s", rvalue
);
437 } else if (!IN_SET(r
, IPPROTO_UDPLITE
, IPPROTO_SCTP
)) {
438 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
442 s
->socket_protocol
= r
;
447 int config_parse_socket_bind(const char *unit
,
448 const char *filename
,
451 unsigned section_line
,
459 SocketAddressBindIPv6Only b
;
468 b
= parse_socket_address_bind_ipv6_only_or_bool(rvalue
);
470 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
474 s
->bind_ipv6_only
= b
;
479 int config_parse_exec_nice(
481 const char *filename
,
484 unsigned section_line
,
491 ExecContext
*c
= data
;
499 r
= parse_nice(rvalue
, &priority
);
502 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
504 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
515 int config_parse_exec_oom_score_adjust(const char* unit
,
516 const char *filename
,
519 unsigned section_line
,
526 ExecContext
*c
= data
;
534 r
= safe_atoi(rvalue
, &oa
);
536 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
540 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
541 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
545 c
->oom_score_adjust
= oa
;
546 c
->oom_score_adjust_set
= true;
551 int config_parse_exec(
553 const char *filename
,
556 unsigned section_line
,
563 ExecCommand
**e
= data
;
575 rvalue
+= strspn(rvalue
, WHITESPACE
);
577 if (isempty(rvalue
)) {
578 /* An empty assignment resets the list */
579 *e
= exec_command_free_list(*e
);
585 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
586 ExecCommandFlags flags
= 0;
587 bool ignore
= false, separate_argv0
= false;
588 _cleanup_free_ ExecCommand
*nce
= NULL
;
589 _cleanup_strv_free_
char **n
= NULL
;
590 size_t nlen
= 0, nbufsize
= 0;
595 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
601 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
602 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
603 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
604 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
605 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
606 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
607 * other sandboxing, with some special exceptions for changing UID.
609 * The idea is that '!!' may be used to write services that can take benefit of systemd's
610 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
611 * privilege dropping within the daemon if the kernel does not offer that. */
613 if (*f
== '-' && !(flags
& EXEC_COMMAND_IGNORE_FAILURE
)) {
614 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
616 } else if (*f
== '@' && !separate_argv0
)
617 separate_argv0
= true;
618 else if (*f
== '+' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
619 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
620 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
621 flags
|= EXEC_COMMAND_NO_SETUID
;
622 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
623 flags
&= ~EXEC_COMMAND_NO_SETUID
;
624 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
630 r
= unit_full_printf(u
, f
, &path
);
632 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
633 "Failed to resolve unit specifiers on %s%s: %m",
634 f
, ignore
? ", ignoring" : "");
635 return ignore
? 0 : -ENOEXEC
;
639 /* First word is either "-" or "@" with no command. */
640 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
641 "Empty path in command line%s: \"%s\"",
642 ignore
? ", ignoring" : "", rvalue
);
643 return ignore
? 0 : -ENOEXEC
;
645 if (!string_is_safe(path
)) {
646 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
647 "Executable path contains special characters%s: %s",
648 ignore
? ", ignoring" : "", rvalue
);
649 return ignore
? 0 : -ENOEXEC
;
651 if (!path_is_absolute(path
)) {
652 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
653 "Executable path is not absolute%s: %s",
654 ignore
? ", ignoring" : "", rvalue
);
655 return ignore
? 0 : -ENOEXEC
;
657 if (endswith(path
, "/")) {
658 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
659 "Executable path specifies a directory%s: %s",
660 ignore
? ", ignoring" : "", rvalue
);
661 return ignore
? 0 : -ENOEXEC
;
664 if (!separate_argv0
) {
667 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
677 path_kill_slashes(path
);
679 while (!isempty(p
)) {
680 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
682 /* Check explicitly for an unquoted semicolon as
683 * command separator token. */
684 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
686 p
+= strspn(p
, WHITESPACE
);
691 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
692 * extract_first_word() would return the same for all of those. */
693 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
697 p
+= strspn(p
, WHITESPACE
);
699 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
710 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
714 return ignore
? 0 : -ENOEXEC
;
716 r
= unit_full_printf(u
, word
, &resolved
);
718 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
719 "Failed to resolve unit specifiers on %s%s: %m",
720 word
, ignore
? ", ignoring" : "");
721 return ignore
? 0 : -ENOEXEC
;
724 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
726 n
[nlen
++] = resolved
;
732 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
733 "Empty executable name or zeroeth argument%s: %s",
734 ignore
? ", ignoring" : "", rvalue
);
735 return ignore
? 0 : -ENOEXEC
;
738 nce
= new0(ExecCommand
, 1);
746 exec_command_append_list(e
, nce
);
748 /* Do not _cleanup_free_ these. */
759 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
760 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
762 int config_parse_socket_bindtodevice(
764 const char *filename
,
767 unsigned section_line
,
782 if (rvalue
[0] && !streq(rvalue
, "*")) {
783 if (!ifname_valid(rvalue
)) {
784 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
794 free(s
->bind_to_device
);
795 s
->bind_to_device
= n
;
800 int config_parse_exec_input(
802 const char *filename
,
805 unsigned section_line
,
812 ExecContext
*c
= data
;
823 n
= startswith(rvalue
, "fd:");
825 _cleanup_free_
char *resolved
= NULL
;
827 r
= unit_full_printf(u
, n
, &resolved
);
829 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
831 if (isempty(resolved
))
832 resolved
= mfree(resolved
);
833 else if (!fdname_is_valid(resolved
)) {
834 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name: %s", resolved
);
838 free_and_replace(c
->stdio_fdname
[STDIN_FILENO
], resolved
);
840 ei
= EXEC_INPUT_NAMED_FD
;
842 } else if ((n
= startswith(rvalue
, "file:"))) {
843 _cleanup_free_
char *resolved
= NULL
;
845 r
= unit_full_printf(u
, n
, &resolved
);
847 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
849 if (!path_is_absolute(resolved
)) {
850 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires an absolute path name: %s", resolved
);
854 if (!path_is_normalized(resolved
)) {
855 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires a normalized path name: %s", resolved
);
859 free_and_replace(c
->stdio_file
[STDIN_FILENO
], resolved
);
861 ei
= EXEC_INPUT_FILE
;
864 ei
= exec_input_from_string(rvalue
);
866 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
875 int config_parse_exec_input_text(
877 const char *filename
,
880 unsigned section_line
,
887 _cleanup_free_
char *unescaped
= NULL
, *resolved
= NULL
;
888 ExecContext
*c
= data
;
899 if (isempty(rvalue
)) {
900 /* Reset if the empty string is assigned */
901 c
->stdin_data
= mfree(c
->stdin_data
);
902 c
->stdin_data_size
= 0;
906 r
= cunescape(rvalue
, 0, &unescaped
);
908 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to decode C escaped text: %s", rvalue
);
910 r
= unit_full_printf(u
, unescaped
, &resolved
);
912 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", unescaped
);
914 sz
= strlen(resolved
);
915 if (c
->stdin_data_size
+ sz
+ 1 < c
->stdin_data_size
|| /* check for overflow */
916 c
->stdin_data_size
+ sz
+ 1 > EXEC_STDIN_DATA_MAX
) {
917 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
);
921 p
= realloc(c
->stdin_data
, c
->stdin_data_size
+ sz
+ 1);
925 *((char*) mempcpy((char*) p
+ c
->stdin_data_size
, resolved
, sz
)) = '\n';
928 c
->stdin_data_size
+= sz
+ 1;
933 int config_parse_exec_input_data(
935 const char *filename
,
938 unsigned section_line
,
945 _cleanup_free_
void *p
= NULL
;
946 ExecContext
*c
= data
;
956 if (isempty(rvalue
)) {
957 /* Reset if the empty string is assigned */
958 c
->stdin_data
= mfree(c
->stdin_data
);
959 c
->stdin_data_size
= 0;
963 r
= unbase64mem(rvalue
, (size_t) -1, &p
, &sz
);
965 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to decode base64 data, ignoring: %s", rvalue
);
969 if (c
->stdin_data_size
+ sz
< c
->stdin_data_size
|| /* check for overflow */
970 c
->stdin_data_size
+ sz
> EXEC_STDIN_DATA_MAX
) {
971 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
);
975 q
= realloc(c
->stdin_data
, c
->stdin_data_size
+ sz
);
979 memcpy((uint8_t*) q
+ c
->stdin_data_size
, p
, sz
);
982 c
->stdin_data_size
+= sz
;
987 int config_parse_exec_output(
989 const char *filename
,
992 unsigned section_line
,
999 _cleanup_free_
char *resolved
= NULL
;
1001 ExecContext
*c
= data
;
1012 n
= startswith(rvalue
, "fd:");
1014 r
= unit_full_printf(u
, n
, &resolved
);
1016 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
1018 if (isempty(resolved
))
1019 resolved
= mfree(resolved
);
1020 else if (!fdname_is_valid(resolved
)) {
1021 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name: %s", resolved
);
1025 eo
= EXEC_OUTPUT_NAMED_FD
;
1027 } else if ((n
= startswith(rvalue
, "file:"))) {
1029 r
= unit_full_printf(u
, n
, &resolved
);
1031 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s: %m", n
);
1033 if (!path_is_absolute(resolved
)) {
1034 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires an absolute path name: %s", resolved
);
1038 if (!path_is_normalized(resolved
)) {
1039 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "file: requires a normalized path name, ignoring: %s", resolved
);
1043 eo
= EXEC_OUTPUT_FILE
;
1046 eo
= exec_output_from_string(rvalue
);
1048 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
1053 if (streq(lvalue
, "StandardOutput")) {
1054 if (eo
== EXEC_OUTPUT_NAMED_FD
)
1055 free_and_replace(c
->stdio_fdname
[STDOUT_FILENO
], resolved
);
1057 free_and_replace(c
->stdio_file
[STDOUT_FILENO
], resolved
);
1062 assert(streq(lvalue
, "StandardError"));
1064 if (eo
== EXEC_OUTPUT_NAMED_FD
)
1065 free_and_replace(c
->stdio_fdname
[STDERR_FILENO
], resolved
);
1067 free_and_replace(c
->stdio_file
[STDERR_FILENO
], resolved
);
1075 int config_parse_exec_io_class(const char *unit
,
1076 const char *filename
,
1078 const char *section
,
1079 unsigned section_line
,
1086 ExecContext
*c
= data
;
1094 x
= ioprio_class_from_string(rvalue
);
1096 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
1100 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
1101 c
->ioprio_set
= true;
1106 int config_parse_exec_io_priority(const char *unit
,
1107 const char *filename
,
1109 const char *section
,
1110 unsigned section_line
,
1117 ExecContext
*c
= data
;
1125 r
= ioprio_parse_priority(rvalue
, &i
);
1127 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
1131 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
1132 c
->ioprio_set
= true;
1137 int config_parse_exec_cpu_sched_policy(const char *unit
,
1138 const char *filename
,
1140 const char *section
,
1141 unsigned section_line
,
1149 ExecContext
*c
= data
;
1157 x
= sched_policy_from_string(rvalue
);
1159 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1163 c
->cpu_sched_policy
= x
;
1164 /* Moving to or from real-time policy? We need to adjust the priority */
1165 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
1166 c
->cpu_sched_set
= true;
1171 int config_parse_exec_cpu_sched_prio(const char *unit
,
1172 const char *filename
,
1174 const char *section
,
1175 unsigned section_line
,
1182 ExecContext
*c
= data
;
1190 r
= safe_atoi(rvalue
, &i
);
1192 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1196 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1197 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1198 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1200 if (i
< min
|| i
> max
) {
1201 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1205 c
->cpu_sched_priority
= i
;
1206 c
->cpu_sched_set
= true;
1211 int config_parse_exec_cpu_affinity(const char *unit
,
1212 const char *filename
,
1214 const char *section
,
1215 unsigned section_line
,
1222 ExecContext
*c
= data
;
1223 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1231 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1236 /* An empty assignment resets the CPU list */
1237 c
->cpuset
= cpu_set_mfree(c
->cpuset
);
1238 c
->cpuset_ncpus
= 0;
1245 c
->cpuset_ncpus
= (unsigned) ncpus
;
1249 if (c
->cpuset_ncpus
< (unsigned) ncpus
) {
1250 CPU_OR_S(CPU_ALLOC_SIZE(c
->cpuset_ncpus
), cpuset
, c
->cpuset
, cpuset
);
1251 CPU_FREE(c
->cpuset
);
1254 c
->cpuset_ncpus
= (unsigned) ncpus
;
1258 CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus
), c
->cpuset
, c
->cpuset
, cpuset
);
1263 int config_parse_exec_secure_bits(const char *unit
,
1264 const char *filename
,
1266 const char *section
,
1267 unsigned section_line
,
1274 ExecContext
*c
= data
;
1282 if (isempty(rvalue
)) {
1283 /* An empty assignment resets the field */
1288 r
= secure_bits_from_string(rvalue
);
1292 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1293 "Invalid syntax, ignoring: %s", rvalue
);
1302 int config_parse_capability_set(
1304 const char *filename
,
1306 const char *section
,
1307 unsigned section_line
,
1314 uint64_t *capability_set
= data
;
1315 uint64_t sum
= 0, initial
= 0;
1316 bool invert
= false;
1324 if (rvalue
[0] == '~') {
1329 if (streq(lvalue
, "CapabilityBoundingSet"))
1330 initial
= CAP_ALL
; /* initialized to all bits on */
1331 /* else "AmbientCapabilities" initialized to all bits off */
1333 r
= capability_set_from_string(rvalue
, &sum
);
1337 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word: %s", rvalue
);
1341 if (sum
== 0 || *capability_set
== initial
)
1342 /* "", "~" or uninitialized data -> replace */
1343 *capability_set
= invert
? ~sum
: sum
;
1345 /* previous data -> merge */
1347 *capability_set
&= ~sum
;
1349 *capability_set
|= sum
;
1355 int config_parse_limit(
1357 const char *filename
,
1359 const char *section
,
1360 unsigned section_line
,
1367 struct rlimit
**rl
= data
, d
= {};
1375 r
= rlimit_parse(ltype
, rvalue
, &d
);
1377 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1381 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1388 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1396 #if HAVE_SYSV_COMPAT
1397 int config_parse_sysv_priority(const char *unit
,
1398 const char *filename
,
1400 const char *section
,
1401 unsigned section_line
,
1408 int *priority
= data
;
1416 r
= safe_atoi(rvalue
, &i
);
1417 if (r
< 0 || i
< 0) {
1418 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1422 *priority
= (int) i
;
1427 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1428 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1430 int config_parse_exec_mount_flags(
1432 const char *filename
,
1434 const char *section
,
1435 unsigned section_line
,
1443 ExecContext
*c
= data
;
1451 r
= mount_propagation_flags_from_string(rvalue
, &c
->mount_flags
);
1453 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1458 int config_parse_exec_selinux_context(
1460 const char *filename
,
1462 const char *section
,
1463 unsigned section_line
,
1470 ExecContext
*c
= data
;
1481 if (isempty(rvalue
)) {
1482 c
->selinux_context
= mfree(c
->selinux_context
);
1483 c
->selinux_context_ignore
= false;
1487 if (rvalue
[0] == '-') {
1493 r
= unit_full_printf(u
, rvalue
, &k
);
1495 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1496 "Failed to resolve specifiers%s: %m",
1497 ignore
? ", ignoring" : "");
1498 return ignore
? 0 : -ENOEXEC
;
1501 free(c
->selinux_context
);
1502 c
->selinux_context
= k
;
1503 c
->selinux_context_ignore
= ignore
;
1508 int config_parse_exec_apparmor_profile(
1510 const char *filename
,
1512 const char *section
,
1513 unsigned section_line
,
1520 ExecContext
*c
= data
;
1531 if (isempty(rvalue
)) {
1532 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1533 c
->apparmor_profile_ignore
= false;
1537 if (rvalue
[0] == '-') {
1543 r
= unit_full_printf(u
, rvalue
, &k
);
1545 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1546 "Failed to resolve specifiers%s: %m",
1547 ignore
? ", ignoring" : "");
1548 return ignore
? 0 : -ENOEXEC
;
1551 free(c
->apparmor_profile
);
1552 c
->apparmor_profile
= k
;
1553 c
->apparmor_profile_ignore
= ignore
;
1558 int config_parse_exec_smack_process_label(
1560 const char *filename
,
1562 const char *section
,
1563 unsigned section_line
,
1570 ExecContext
*c
= data
;
1581 if (isempty(rvalue
)) {
1582 c
->smack_process_label
= mfree(c
->smack_process_label
);
1583 c
->smack_process_label_ignore
= false;
1587 if (rvalue
[0] == '-') {
1593 r
= unit_full_printf(u
, rvalue
, &k
);
1595 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1596 "Failed to resolve specifiers%s: %m",
1597 ignore
? ", ignoring" : "");
1598 return ignore
? 0 : -ENOEXEC
;
1601 free(c
->smack_process_label
);
1602 c
->smack_process_label
= k
;
1603 c
->smack_process_label_ignore
= ignore
;
1608 int config_parse_timer(const char *unit
,
1609 const char *filename
,
1611 const char *section
,
1612 unsigned section_line
,
1623 CalendarSpec
*c
= NULL
;
1625 _cleanup_free_
char *k
= NULL
;
1633 if (isempty(rvalue
)) {
1634 /* Empty assignment resets list */
1635 timer_free_values(t
);
1639 b
= timer_base_from_string(lvalue
);
1641 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1645 r
= unit_full_printf(u
, rvalue
, &k
);
1647 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1651 if (b
== TIMER_CALENDAR
) {
1652 if (calendar_spec_from_string(k
, &c
) < 0) {
1653 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1657 if (parse_sec(k
, &usec
) < 0) {
1658 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1663 v
= new0(TimerValue
, 1);
1665 calendar_spec_free(c
);
1671 v
->calendar_spec
= c
;
1673 LIST_PREPEND(value
, t
->values
, v
);
1678 int config_parse_trigger_unit(
1680 const char *filename
,
1682 const char *section
,
1683 unsigned section_line
,
1690 _cleanup_free_
char *p
= NULL
;
1700 if (!hashmap_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1701 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1705 r
= unit_name_printf(u
, rvalue
, &p
);
1707 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1711 type
= unit_name_to_type(p
);
1713 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1716 if (unit_has_name(u
, p
)) {
1717 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Units cannot trigger themselves, ignoring: %s", rvalue
);
1721 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1723 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1730 int config_parse_path_spec(const char *unit
,
1731 const char *filename
,
1733 const char *section
,
1734 unsigned section_line
,
1744 _cleanup_free_
char *k
= NULL
;
1752 if (isempty(rvalue
)) {
1753 /* Empty assignment clears list */
1758 b
= path_type_from_string(lvalue
);
1760 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1764 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1766 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1770 if (!path_is_absolute(k
)) {
1771 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1775 s
= new0(PathSpec
, 1);
1780 s
->path
= path_kill_slashes(k
);
1785 LIST_PREPEND(spec
, p
->specs
, s
);
1790 int config_parse_socket_service(
1792 const char *filename
,
1794 const char *section
,
1795 unsigned section_line
,
1802 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1803 _cleanup_free_
char *p
= NULL
;
1813 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1815 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", rvalue
);
1819 if (!endswith(p
, ".service")) {
1820 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service: %s", rvalue
);
1824 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1826 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s: %s", rvalue
, bus_error_message(&error
, r
));
1830 unit_ref_set(&s
->service
, UNIT(s
), x
);
1835 int config_parse_fdname(
1837 const char *filename
,
1839 const char *section
,
1840 unsigned section_line
,
1847 _cleanup_free_
char *p
= NULL
;
1856 if (isempty(rvalue
)) {
1857 s
->fdname
= mfree(s
->fdname
);
1861 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1863 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1867 if (!fdname_is_valid(p
)) {
1868 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1872 return free_and_replace(s
->fdname
, p
);
1875 int config_parse_service_sockets(
1877 const char *filename
,
1879 const char *section
,
1880 unsigned section_line
,
1898 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1900 r
= extract_first_word(&p
, &word
, NULL
, 0);
1906 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1910 r
= unit_name_printf(UNIT(s
), word
, &k
);
1912 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1916 if (!endswith(k
, ".socket")) {
1917 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1921 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1923 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1925 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1927 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1933 int config_parse_bus_name(
1935 const char *filename
,
1937 const char *section
,
1938 unsigned section_line
,
1945 _cleanup_free_
char *k
= NULL
;
1954 r
= unit_full_printf(u
, rvalue
, &k
);
1956 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1960 if (!service_name_is_valid(k
)) {
1961 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1965 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1968 int config_parse_service_timeout(
1970 const char *filename
,
1972 const char *section
,
1973 unsigned section_line
,
1980 Service
*s
= userdata
;
1989 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1991 r
= parse_sec(rvalue
, &usec
);
1993 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1997 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1998 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1999 * all other timeouts. */
2001 usec
= USEC_INFINITY
;
2003 if (!streq(lvalue
, "TimeoutStopSec")) {
2004 s
->start_timeout_defined
= true;
2005 s
->timeout_start_usec
= usec
;
2008 if (!streq(lvalue
, "TimeoutStartSec"))
2009 s
->timeout_stop_usec
= usec
;
2014 int config_parse_sec_fix_0(
2016 const char *filename
,
2018 const char *section
,
2019 unsigned section_line
,
2026 usec_t
*usec
= data
;
2034 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
2035 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
2038 r
= parse_sec_fix_0(rvalue
, usec
);
2040 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
2047 int config_parse_user_group(
2049 const char *filename
,
2051 const char *section
,
2052 unsigned section_line
,
2059 char **user
= data
, *n
;
2068 if (isempty(rvalue
))
2071 _cleanup_free_
char *k
= NULL
;
2073 r
= unit_full_printf(u
, rvalue
, &k
);
2075 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
2079 if (!valid_user_group_name_or_id(k
)) {
2080 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
2094 int config_parse_user_group_strv(
2096 const char *filename
,
2098 const char *section
,
2099 unsigned section_line
,
2106 char ***users
= data
;
2116 if (isempty(rvalue
)) {
2117 *users
= strv_free(*users
);
2123 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2125 r
= extract_first_word(&p
, &word
, NULL
, 0);
2131 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax: %s", rvalue
);
2135 r
= unit_full_printf(u
, word
, &k
);
2137 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", word
);
2141 if (!valid_user_group_name_or_id(k
)) {
2142 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
2146 r
= strv_push(users
, k
);
2156 int config_parse_working_directory(
2158 const char *filename
,
2160 const char *section
,
2161 unsigned section_line
,
2168 ExecContext
*c
= data
;
2179 if (rvalue
[0] == '-') {
2185 if (streq(rvalue
, "~")) {
2186 c
->working_directory_home
= true;
2187 c
->working_directory
= mfree(c
->working_directory
);
2189 _cleanup_free_
char *k
= NULL
;
2191 r
= unit_full_printf(u
, rvalue
, &k
);
2193 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2194 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2195 rvalue
, missing_ok
? ", ignoring" : "");
2196 return missing_ok
? 0 : -ENOEXEC
;
2199 path_kill_slashes(k
);
2201 if (!utf8_is_valid(k
)) {
2202 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2203 return missing_ok
? 0 : -ENOEXEC
;
2206 if (!path_is_absolute(k
)) {
2207 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2208 "Working directory path '%s' is not absolute%s.",
2209 rvalue
, missing_ok
? ", ignoring" : "");
2210 return missing_ok
? 0 : -ENOEXEC
;
2213 c
->working_directory_home
= false;
2214 free_and_replace(c
->working_directory
, k
);
2217 c
->working_directory_missing_ok
= missing_ok
;
2221 int config_parse_unit_env_file(const char *unit
,
2222 const char *filename
,
2224 const char *section
,
2225 unsigned section_line
,
2234 _cleanup_free_
char *n
= NULL
;
2242 if (isempty(rvalue
)) {
2243 /* Empty assignment frees the list */
2244 *env
= strv_free(*env
);
2248 r
= unit_full_printf(u
, rvalue
, &n
);
2250 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2254 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2255 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2259 r
= strv_extend(env
, n
);
2266 int config_parse_environ(
2268 const char *filename
,
2270 const char *section
,
2271 unsigned section_line
,
2288 if (isempty(rvalue
)) {
2289 /* Empty assignment resets the list */
2290 *env
= strv_free(*env
);
2294 for (p
= rvalue
;; ) {
2295 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2297 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2303 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2304 "Invalid syntax, ignoring: %s", rvalue
);
2309 r
= unit_full_printf(u
, word
, &k
);
2311 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2312 "Failed to resolve specifiers, ignoring: %s", word
);
2320 if (!env_assignment_is_valid(k
)) {
2321 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2322 "Invalid environment assignment, ignoring: %s", k
);
2326 r
= strv_env_replace(env
, k
);
2334 int config_parse_pass_environ(
2336 const char *filename
,
2338 const char *section
,
2339 unsigned section_line
,
2346 const char *whole_rvalue
= rvalue
;
2347 _cleanup_strv_free_
char **n
= NULL
;
2348 size_t nlen
= 0, nbufsize
= 0;
2349 char*** passenv
= data
;
2358 if (isempty(rvalue
)) {
2359 /* Empty assignment resets the list */
2360 *passenv
= strv_free(*passenv
);
2365 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2367 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2373 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2374 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2379 r
= unit_full_printf(u
, word
, &k
);
2381 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2382 "Failed to resolve specifiers, ignoring: %s", word
);
2390 if (!env_name_is_valid(k
)) {
2391 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2392 "Invalid environment name for %s, ignoring: %s", lvalue
, k
);
2396 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2405 r
= strv_extend_strv(passenv
, n
, true);
2413 int config_parse_unset_environ(
2415 const char *filename
,
2417 const char *section
,
2418 unsigned section_line
,
2425 _cleanup_strv_free_
char **n
= NULL
;
2426 const char *whole_rvalue
= rvalue
;
2427 size_t nlen
= 0, nbufsize
= 0;
2428 char*** unsetenv
= data
;
2437 if (isempty(rvalue
)) {
2438 /* Empty assignment resets the list */
2439 *unsetenv
= strv_free(*unsetenv
);
2444 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2446 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2452 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2453 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2458 r
= unit_full_printf(u
, word
, &k
);
2460 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2461 "Failed to resolve specifiers, ignoring: %s", word
);
2469 if (!env_assignment_is_valid(k
) && !env_name_is_valid(k
)) {
2470 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2471 "Invalid environment name or assignment %s, ignoring: %s", lvalue
, k
);
2475 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2484 r
= strv_extend_strv(unsetenv
, n
, true);
2492 int config_parse_log_extra_fields(
2494 const char *filename
,
2496 const char *section
,
2497 unsigned section_line
,
2504 ExecContext
*c
= data
;
2514 if (isempty(rvalue
)) {
2515 exec_context_free_log_extra_fields(c
);
2519 for (p
= rvalue
;; ) {
2520 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2524 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2530 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2534 r
= unit_full_printf(u
, word
, &k
);
2536 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring field: %m", word
);
2540 eq
= strchr(k
, '=');
2542 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field lacks '=' character, ignoring field: %s", k
);
2546 if (!journal_field_valid(k
, eq
-k
, false)) {
2547 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field name is invalid, ignoring field: %s", k
);
2551 t
= reallocarray(c
->log_extra_fields
, c
->n_log_extra_fields
+1, sizeof(struct iovec
));
2555 c
->log_extra_fields
= t
;
2556 c
->log_extra_fields
[c
->n_log_extra_fields
++] = IOVEC_MAKE_STRING(k
);
2564 int config_parse_ip_tos(const char *unit
,
2565 const char *filename
,
2567 const char *section
,
2568 unsigned section_line
,
2575 int *ip_tos
= data
, x
;
2582 x
= ip_tos_from_string(rvalue
);
2584 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2592 int config_parse_unit_condition_path(
2594 const char *filename
,
2596 const char *section
,
2597 unsigned section_line
,
2604 _cleanup_free_
char *p
= NULL
;
2605 Condition
**list
= data
, *c
;
2606 ConditionType t
= ltype
;
2607 bool trigger
, negate
;
2616 if (isempty(rvalue
)) {
2617 /* Empty assignment resets the list */
2618 *list
= condition_free_list(*list
);
2622 trigger
= rvalue
[0] == '|';
2626 negate
= rvalue
[0] == '!';
2630 r
= unit_full_printf(u
, rvalue
, &p
);
2632 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2636 if (!path_is_absolute(p
)) {
2637 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2641 c
= condition_new(t
, p
, trigger
, negate
);
2645 LIST_PREPEND(conditions
, *list
, c
);
2649 int config_parse_unit_condition_string(
2651 const char *filename
,
2653 const char *section
,
2654 unsigned section_line
,
2661 _cleanup_free_
char *s
= NULL
;
2662 Condition
**list
= data
, *c
;
2663 ConditionType t
= ltype
;
2664 bool trigger
, negate
;
2673 if (isempty(rvalue
)) {
2674 /* Empty assignment resets the list */
2675 *list
= condition_free_list(*list
);
2679 trigger
= rvalue
[0] == '|';
2683 negate
= rvalue
[0] == '!';
2687 r
= unit_full_printf(u
, rvalue
, &s
);
2689 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2693 c
= condition_new(t
, s
, trigger
, negate
);
2697 LIST_PREPEND(conditions
, *list
, c
);
2701 int config_parse_unit_condition_null(
2703 const char *filename
,
2705 const char *section
,
2706 unsigned section_line
,
2713 Condition
**list
= data
, *c
;
2714 bool trigger
, negate
;
2722 if (isempty(rvalue
)) {
2723 /* Empty assignment resets the list */
2724 *list
= condition_free_list(*list
);
2728 trigger
= rvalue
[0] == '|';
2732 negate
= rvalue
[0] == '!';
2736 b
= parse_boolean(rvalue
);
2738 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2745 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2749 LIST_PREPEND(conditions
, *list
, c
);
2753 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2754 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2756 int config_parse_unit_requires_mounts_for(
2758 const char *filename
,
2760 const char *section
,
2761 unsigned section_line
,
2777 for (p
= rvalue
;; ) {
2778 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2780 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2786 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2787 "Invalid syntax, ignoring: %s", rvalue
);
2791 if (!utf8_is_valid(word
)) {
2792 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2796 r
= unit_full_printf(u
, word
, &resolved
);
2798 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit name \"%s\", ignoring: %m", word
);
2802 r
= unit_require_mounts_for(u
, resolved
, UNIT_DEPENDENCY_FILE
);
2804 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", resolved
);
2810 int config_parse_documentation(const char *unit
,
2811 const char *filename
,
2813 const char *section
,
2814 unsigned section_line
,
2830 if (isempty(rvalue
)) {
2831 /* Empty assignment resets the list */
2832 u
->documentation
= strv_free(u
->documentation
);
2836 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2837 rvalue
, data
, userdata
);
2841 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2843 if (documentation_url_is_valid(*a
))
2846 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2857 int config_parse_syscall_filter(
2859 const char *filename
,
2861 const char *section
,
2862 unsigned section_line
,
2869 ExecContext
*c
= data
;
2871 bool invert
= false;
2880 if (isempty(rvalue
)) {
2881 /* Empty assignment resets the list */
2882 c
->syscall_filter
= hashmap_free(c
->syscall_filter
);
2883 c
->syscall_whitelist
= false;
2887 if (rvalue
[0] == '~') {
2892 if (!c
->syscall_filter
) {
2893 c
->syscall_filter
= hashmap_new(NULL
);
2894 if (!c
->syscall_filter
)
2898 /* Allow everything but the ones listed */
2899 c
->syscall_whitelist
= false;
2901 /* Allow nothing but the ones listed */
2902 c
->syscall_whitelist
= true;
2904 /* Accept default syscalls if we are on a whitelist */
2905 r
= seccomp_parse_syscall_filter("@default", -1, c
->syscall_filter
, SECCOMP_PARSE_WHITELIST
);
2913 _cleanup_free_
char *word
= NULL
, *name
= NULL
;
2916 r
= extract_first_word(&p
, &word
, NULL
, 0);
2922 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2926 r
= parse_syscall_and_errno(word
, &name
, &num
);
2928 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse syscall:errno, ignoring: %s", word
);
2932 r
= seccomp_parse_syscall_filter_full(name
, num
, c
->syscall_filter
,
2933 SECCOMP_PARSE_LOG
|SECCOMP_PARSE_PERMISSIVE
|(invert
? SECCOMP_PARSE_INVERT
: 0)|(c
->syscall_whitelist
? SECCOMP_PARSE_WHITELIST
: 0),
2934 unit
, filename
, line
);
2942 int config_parse_syscall_archs(
2944 const char *filename
,
2946 const char *section
,
2947 unsigned section_line
,
2958 if (isempty(rvalue
)) {
2959 *archs
= set_free(*archs
);
2963 r
= set_ensure_allocated(archs
, NULL
);
2967 for (p
= rvalue
;;) {
2968 _cleanup_free_
char *word
= NULL
;
2971 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2977 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2978 "Invalid syntax, ignoring: %s", rvalue
);
2982 r
= seccomp_arch_from_string(word
, &a
);
2984 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2985 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
2989 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2995 int config_parse_syscall_errno(
2997 const char *filename
,
2999 const char *section
,
3000 unsigned section_line
,
3007 ExecContext
*c
= data
;
3014 if (isempty(rvalue
)) {
3015 /* Empty assignment resets to KILL */
3016 c
->syscall_errno
= 0;
3020 e
= parse_errno(rvalue
);
3022 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
3026 c
->syscall_errno
= e
;
3030 int config_parse_address_families(
3032 const char *filename
,
3034 const char *section
,
3035 unsigned section_line
,
3042 ExecContext
*c
= data
;
3043 bool invert
= false;
3051 if (isempty(rvalue
)) {
3052 /* Empty assignment resets the list */
3053 c
->address_families
= set_free(c
->address_families
);
3054 c
->address_families_whitelist
= false;
3058 if (rvalue
[0] == '~') {
3063 if (!c
->address_families
) {
3064 c
->address_families
= set_new(NULL
);
3065 if (!c
->address_families
)
3068 c
->address_families_whitelist
= !invert
;
3071 for (p
= rvalue
;;) {
3072 _cleanup_free_
char *word
= NULL
;
3075 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3081 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3082 "Invalid syntax, ignoring: %s", rvalue
);
3086 af
= af_from_name(word
);
3088 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3089 "Failed to parse address family \"%s\", ignoring: %m", word
);
3093 /* If we previously wanted to forbid an address family and now
3094 * we want to allow it, then just remove it from the list.
3096 if (!invert
== c
->address_families_whitelist
) {
3097 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
3101 set_remove(c
->address_families
, INT_TO_PTR(af
));
3105 int config_parse_restrict_namespaces(
3107 const char *filename
,
3109 const char *section
,
3110 unsigned section_line
,
3117 ExecContext
*c
= data
;
3118 bool invert
= false;
3121 if (isempty(rvalue
)) {
3122 /* Reset to the default. */
3123 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3127 if (rvalue
[0] == '~') {
3132 r
= parse_boolean(rvalue
);
3134 c
->restrict_namespaces
= 0;
3136 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
3138 /* Not a boolean argument, in this case it's a list of namespace types. */
3140 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
3142 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
3148 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
3154 int config_parse_unit_slice(
3156 const char *filename
,
3158 const char *section
,
3159 unsigned section_line
,
3166 _cleanup_free_
char *k
= NULL
;
3167 Unit
*u
= userdata
, *slice
= NULL
;
3175 r
= unit_name_printf(u
, rvalue
, &k
);
3177 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
3181 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
3183 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
3187 r
= unit_set_slice(u
, slice
);
3189 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
3196 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
3198 int config_parse_cpu_weight(
3200 const char *filename
,
3202 const char *section
,
3203 unsigned section_line
,
3210 uint64_t *weight
= data
;
3217 r
= cg_weight_parse(rvalue
, weight
);
3219 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
3226 int config_parse_cpu_shares(
3228 const char *filename
,
3230 const char *section
,
3231 unsigned section_line
,
3238 uint64_t *shares
= data
;
3245 r
= cg_cpu_shares_parse(rvalue
, shares
);
3247 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
3254 int config_parse_cpu_quota(
3256 const char *filename
,
3258 const char *section
,
3259 unsigned section_line
,
3266 CGroupContext
*c
= data
;
3273 if (isempty(rvalue
)) {
3274 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3278 r
= parse_percent_unbounded(rvalue
);
3280 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3284 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3288 int config_parse_memory_limit(
3290 const char *filename
,
3292 const char *section
,
3293 unsigned section_line
,
3300 CGroupContext
*c
= data
;
3301 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3304 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3306 r
= parse_percent(rvalue
);
3308 r
= parse_size(rvalue
, 1024, &bytes
);
3310 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3314 bytes
= physical_memory_scale(r
, 100U);
3316 if (bytes
>= UINT64_MAX
||
3317 (bytes
<= 0 && !streq(lvalue
, "MemorySwapMax"))) {
3318 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3323 if (streq(lvalue
, "MemoryLow"))
3324 c
->memory_low
= bytes
;
3325 else if (streq(lvalue
, "MemoryHigh"))
3326 c
->memory_high
= bytes
;
3327 else if (streq(lvalue
, "MemoryMax"))
3328 c
->memory_max
= bytes
;
3329 else if (streq(lvalue
, "MemorySwapMax"))
3330 c
->memory_swap_max
= bytes
;
3331 else if (streq(lvalue
, "MemoryLimit"))
3332 c
->memory_limit
= bytes
;
3339 int config_parse_tasks_max(
3341 const char *filename
,
3343 const char *section
,
3344 unsigned section_line
,
3351 uint64_t *tasks_max
= data
, v
;
3355 if (isempty(rvalue
)) {
3356 *tasks_max
= u
->manager
->default_tasks_max
;
3360 if (streq(rvalue
, "infinity")) {
3361 *tasks_max
= CGROUP_LIMIT_MAX
;
3365 r
= parse_percent(rvalue
);
3367 r
= safe_atou64(rvalue
, &v
);
3369 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3373 v
= system_tasks_max_scale(r
, 100U);
3375 if (v
<= 0 || v
>= UINT64_MAX
) {
3376 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3384 int config_parse_delegate(
3386 const char *filename
,
3388 const char *section
,
3389 unsigned section_line
,
3396 CGroupContext
*c
= data
;
3399 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3400 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3401 * mask to delegate. */
3403 if (isempty(rvalue
)) {
3405 c
->delegate_controllers
= 0;
3409 r
= parse_boolean(rvalue
);
3411 const char *p
= rvalue
;
3412 CGroupMask mask
= 0;
3415 _cleanup_free_
char *word
= NULL
;
3416 CGroupController cc
;
3418 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3424 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
3428 cc
= cgroup_controller_from_string(word
);
3430 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid controller name '%s', ignoring", rvalue
);
3434 mask
|= CGROUP_CONTROLLER_TO_MASK(cc
);
3438 c
->delegate_controllers
|= mask
;
3442 c
->delegate_controllers
= _CGROUP_MASK_ALL
;
3444 c
->delegate
= false;
3445 c
->delegate_controllers
= 0;
3451 int config_parse_device_allow(
3453 const char *filename
,
3455 const char *section
,
3456 unsigned section_line
,
3463 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3464 CGroupContext
*c
= data
;
3465 CGroupDeviceAllow
*a
;
3466 const char *m
= NULL
;
3470 if (isempty(rvalue
)) {
3471 while (c
->device_allow
)
3472 cgroup_context_free_device_allow(c
, c
->device_allow
);
3477 r
= unit_full_printf(userdata
, rvalue
, &t
);
3479 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3480 "Failed to resolve specifiers in %s, ignoring: %m",
3485 n
= strcspn(t
, WHITESPACE
);
3487 path
= strndup(t
, n
);
3491 if (!is_deviceallow_pattern(path
) &&
3492 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3493 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3497 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3501 if (!in_charset(m
, "rwm")) {
3502 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3506 a
= new0(CGroupDeviceAllow
, 1);
3512 a
->r
= !!strchr(m
, 'r');
3513 a
->w
= !!strchr(m
, 'w');
3514 a
->m
= !!strchr(m
, 'm');
3516 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3520 int config_parse_io_weight(
3522 const char *filename
,
3524 const char *section
,
3525 unsigned section_line
,
3532 uint64_t *weight
= data
;
3539 r
= cg_weight_parse(rvalue
, weight
);
3541 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3548 int config_parse_io_device_weight(
3550 const char *filename
,
3552 const char *section
,
3553 unsigned section_line
,
3560 _cleanup_free_
char *path
= NULL
;
3561 CGroupIODeviceWeight
*w
;
3562 CGroupContext
*c
= data
;
3572 if (isempty(rvalue
)) {
3573 while (c
->io_device_weights
)
3574 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3579 n
= strcspn(rvalue
, WHITESPACE
);
3580 weight
= rvalue
+ n
;
3581 weight
+= strspn(weight
, WHITESPACE
);
3583 if (isempty(weight
)) {
3584 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3588 path
= strndup(rvalue
, n
);
3592 if (!path_startswith(path
, "/dev") &&
3593 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3594 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3598 r
= cg_weight_parse(weight
, &u
);
3600 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3604 assert(u
!= CGROUP_WEIGHT_INVALID
);
3606 w
= new0(CGroupIODeviceWeight
, 1);
3615 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3619 int config_parse_io_limit(
3621 const char *filename
,
3623 const char *section
,
3624 unsigned section_line
,
3631 _cleanup_free_
char *path
= NULL
;
3632 CGroupIODeviceLimit
*l
= NULL
, *t
;
3633 CGroupContext
*c
= data
;
3634 CGroupIOLimitType type
;
3644 type
= cgroup_io_limit_type_from_string(lvalue
);
3647 if (isempty(rvalue
)) {
3648 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3649 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3653 n
= strcspn(rvalue
, WHITESPACE
);
3655 limit
+= strspn(limit
, WHITESPACE
);
3658 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3662 path
= strndup(rvalue
, n
);
3666 if (!path_startswith(path
, "/dev") &&
3667 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3668 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3672 if (streq("infinity", limit
)) {
3673 num
= CGROUP_LIMIT_MAX
;
3675 r
= parse_size(limit
, 1000, &num
);
3676 if (r
< 0 || num
<= 0) {
3677 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3682 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3683 if (path_equal(path
, t
->path
)) {
3690 CGroupIOLimitType ttype
;
3692 l
= new0(CGroupIODeviceLimit
, 1);
3698 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3699 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3701 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3704 l
->limits
[type
] = num
;
3709 int config_parse_blockio_weight(
3711 const char *filename
,
3713 const char *section
,
3714 unsigned section_line
,
3721 uint64_t *weight
= data
;
3728 r
= cg_blkio_weight_parse(rvalue
, weight
);
3730 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3737 int config_parse_blockio_device_weight(
3739 const char *filename
,
3741 const char *section
,
3742 unsigned section_line
,
3749 _cleanup_free_
char *path
= NULL
;
3750 CGroupBlockIODeviceWeight
*w
;
3751 CGroupContext
*c
= data
;
3761 if (isempty(rvalue
)) {
3762 while (c
->blockio_device_weights
)
3763 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3768 n
= strcspn(rvalue
, WHITESPACE
);
3769 weight
= rvalue
+ n
;
3770 weight
+= strspn(weight
, WHITESPACE
);
3772 if (isempty(weight
)) {
3773 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3777 path
= strndup(rvalue
, n
);
3781 if (!path_startswith(path
, "/dev") &&
3782 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3783 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3787 r
= cg_blkio_weight_parse(weight
, &u
);
3789 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3793 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3795 w
= new0(CGroupBlockIODeviceWeight
, 1);
3804 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3808 int config_parse_blockio_bandwidth(
3810 const char *filename
,
3812 const char *section
,
3813 unsigned section_line
,
3820 _cleanup_free_
char *path
= NULL
;
3821 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3822 CGroupContext
*c
= data
;
3823 const char *bandwidth
;
3833 read
= streq("BlockIOReadBandwidth", lvalue
);
3835 if (isempty(rvalue
)) {
3836 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3837 b
->rbps
= CGROUP_LIMIT_MAX
;
3838 b
->wbps
= CGROUP_LIMIT_MAX
;
3843 n
= strcspn(rvalue
, WHITESPACE
);
3844 bandwidth
= rvalue
+ n
;
3845 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3848 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3852 path
= strndup(rvalue
, n
);
3856 if (!path_startswith(path
, "/dev") &&
3857 !path_startswith(path
, "/run/systemd/inaccessible/")) {
3858 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3862 r
= parse_size(bandwidth
, 1000, &bytes
);
3863 if (r
< 0 || bytes
<= 0) {
3864 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3868 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3869 if (path_equal(path
, t
->path
)) {
3876 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3882 b
->rbps
= CGROUP_LIMIT_MAX
;
3883 b
->wbps
= CGROUP_LIMIT_MAX
;
3885 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3896 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3898 int config_parse_job_mode_isolate(
3900 const char *filename
,
3902 const char *section
,
3903 unsigned section_line
,
3917 r
= parse_boolean(rvalue
);
3919 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3923 log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue
);
3925 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3929 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode
, exec_preserve_mode
, ExecPreserveMode
, "Failed to parse runtime directory preserve mode");
3931 int config_parse_exec_directories(
3933 const char *filename
,
3935 const char *section
,
3936 unsigned section_line
,
3953 if (isempty(rvalue
)) {
3954 /* Empty assignment resets the list */
3955 *rt
= strv_free(*rt
);
3959 for (p
= rvalue
;;) {
3960 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3962 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3966 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3967 "Invalid syntax, ignoring: %s", rvalue
);
3973 r
= unit_full_printf(u
, word
, &k
);
3975 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3976 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
3980 if (!path_is_normalized(k
) || path_is_absolute(k
)) {
3981 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3982 "%s= path is not valid, ignoring assignment: %s", lvalue
, rvalue
);
3986 r
= strv_push(rt
, k
);
3993 int config_parse_set_status(
3995 const char *filename
,
3997 const char *section
,
3998 unsigned section_line
,
4006 const char *word
, *state
;
4008 ExitStatusSet
*status_set
= data
;
4015 /* Empty assignment resets the list */
4016 if (isempty(rvalue
)) {
4017 exit_status_set_free(status_set
);
4021 FOREACH_WORD(word
, l
, rvalue
, state
) {
4022 _cleanup_free_
char *temp
;
4026 temp
= strndup(word
, l
);
4030 r
= safe_atoi(temp
, &val
);
4032 val
= signal_from_string_try_harder(temp
);
4035 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
4038 set
= &status_set
->signal
;
4040 if (val
< 0 || val
> 255) {
4041 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
4044 set
= &status_set
->status
;
4047 r
= set_ensure_allocated(set
, NULL
);
4051 r
= set_put(*set
, INT_TO_PTR(val
));
4053 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
4057 if (!isempty(state
))
4058 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
4063 int config_parse_namespace_path_strv(
4065 const char *filename
,
4067 const char *section
,
4068 unsigned section_line
,
4085 if (isempty(rvalue
)) {
4086 /* Empty assignment resets the list */
4087 *sv
= strv_free(*sv
);
4093 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
4095 bool ignore_enoent
= false, shall_prefix
= false;
4097 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
4103 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
4107 if (!utf8_is_valid(word
)) {
4108 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
4113 if (startswith(w
, "-")) {
4114 ignore_enoent
= true;
4117 if (startswith(w
, "+")) {
4118 shall_prefix
= true;
4122 r
= unit_full_printf(u
, w
, &resolved
);
4124 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s: %m", word
);
4128 if (!path_is_absolute(resolved
)) {
4129 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
4133 path_kill_slashes(resolved
);
4135 joined
= strjoin(ignore_enoent
? "-" : "",
4136 shall_prefix
? "+" : "",
4139 r
= strv_push(sv
, joined
);
4149 int config_parse_temporary_filesystems(
4151 const char *filename
,
4153 const char *section
,
4154 unsigned section_line
,
4162 ExecContext
*c
= data
;
4171 if (isempty(rvalue
)) {
4172 /* Empty assignment resets the list */
4173 temporary_filesystem_free_many(c
->temporary_filesystems
, c
->n_temporary_filesystems
);
4174 c
->temporary_filesystems
= NULL
;
4175 c
->n_temporary_filesystems
= 0;
4181 _cleanup_free_
char *word
= NULL
, *path
= NULL
, *resolved
= NULL
;
4184 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
4190 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
4195 r
= extract_first_word(&w
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
4201 r
= unit_full_printf(u
, path
, &resolved
);
4203 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s, ignoring: %m", word
);
4207 if (!path_is_absolute(resolved
)) {
4208 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
4212 path_kill_slashes(resolved
);
4214 r
= temporary_filesystem_add(&c
->temporary_filesystems
, &c
->n_temporary_filesystems
, path
, w
);
4218 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse mount options, ignoring: %s", word
);
4226 int config_parse_bind_paths(
4228 const char *filename
,
4230 const char *section
,
4231 unsigned section_line
,
4238 ExecContext
*c
= data
;
4248 if (isempty(rvalue
)) {
4249 /* Empty assignment resets the list */
4250 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
4251 c
->bind_mounts
= NULL
;
4252 c
->n_bind_mounts
= 0;
4258 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
4259 _cleanup_free_
char *sresolved
= NULL
, *dresolved
= NULL
;
4260 char *s
= NULL
, *d
= NULL
;
4261 bool rbind
= true, ignore_enoent
= false;
4263 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4269 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4273 r
= unit_full_printf(u
, source
, &sresolved
);
4275 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4276 "Failed to resolved specifiers in \"%s\", ignoring: %m", source
);
4282 ignore_enoent
= true;
4286 if (!utf8_is_valid(s
)) {
4287 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, s
);
4290 if (!path_is_absolute(s
)) {
4291 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute source path, ignoring: %s", s
);
4295 path_kill_slashes(s
);
4297 /* Optionally, the destination is specified. */
4298 if (p
&& p
[-1] == ':') {
4299 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4303 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4307 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':': %s", rvalue
);
4311 r
= unit_full_printf(u
, destination
, &dresolved
);
4313 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4314 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination
);
4318 if (!utf8_is_valid(dresolved
)) {
4319 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, dresolved
);
4322 if (!path_is_absolute(dresolved
)) {
4323 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute destination path, ignoring: %s", dresolved
);
4327 d
= path_kill_slashes(dresolved
);
4329 /* Optionally, there's also a short option string specified */
4330 if (p
&& p
[-1] == ':') {
4331 _cleanup_free_
char *options
= NULL
;
4333 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
4337 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4341 if (isempty(options
) || streq(options
, "rbind"))
4343 else if (streq(options
, "norbind"))
4346 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
4353 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
4357 .read_only
= !!strstr(lvalue
, "ReadOnly"),
4359 .ignore_enoent
= ignore_enoent
,
4368 int config_parse_no_new_privileges(
4370 const char *filename
,
4372 const char *section
,
4373 unsigned section_line
,
4380 ExecContext
*c
= data
;
4388 k
= parse_boolean(rvalue
);
4390 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
4394 c
->no_new_privileges
= k
;
4399 int config_parse_protect_home(
4401 const char *filename
,
4403 const char *section
,
4404 unsigned section_line
,
4411 ExecContext
*c
= data
;
4419 /* Our enum shall be a superset of booleans, hence first try
4420 * to parse as boolean, and then as enum */
4422 h
= parse_protect_home_or_bool(rvalue
);
4424 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
4428 c
->protect_home
= h
;
4433 int config_parse_protect_system(
4435 const char *filename
,
4437 const char *section
,
4438 unsigned section_line
,
4445 ExecContext
*c
= data
;
4453 /* Our enum shall be a superset of booleans, hence first try
4454 * to parse as boolean, and then as enum */
4456 s
= parse_protect_system_or_bool(rvalue
);
4458 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
4462 c
->protect_system
= s
;
4467 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode
, exec_keyring_mode
, ExecKeyringMode
, "Failed to parse keyring mode");
4469 int config_parse_job_timeout_sec(
4471 const char *filename
,
4473 const char *section
,
4474 unsigned section_line
,
4490 r
= parse_sec_fix_0(rvalue
, &usec
);
4492 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue
);
4496 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
4497 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
4500 if (!u
->job_running_timeout_set
)
4501 u
->job_running_timeout
= usec
;
4503 u
->job_timeout
= usec
;
4508 int config_parse_job_running_timeout_sec(
4510 const char *filename
,
4512 const char *section
,
4513 unsigned section_line
,
4529 r
= parse_sec_fix_0(rvalue
, &usec
);
4531 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue
);
4535 u
->job_running_timeout
= usec
;
4536 u
->job_running_timeout_set
= true;
4541 #define FOLLOW_MAX 8
4543 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4554 /* This will update the filename pointer if the loaded file is
4555 * reached by a symlink. The old string will be freed. */
4558 char *target
, *name
;
4560 if (c
++ >= FOLLOW_MAX
)
4563 path_kill_slashes(*filename
);
4565 /* Add the file name we are currently looking at to
4566 * the names of this unit, but only if it is a valid
4568 name
= basename(*filename
);
4569 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4571 id
= set_get(names
, name
);
4577 r
= set_consume(names
, id
);
4583 /* Try to open the file name, but don't if its a symlink */
4584 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4591 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4592 r
= readlink_and_make_absolute(*filename
, &target
);
4600 f
= fdopen(fd
, "re");
4612 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4620 /* Let's try to add in all symlink names we found */
4621 while ((k
= set_steal_first(names
))) {
4623 /* First try to merge in the other name into our
4625 r
= unit_merge_by_name(*u
, k
);
4629 /* Hmm, we couldn't merge the other unit into
4630 * ours? Then let's try it the other way
4633 /* If the symlink name we are looking at is unit template, then
4634 we must search for instance of this template */
4635 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4636 _cleanup_free_
char *instance
= NULL
;
4638 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4642 other
= manager_get_unit((*u
)->manager
, instance
);
4644 other
= manager_get_unit((*u
)->manager
, k
);
4649 r
= unit_merge(other
, *u
);
4652 return merge_by_names(u
, names
, NULL
);
4660 unit_choose_id(*u
, id
);
4668 static int load_from_path(Unit
*u
, const char *path
) {
4669 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4670 _cleanup_fclose_
FILE *f
= NULL
;
4671 _cleanup_free_
char *filename
= NULL
;
4680 symlink_names
= set_new(&string_hash_ops
);
4684 if (path_is_absolute(path
)) {
4686 filename
= strdup(path
);
4690 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4692 filename
= mfree(filename
);
4700 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4702 /* Instead of opening the path right away, we manually
4703 * follow all symlinks and add their name to our unit
4704 * name set while doing so */
4705 filename
= path_make_absolute(path
, *p
);
4709 if (u
->manager
->unit_path_cache
&&
4710 !set_get(u
->manager
->unit_path_cache
, filename
))
4713 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4716 filename
= mfree(filename
);
4718 /* ENOENT means that the file is missing or is a dangling symlink.
4719 * ENOTDIR means that one of paths we expect to be is a directory
4720 * is not a directory, we should just ignore that.
4721 * EACCES means that the directory or file permissions are wrong.
4724 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4725 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4728 /* Empty the symlink names for the next run */
4729 set_clear_free(symlink_names
);
4734 /* Hmm, no suitable file found? */
4737 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4738 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4743 r
= merge_by_names(&merged
, symlink_names
, id
);
4748 u
->load_state
= UNIT_MERGED
;
4752 if (fstat(fileno(f
), &st
) < 0)
4755 if (null_or_empty(&st
)) {
4756 u
->load_state
= UNIT_MASKED
;
4757 u
->fragment_mtime
= 0;
4759 u
->load_state
= UNIT_LOADED
;
4760 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4762 /* Now, parse the file contents */
4763 r
= config_parse(u
->id
, filename
, f
,
4764 UNIT_VTABLE(u
)->sections
,
4765 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4766 CONFIG_PARSE_ALLOW_INCLUDE
, u
);
4771 free(u
->fragment_path
);
4772 u
->fragment_path
= filename
;
4775 if (u
->source_path
) {
4776 if (stat(u
->source_path
, &st
) >= 0)
4777 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4779 u
->source_mtime
= 0;
4785 int unit_load_fragment(Unit
*u
) {
4791 assert(u
->load_state
== UNIT_STUB
);
4795 u
->load_state
= UNIT_LOADED
;
4799 /* First, try to find the unit under its id. We always look
4800 * for unit files in the default directories, to make it easy
4801 * to override things by placing things in /etc/systemd/system */
4802 r
= load_from_path(u
, u
->id
);
4806 /* Try to find an alias we can load this with */
4807 if (u
->load_state
== UNIT_STUB
) {
4808 SET_FOREACH(t
, u
->names
, i
) {
4813 r
= load_from_path(u
, t
);
4817 if (u
->load_state
!= UNIT_STUB
)
4822 /* And now, try looking for it under the suggested (originally linked) path */
4823 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4825 r
= load_from_path(u
, u
->fragment_path
);
4829 if (u
->load_state
== UNIT_STUB
)
4830 /* Hmm, this didn't work? Then let's get rid
4831 * of the fragment path stored for us, so that
4832 * we don't point to an invalid location. */
4833 u
->fragment_path
= mfree(u
->fragment_path
);
4836 /* Look for a template */
4837 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4838 _cleanup_free_
char *k
= NULL
;
4840 r
= unit_name_template(u
->id
, &k
);
4844 r
= load_from_path(u
, k
);
4847 log_unit_notice(u
, "Unit configuration has fatal error, unit will not be started.");
4851 if (u
->load_state
== UNIT_STUB
) {
4852 SET_FOREACH(t
, u
->names
, i
) {
4853 _cleanup_free_
char *z
= NULL
;
4858 r
= unit_name_template(t
, &z
);
4862 r
= load_from_path(u
, z
);
4866 if (u
->load_state
!= UNIT_STUB
)
4875 void unit_dump_config_items(FILE *f
) {
4876 static const struct {
4877 const ConfigParserCallback callback
;
4880 #if !HAVE_SYSV_COMPAT || !HAVE_SECCOMP || !HAVE_PAM || !HAVE_SELINUX || !ENABLE_SMACK || !HAVE_APPARMOR
4881 { config_parse_warn_compat
, "NOTSUPPORTED" },
4883 { config_parse_int
, "INTEGER" },
4884 { config_parse_unsigned
, "UNSIGNED" },
4885 { config_parse_iec_size
, "SIZE" },
4886 { config_parse_iec_uint64
, "SIZE" },
4887 { config_parse_si_size
, "SIZE" },
4888 { config_parse_bool
, "BOOLEAN" },
4889 { config_parse_string
, "STRING" },
4890 { config_parse_path
, "PATH" },
4891 { config_parse_unit_path_printf
, "PATH" },
4892 { config_parse_strv
, "STRING [...]" },
4893 { config_parse_exec_nice
, "NICE" },
4894 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4895 { config_parse_exec_io_class
, "IOCLASS" },
4896 { config_parse_exec_io_priority
, "IOPRIORITY" },
4897 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4898 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4899 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4900 { config_parse_mode
, "MODE" },
4901 { config_parse_unit_env_file
, "FILE" },
4902 { config_parse_exec_output
, "OUTPUT" },
4903 { config_parse_exec_input
, "INPUT" },
4904 { config_parse_log_facility
, "FACILITY" },
4905 { config_parse_log_level
, "LEVEL" },
4906 { config_parse_exec_secure_bits
, "SECUREBITS" },
4907 { config_parse_capability_set
, "BOUNDINGSET" },
4908 { config_parse_limit
, "LIMIT" },
4909 { config_parse_unit_deps
, "UNIT [...]" },
4910 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4911 { config_parse_service_type
, "SERVICETYPE" },
4912 { config_parse_service_restart
, "SERVICERESTART" },
4913 #if HAVE_SYSV_COMPAT
4914 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4916 { config_parse_kill_mode
, "KILLMODE" },
4917 { config_parse_signal
, "SIGNAL" },
4918 { config_parse_socket_listen
, "SOCKET [...]" },
4919 { config_parse_socket_bind
, "SOCKETBIND" },
4920 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4921 { config_parse_sec
, "SECONDS" },
4922 { config_parse_nsec
, "NANOSECONDS" },
4923 { config_parse_namespace_path_strv
, "PATH [...]" },
4924 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4925 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4926 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4927 { config_parse_unit_string_printf
, "STRING" },
4928 { config_parse_trigger_unit
, "UNIT" },
4929 { config_parse_timer
, "TIMER" },
4930 { config_parse_path_spec
, "PATH" },
4931 { config_parse_notify_access
, "ACCESS" },
4932 { config_parse_ip_tos
, "TOS" },
4933 { config_parse_unit_condition_path
, "CONDITION" },
4934 { config_parse_unit_condition_string
, "CONDITION" },
4935 { config_parse_unit_condition_null
, "CONDITION" },
4936 { config_parse_unit_slice
, "SLICE" },
4937 { config_parse_documentation
, "URL" },
4938 { config_parse_service_timeout
, "SECONDS" },
4939 { config_parse_emergency_action
, "ACTION" },
4940 { config_parse_set_status
, "STATUS" },
4941 { config_parse_service_sockets
, "SOCKETS" },
4942 { config_parse_environ
, "ENVIRON" },
4944 { config_parse_syscall_filter
, "SYSCALLS" },
4945 { config_parse_syscall_archs
, "ARCHS" },
4946 { config_parse_syscall_errno
, "ERRNO" },
4947 { config_parse_address_families
, "FAMILIES" },
4948 { config_parse_restrict_namespaces
, "NAMESPACES" },
4950 { config_parse_cpu_shares
, "SHARES" },
4951 { config_parse_cpu_weight
, "WEIGHT" },
4952 { config_parse_memory_limit
, "LIMIT" },
4953 { config_parse_device_allow
, "DEVICE" },
4954 { config_parse_device_policy
, "POLICY" },
4955 { config_parse_io_limit
, "LIMIT" },
4956 { config_parse_io_weight
, "WEIGHT" },
4957 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4958 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4959 { config_parse_blockio_weight
, "WEIGHT" },
4960 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4961 { config_parse_long
, "LONG" },
4962 { config_parse_socket_service
, "SERVICE" },
4964 { config_parse_exec_selinux_context
, "LABEL" },
4966 { config_parse_job_mode
, "MODE" },
4967 { config_parse_job_mode_isolate
, "BOOLEAN" },
4968 { config_parse_personality
, "PERSONALITY" },
4971 const char *prev
= NULL
;
4976 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4977 const char *rvalue
= "OTHER", *lvalue
;
4981 const ConfigPerfItem
*p
;
4983 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4985 dot
= strchr(i
, '.');
4986 lvalue
= dot
? dot
+ 1 : i
;
4990 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
4994 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
4997 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
4998 if (p
->parse
== table
[j
].callback
) {
4999 rvalue
= table
[j
].rvalue
;
5003 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);