2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
5 Copyright 2012 Holger Hans Peter Freyther
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <linux/oom.h>
30 #include <sys/resource.h>
34 #include "alloc-util.h"
35 #include "bus-error.h"
36 #include "bus-internal.h"
39 #include "capability-util.h"
41 #include "conf-parser.h"
42 #include "cpu-set-util.h"
44 #include "errno-list.h"
49 #include "load-fragment.h"
52 #include "mount-util.h"
53 #include "parse-util.h"
54 #include "path-util.h"
55 #include "process-util.h"
56 #include "rlimit-util.h"
58 #include "seccomp-util.h"
60 #include "securebits.h"
61 #include "securebits-util.h"
62 #include "signal-util.h"
63 #include "stat-util.h"
64 #include "string-util.h"
66 #include "unit-name.h"
67 #include "unit-printf.h"
69 #include "user-util.h"
73 int config_parse_warn_compat(
78 unsigned section_line
,
84 Disabled reason
= ltype
;
87 case DISABLED_CONFIGURATION
:
88 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
89 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
92 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
93 "Support for option %s= has been removed and it is ignored", lvalue
);
95 case DISABLED_EXPERIMENTAL
:
96 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
97 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
104 int config_parse_unit_deps(
106 const char *filename
,
109 unsigned section_line
,
116 UnitDependency d
= ltype
;
126 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
129 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_RETAIN_ESCAPE
);
135 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
139 r
= unit_name_printf(u
, word
, &k
);
141 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
145 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
147 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
153 int config_parse_obsolete_unit_deps(
155 const char *filename
,
158 unsigned section_line
,
165 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
166 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue
, unit_dependency_to_string(ltype
));
168 return config_parse_unit_deps(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, data
, userdata
);
171 int config_parse_unit_string_printf(
173 const char *filename
,
176 unsigned section_line
,
183 _cleanup_free_
char *k
= NULL
;
192 r
= unit_full_printf(u
, rvalue
, &k
);
194 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
198 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
201 int config_parse_unit_strv_printf(
203 const char *filename
,
206 unsigned section_line
,
214 _cleanup_free_
char *k
= NULL
;
222 r
= unit_full_printf(u
, rvalue
, &k
);
224 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
228 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
231 int config_parse_unit_path_printf(
233 const char *filename
,
236 unsigned section_line
,
243 _cleanup_free_
char *k
= NULL
;
253 r
= unit_full_printf(u
, rvalue
, &k
);
255 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
256 "Failed to resolve unit specifiers on %s%s: %m",
257 fatal
? "" : ", ignoring", rvalue
);
258 return fatal
? -ENOEXEC
: 0;
261 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
264 int config_parse_unit_path_strv_printf(
266 const char *filename
,
269 unsigned section_line
,
286 if (isempty(rvalue
)) {
292 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
294 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
300 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
301 "Invalid syntax, ignoring: %s", rvalue
);
305 r
= unit_full_printf(u
, word
, &k
);
307 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
308 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
312 if (!utf8_is_valid(k
)) {
313 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
317 if (!path_is_absolute(k
)) {
318 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
319 "Symlink path is not absolute: %s", k
);
323 path_kill_slashes(k
);
332 int config_parse_socket_listen(const char *unit
,
333 const char *filename
,
336 unsigned section_line
,
343 _cleanup_free_ SocketPort
*p
= NULL
;
355 if (isempty(rvalue
)) {
356 /* An empty assignment removes all ports */
357 socket_free_ports(s
);
361 p
= new0(SocketPort
, 1);
365 if (ltype
!= SOCKET_SOCKET
) {
368 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
370 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
374 path_kill_slashes(p
->path
);
376 } else if (streq(lvalue
, "ListenNetlink")) {
377 _cleanup_free_
char *k
= NULL
;
379 p
->type
= SOCKET_SOCKET
;
380 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
382 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
386 r
= socket_address_parse_netlink(&p
->address
, k
);
388 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
393 _cleanup_free_
char *k
= NULL
;
395 p
->type
= SOCKET_SOCKET
;
396 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
398 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
402 r
= socket_address_parse_and_warn(&p
->address
, k
);
404 if (r
!= -EAFNOSUPPORT
)
405 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
410 if (streq(lvalue
, "ListenStream"))
411 p
->address
.type
= SOCK_STREAM
;
412 else if (streq(lvalue
, "ListenDatagram"))
413 p
->address
.type
= SOCK_DGRAM
;
415 assert(streq(lvalue
, "ListenSequentialPacket"));
416 p
->address
.type
= SOCK_SEQPACKET
;
419 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
420 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
426 p
->auxiliary_fds
= NULL
;
427 p
->n_auxiliary_fds
= 0;
431 LIST_FIND_TAIL(port
, s
->ports
, tail
);
432 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
434 LIST_PREPEND(port
, s
->ports
, p
);
440 int config_parse_socket_protocol(const char *unit
,
441 const char *filename
,
444 unsigned section_line
,
459 if (streq(rvalue
, "udplite"))
460 s
->socket_protocol
= IPPROTO_UDPLITE
;
461 else if (streq(rvalue
, "sctp"))
462 s
->socket_protocol
= IPPROTO_SCTP
;
464 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
471 int config_parse_socket_bind(const char *unit
,
472 const char *filename
,
475 unsigned section_line
,
483 SocketAddressBindIPv6Only b
;
492 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
496 r
= parse_boolean(rvalue
);
498 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
502 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
504 s
->bind_ipv6_only
= b
;
509 int config_parse_exec_nice(
511 const char *filename
,
514 unsigned section_line
,
521 ExecContext
*c
= data
;
529 r
= parse_nice(rvalue
, &priority
);
532 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
534 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
545 int config_parse_exec_oom_score_adjust(const char* unit
,
546 const char *filename
,
549 unsigned section_line
,
556 ExecContext
*c
= data
;
564 r
= safe_atoi(rvalue
, &oa
);
566 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
570 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
571 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
575 c
->oom_score_adjust
= oa
;
576 c
->oom_score_adjust_set
= true;
581 int config_parse_exec(
583 const char *filename
,
586 unsigned section_line
,
593 ExecCommand
**e
= data
;
605 rvalue
+= strspn(rvalue
, WHITESPACE
);
607 if (isempty(rvalue
)) {
608 /* An empty assignment resets the list */
609 *e
= exec_command_free_list(*e
);
615 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
616 ExecCommandFlags flags
= 0;
617 bool ignore
= false, separate_argv0
= false;
618 _cleanup_free_ ExecCommand
*nce
= NULL
;
619 _cleanup_strv_free_
char **n
= NULL
;
620 size_t nlen
= 0, nbufsize
= 0;
625 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
631 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
632 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
633 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
634 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
635 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
636 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
637 * other sandboxing, with some special exceptions for changing UID.
639 * The idea is that '!!' may be used to write services that can take benefit of systemd's
640 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
641 * privilege dropping within the daemon if the kernel does not offer that. */
643 if (*f
== '-' && !(flags
& EXEC_COMMAND_IGNORE_FAILURE
)) {
644 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
646 } else if (*f
== '@' && !separate_argv0
)
647 separate_argv0
= true;
648 else if (*f
== '+' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
649 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
650 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
651 flags
|= EXEC_COMMAND_NO_SETUID
;
652 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
653 flags
&= ~EXEC_COMMAND_NO_SETUID
;
654 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
660 r
= unit_full_printf(u
, f
, &path
);
662 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
663 "Failed to resolve unit specifiers on %s%s: %m",
664 f
, ignore
? ", ignoring" : "");
665 return ignore
? 0 : -ENOEXEC
;
669 /* First word is either "-" or "@" with no command. */
670 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
671 "Empty path in command line%s: \"%s\"",
672 ignore
? ", ignoring" : "", rvalue
);
673 return ignore
? 0 : -ENOEXEC
;
675 if (!string_is_safe(path
)) {
676 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
677 "Executable path contains special characters%s: %s",
678 ignore
? ", ignoring" : "", rvalue
);
679 return ignore
? 0 : -ENOEXEC
;
681 if (!path_is_absolute(path
)) {
682 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
683 "Executable path is not absolute%s: %s",
684 ignore
? ", ignoring" : "", rvalue
);
685 return ignore
? 0 : -ENOEXEC
;
687 if (endswith(path
, "/")) {
688 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
689 "Executable path specifies a directory%s: %s",
690 ignore
? ", ignoring" : "", rvalue
);
691 return ignore
? 0 : -ENOEXEC
;
694 if (!separate_argv0
) {
697 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
707 path_kill_slashes(path
);
709 while (!isempty(p
)) {
710 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
712 /* Check explicitly for an unquoted semicolon as
713 * command separator token. */
714 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
716 p
+= strspn(p
, WHITESPACE
);
721 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
722 * extract_first_word() would return the same for all of those. */
723 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
727 p
+= strspn(p
, WHITESPACE
);
729 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
740 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
744 return ignore
? 0 : -ENOEXEC
;
746 r
= unit_full_printf(u
, word
, &resolved
);
748 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
749 "Failed to resolve unit specifiers on %s%s: %m",
750 word
, ignore
? ", ignoring" : "");
751 return ignore
? 0 : -ENOEXEC
;
754 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
756 n
[nlen
++] = resolved
;
762 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
763 "Empty executable name or zeroeth argument%s: %s",
764 ignore
? ", ignoring" : "", rvalue
);
765 return ignore
? 0 : -ENOEXEC
;
768 nce
= new0(ExecCommand
, 1);
776 exec_command_append_list(e
, nce
);
778 /* Do not _cleanup_free_ these. */
789 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
790 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
792 int config_parse_socket_bindtodevice(
794 const char *filename
,
797 unsigned section_line
,
812 if (rvalue
[0] && !streq(rvalue
, "*")) {
813 if (!ifname_valid(rvalue
)) {
814 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
824 free(s
->bind_to_device
);
825 s
->bind_to_device
= n
;
830 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input literal specifier");
831 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output literal specifier");
833 int config_parse_exec_input(const char *unit
,
834 const char *filename
,
837 unsigned section_line
,
843 ExecContext
*c
= data
;
852 name
= startswith(rvalue
, "fd:");
854 /* Strip prefix and validate fd name */
855 if (!fdname_is_valid(name
)) {
856 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
859 c
->std_input
= EXEC_INPUT_NAMED_FD
;
860 r
= free_and_strdup(&c
->stdio_fdname
[STDIN_FILENO
], name
);
865 ExecInput ei
= exec_input_from_string(rvalue
);
866 if (ei
== _EXEC_INPUT_INVALID
)
867 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
874 int config_parse_exec_output(const char *unit
,
875 const char *filename
,
878 unsigned section_line
,
884 ExecContext
*c
= data
;
895 name
= startswith(rvalue
, "fd:");
897 /* Strip prefix and validate fd name */
898 if (!fdname_is_valid(name
)) {
899 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
902 eo
= EXEC_OUTPUT_NAMED_FD
;
904 eo
= exec_output_from_string(rvalue
);
905 if (eo
== _EXEC_OUTPUT_INVALID
) {
906 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
911 if (streq(lvalue
, "StandardOutput")) {
913 r
= free_and_strdup(&c
->stdio_fdname
[STDOUT_FILENO
], name
);
917 } else if (streq(lvalue
, "StandardError")) {
919 r
= free_and_strdup(&c
->stdio_fdname
[STDERR_FILENO
], name
);
924 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output property, ignoring: %s", lvalue
);
929 int config_parse_exec_io_class(const char *unit
,
930 const char *filename
,
933 unsigned section_line
,
940 ExecContext
*c
= data
;
948 x
= ioprio_class_from_string(rvalue
);
950 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
954 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
955 c
->ioprio_set
= true;
960 int config_parse_exec_io_priority(const char *unit
,
961 const char *filename
,
964 unsigned section_line
,
971 ExecContext
*c
= data
;
979 r
= ioprio_parse_priority(rvalue
, &i
);
981 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
985 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
986 c
->ioprio_set
= true;
991 int config_parse_exec_cpu_sched_policy(const char *unit
,
992 const char *filename
,
995 unsigned section_line
,
1003 ExecContext
*c
= data
;
1011 x
= sched_policy_from_string(rvalue
);
1013 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1017 c
->cpu_sched_policy
= x
;
1018 /* Moving to or from real-time policy? We need to adjust the priority */
1019 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
1020 c
->cpu_sched_set
= true;
1025 int config_parse_exec_cpu_sched_prio(const char *unit
,
1026 const char *filename
,
1028 const char *section
,
1029 unsigned section_line
,
1036 ExecContext
*c
= data
;
1044 r
= safe_atoi(rvalue
, &i
);
1046 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1050 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1051 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1052 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1054 if (i
< min
|| i
> max
) {
1055 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1059 c
->cpu_sched_priority
= i
;
1060 c
->cpu_sched_set
= true;
1065 int config_parse_exec_cpu_affinity(const char *unit
,
1066 const char *filename
,
1068 const char *section
,
1069 unsigned section_line
,
1076 ExecContext
*c
= data
;
1077 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1085 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1090 CPU_FREE(c
->cpuset
);
1093 /* An empty assignment resets the CPU list */
1099 c
->cpuset_ncpus
= ncpus
;
1104 int config_parse_exec_secure_bits(const char *unit
,
1105 const char *filename
,
1107 const char *section
,
1108 unsigned section_line
,
1115 ExecContext
*c
= data
;
1123 if (isempty(rvalue
)) {
1124 /* An empty assignment resets the field */
1129 r
= secure_bits_from_string(rvalue
);
1133 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1134 "Invalid syntax, ignoring: %s", rvalue
);
1143 int config_parse_capability_set(
1145 const char *filename
,
1147 const char *section
,
1148 unsigned section_line
,
1155 uint64_t *capability_set
= data
;
1156 uint64_t sum
= 0, initial
= 0;
1157 bool invert
= false;
1165 if (rvalue
[0] == '~') {
1170 if (streq(lvalue
, "CapabilityBoundingSet"))
1171 initial
= CAP_ALL
; /* initialized to all bits on */
1172 /* else "AmbientCapabilities" initialized to all bits off */
1174 r
= capability_set_from_string(rvalue
, &sum
);
1178 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word: %s", rvalue
);
1182 if (sum
== 0 || *capability_set
== initial
)
1183 /* "", "~" or uninitialized data -> replace */
1184 *capability_set
= invert
? ~sum
: sum
;
1186 /* previous data -> merge */
1188 *capability_set
&= ~sum
;
1190 *capability_set
|= sum
;
1196 int config_parse_limit(
1198 const char *filename
,
1200 const char *section
,
1201 unsigned section_line
,
1208 struct rlimit
**rl
= data
, d
= {};
1216 r
= rlimit_parse(ltype
, rvalue
, &d
);
1218 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1222 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1229 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1237 #if HAVE_SYSV_COMPAT
1238 int config_parse_sysv_priority(const char *unit
,
1239 const char *filename
,
1241 const char *section
,
1242 unsigned section_line
,
1249 int *priority
= data
;
1257 r
= safe_atoi(rvalue
, &i
);
1258 if (r
< 0 || i
< 0) {
1259 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1263 *priority
= (int) i
;
1268 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1269 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1271 int config_parse_exec_mount_flags(
1273 const char *filename
,
1275 const char *section
,
1276 unsigned section_line
,
1284 ExecContext
*c
= data
;
1292 r
= mount_propagation_flags_from_string(rvalue
, &c
->mount_flags
);
1294 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1299 int config_parse_exec_selinux_context(
1301 const char *filename
,
1303 const char *section
,
1304 unsigned section_line
,
1311 ExecContext
*c
= data
;
1322 if (isempty(rvalue
)) {
1323 c
->selinux_context
= mfree(c
->selinux_context
);
1324 c
->selinux_context_ignore
= false;
1328 if (rvalue
[0] == '-') {
1334 r
= unit_full_printf(u
, rvalue
, &k
);
1336 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1337 "Failed to resolve specifiers%s: %m",
1338 ignore
? ", ignoring" : "");
1339 return ignore
? 0 : -ENOEXEC
;
1342 free(c
->selinux_context
);
1343 c
->selinux_context
= k
;
1344 c
->selinux_context_ignore
= ignore
;
1349 int config_parse_exec_apparmor_profile(
1351 const char *filename
,
1353 const char *section
,
1354 unsigned section_line
,
1361 ExecContext
*c
= data
;
1372 if (isempty(rvalue
)) {
1373 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1374 c
->apparmor_profile_ignore
= false;
1378 if (rvalue
[0] == '-') {
1384 r
= unit_full_printf(u
, rvalue
, &k
);
1386 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1387 "Failed to resolve specifiers%s: %m",
1388 ignore
? ", ignoring" : "");
1389 return ignore
? 0 : -ENOEXEC
;
1392 free(c
->apparmor_profile
);
1393 c
->apparmor_profile
= k
;
1394 c
->apparmor_profile_ignore
= ignore
;
1399 int config_parse_exec_smack_process_label(
1401 const char *filename
,
1403 const char *section
,
1404 unsigned section_line
,
1411 ExecContext
*c
= data
;
1422 if (isempty(rvalue
)) {
1423 c
->smack_process_label
= mfree(c
->smack_process_label
);
1424 c
->smack_process_label_ignore
= false;
1428 if (rvalue
[0] == '-') {
1434 r
= unit_full_printf(u
, rvalue
, &k
);
1436 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1437 "Failed to resolve specifiers%s: %m",
1438 ignore
? ", ignoring" : "");
1439 return ignore
? 0 : -ENOEXEC
;
1442 free(c
->smack_process_label
);
1443 c
->smack_process_label
= k
;
1444 c
->smack_process_label_ignore
= ignore
;
1449 int config_parse_timer(const char *unit
,
1450 const char *filename
,
1452 const char *section
,
1453 unsigned section_line
,
1464 CalendarSpec
*c
= NULL
;
1466 _cleanup_free_
char *k
= NULL
;
1474 if (isempty(rvalue
)) {
1475 /* Empty assignment resets list */
1476 timer_free_values(t
);
1480 b
= timer_base_from_string(lvalue
);
1482 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1486 r
= unit_full_printf(u
, rvalue
, &k
);
1488 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1492 if (b
== TIMER_CALENDAR
) {
1493 if (calendar_spec_from_string(k
, &c
) < 0) {
1494 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1498 if (parse_sec(k
, &usec
) < 0) {
1499 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1504 v
= new0(TimerValue
, 1);
1506 calendar_spec_free(c
);
1512 v
->calendar_spec
= c
;
1514 LIST_PREPEND(value
, t
->values
, v
);
1519 int config_parse_trigger_unit(
1521 const char *filename
,
1523 const char *section
,
1524 unsigned section_line
,
1531 _cleanup_free_
char *p
= NULL
;
1541 if (!hashmap_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1542 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1546 r
= unit_name_printf(u
, rvalue
, &p
);
1548 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1552 type
= unit_name_to_type(p
);
1554 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1558 if (type
== u
->type
) {
1559 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1563 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1565 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1572 int config_parse_path_spec(const char *unit
,
1573 const char *filename
,
1575 const char *section
,
1576 unsigned section_line
,
1586 _cleanup_free_
char *k
= NULL
;
1594 if (isempty(rvalue
)) {
1595 /* Empty assignment clears list */
1600 b
= path_type_from_string(lvalue
);
1602 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1606 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1608 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1612 if (!path_is_absolute(k
)) {
1613 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1617 s
= new0(PathSpec
, 1);
1622 s
->path
= path_kill_slashes(k
);
1627 LIST_PREPEND(spec
, p
->specs
, s
);
1632 int config_parse_socket_service(
1634 const char *filename
,
1636 const char *section
,
1637 unsigned section_line
,
1644 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1645 _cleanup_free_
char *p
= NULL
;
1655 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1657 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", rvalue
);
1661 if (!endswith(p
, ".service")) {
1662 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service: %s", rvalue
);
1666 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1668 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s: %s", rvalue
, bus_error_message(&error
, r
));
1672 unit_ref_set(&s
->service
, x
);
1677 int config_parse_fdname(
1679 const char *filename
,
1681 const char *section
,
1682 unsigned section_line
,
1689 _cleanup_free_
char *p
= NULL
;
1698 if (isempty(rvalue
)) {
1699 s
->fdname
= mfree(s
->fdname
);
1703 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1705 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1709 if (!fdname_is_valid(p
)) {
1710 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1714 return free_and_replace(s
->fdname
, p
);
1717 int config_parse_service_sockets(
1719 const char *filename
,
1721 const char *section
,
1722 unsigned section_line
,
1740 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1742 r
= extract_first_word(&p
, &word
, NULL
, 0);
1748 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1752 r
= unit_name_printf(UNIT(s
), word
, &k
);
1754 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1758 if (!endswith(k
, ".socket")) {
1759 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1763 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1765 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1767 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true, UNIT_DEPENDENCY_FILE
);
1769 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1775 int config_parse_bus_name(
1777 const char *filename
,
1779 const char *section
,
1780 unsigned section_line
,
1787 _cleanup_free_
char *k
= NULL
;
1796 r
= unit_full_printf(u
, rvalue
, &k
);
1798 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1802 if (!service_name_is_valid(k
)) {
1803 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1807 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1810 int config_parse_service_timeout(
1812 const char *filename
,
1814 const char *section
,
1815 unsigned section_line
,
1822 Service
*s
= userdata
;
1831 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1833 r
= parse_sec(rvalue
, &usec
);
1835 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1839 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1840 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1841 * all other timeouts. */
1843 usec
= USEC_INFINITY
;
1845 if (!streq(lvalue
, "TimeoutStopSec")) {
1846 s
->start_timeout_defined
= true;
1847 s
->timeout_start_usec
= usec
;
1850 if (!streq(lvalue
, "TimeoutStartSec"))
1851 s
->timeout_stop_usec
= usec
;
1856 int config_parse_sec_fix_0(
1858 const char *filename
,
1860 const char *section
,
1861 unsigned section_line
,
1868 usec_t
*usec
= data
;
1876 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1877 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1880 r
= parse_sec_fix_0(rvalue
, usec
);
1882 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1889 int config_parse_user_group(
1891 const char *filename
,
1893 const char *section
,
1894 unsigned section_line
,
1901 char **user
= data
, *n
;
1910 if (isempty(rvalue
))
1913 _cleanup_free_
char *k
= NULL
;
1915 r
= unit_full_printf(u
, rvalue
, &k
);
1917 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
1921 if (!valid_user_group_name_or_id(k
)) {
1922 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1936 int config_parse_user_group_strv(
1938 const char *filename
,
1940 const char *section
,
1941 unsigned section_line
,
1948 char ***users
= data
;
1958 if (isempty(rvalue
)) {
1959 *users
= strv_free(*users
);
1965 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1967 r
= extract_first_word(&p
, &word
, NULL
, 0);
1973 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax: %s", rvalue
);
1977 r
= unit_full_printf(u
, word
, &k
);
1979 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", word
);
1983 if (!valid_user_group_name_or_id(k
)) {
1984 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1988 r
= strv_push(users
, k
);
1998 int config_parse_working_directory(
2000 const char *filename
,
2002 const char *section
,
2003 unsigned section_line
,
2010 ExecContext
*c
= data
;
2021 if (rvalue
[0] == '-') {
2027 if (streq(rvalue
, "~")) {
2028 c
->working_directory_home
= true;
2029 c
->working_directory
= mfree(c
->working_directory
);
2031 _cleanup_free_
char *k
= NULL
;
2033 r
= unit_full_printf(u
, rvalue
, &k
);
2035 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2036 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2037 rvalue
, missing_ok
? ", ignoring" : "");
2038 return missing_ok
? 0 : -ENOEXEC
;
2041 path_kill_slashes(k
);
2043 if (!utf8_is_valid(k
)) {
2044 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2045 return missing_ok
? 0 : -ENOEXEC
;
2048 if (!path_is_absolute(k
)) {
2049 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2050 "Working directory path '%s' is not absolute%s.",
2051 rvalue
, missing_ok
? ", ignoring" : "");
2052 return missing_ok
? 0 : -ENOEXEC
;
2055 c
->working_directory_home
= false;
2056 free_and_replace(c
->working_directory
, k
);
2059 c
->working_directory_missing_ok
= missing_ok
;
2063 int config_parse_unit_env_file(const char *unit
,
2064 const char *filename
,
2066 const char *section
,
2067 unsigned section_line
,
2076 _cleanup_free_
char *n
= NULL
;
2084 if (isempty(rvalue
)) {
2085 /* Empty assignment frees the list */
2086 *env
= strv_free(*env
);
2090 r
= unit_full_printf(u
, rvalue
, &n
);
2092 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2096 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2097 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2101 r
= strv_extend(env
, n
);
2108 int config_parse_environ(
2110 const char *filename
,
2112 const char *section
,
2113 unsigned section_line
,
2130 if (isempty(rvalue
)) {
2131 /* Empty assignment resets the list */
2132 *env
= strv_free(*env
);
2136 for (p
= rvalue
;; ) {
2137 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2139 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2145 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2146 "Invalid syntax, ignoring: %s", rvalue
);
2151 r
= unit_full_printf(u
, word
, &k
);
2153 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2154 "Failed to resolve specifiers, ignoring: %s", word
);
2162 if (!env_assignment_is_valid(k
)) {
2163 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2164 "Invalid environment assignment, ignoring: %s", k
);
2168 r
= strv_env_replace(env
, k
);
2176 int config_parse_pass_environ(
2178 const char *filename
,
2180 const char *section
,
2181 unsigned section_line
,
2188 const char *whole_rvalue
= rvalue
;
2189 _cleanup_strv_free_
char **n
= NULL
;
2190 size_t nlen
= 0, nbufsize
= 0;
2191 char*** passenv
= data
;
2200 if (isempty(rvalue
)) {
2201 /* Empty assignment resets the list */
2202 *passenv
= strv_free(*passenv
);
2207 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2209 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2215 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2216 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2221 r
= unit_full_printf(u
, word
, &k
);
2223 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2224 "Failed to resolve specifiers, ignoring: %s", word
);
2232 if (!env_name_is_valid(k
)) {
2233 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2234 "Invalid environment name for %s, ignoring: %s", lvalue
, k
);
2238 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2247 r
= strv_extend_strv(passenv
, n
, true);
2255 int config_parse_unset_environ(
2257 const char *filename
,
2259 const char *section
,
2260 unsigned section_line
,
2267 _cleanup_strv_free_
char **n
= NULL
;
2268 const char *whole_rvalue
= rvalue
;
2269 size_t nlen
= 0, nbufsize
= 0;
2270 char*** unsetenv
= data
;
2279 if (isempty(rvalue
)) {
2280 /* Empty assignment resets the list */
2281 *unsetenv
= strv_free(*unsetenv
);
2286 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2288 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2294 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2295 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2300 r
= unit_full_printf(u
, word
, &k
);
2302 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2303 "Failed to resolve specifiers, ignoring: %s", word
);
2311 if (!env_assignment_is_valid(k
) && !env_name_is_valid(k
)) {
2312 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2313 "Invalid environment name or assignment %s, ignoring: %s", lvalue
, k
);
2317 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2326 r
= strv_extend_strv(unsetenv
, n
, true);
2334 int config_parse_ip_tos(const char *unit
,
2335 const char *filename
,
2337 const char *section
,
2338 unsigned section_line
,
2345 int *ip_tos
= data
, x
;
2352 x
= ip_tos_from_string(rvalue
);
2354 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2362 int config_parse_unit_condition_path(
2364 const char *filename
,
2366 const char *section
,
2367 unsigned section_line
,
2374 _cleanup_free_
char *p
= NULL
;
2375 Condition
**list
= data
, *c
;
2376 ConditionType t
= ltype
;
2377 bool trigger
, negate
;
2386 if (isempty(rvalue
)) {
2387 /* Empty assignment resets the list */
2388 *list
= condition_free_list(*list
);
2392 trigger
= rvalue
[0] == '|';
2396 negate
= rvalue
[0] == '!';
2400 r
= unit_full_printf(u
, rvalue
, &p
);
2402 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2406 if (!path_is_absolute(p
)) {
2407 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2411 c
= condition_new(t
, p
, trigger
, negate
);
2415 LIST_PREPEND(conditions
, *list
, c
);
2419 int config_parse_unit_condition_string(
2421 const char *filename
,
2423 const char *section
,
2424 unsigned section_line
,
2431 _cleanup_free_
char *s
= NULL
;
2432 Condition
**list
= data
, *c
;
2433 ConditionType t
= ltype
;
2434 bool trigger
, negate
;
2443 if (isempty(rvalue
)) {
2444 /* Empty assignment resets the list */
2445 *list
= condition_free_list(*list
);
2449 trigger
= rvalue
[0] == '|';
2453 negate
= rvalue
[0] == '!';
2457 r
= unit_full_printf(u
, rvalue
, &s
);
2459 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2463 c
= condition_new(t
, s
, trigger
, negate
);
2467 LIST_PREPEND(conditions
, *list
, c
);
2471 int config_parse_unit_condition_null(
2473 const char *filename
,
2475 const char *section
,
2476 unsigned section_line
,
2483 Condition
**list
= data
, *c
;
2484 bool trigger
, negate
;
2492 if (isempty(rvalue
)) {
2493 /* Empty assignment resets the list */
2494 *list
= condition_free_list(*list
);
2498 trigger
= rvalue
[0] == '|';
2502 negate
= rvalue
[0] == '!';
2506 b
= parse_boolean(rvalue
);
2508 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2515 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2519 LIST_PREPEND(conditions
, *list
, c
);
2523 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2524 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2526 int config_parse_unit_requires_mounts_for(
2528 const char *filename
,
2530 const char *section
,
2531 unsigned section_line
,
2547 for (p
= rvalue
;; ) {
2548 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2550 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2556 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2557 "Invalid syntax, ignoring: %s", rvalue
);
2561 if (!utf8_is_valid(word
)) {
2562 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2566 r
= unit_full_printf(u
, word
, &resolved
);
2568 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit name \"%s\", ignoring: %m", word
);
2572 r
= unit_require_mounts_for(u
, resolved
, UNIT_DEPENDENCY_FILE
);
2574 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", resolved
);
2580 int config_parse_documentation(const char *unit
,
2581 const char *filename
,
2583 const char *section
,
2584 unsigned section_line
,
2600 if (isempty(rvalue
)) {
2601 /* Empty assignment resets the list */
2602 u
->documentation
= strv_free(u
->documentation
);
2606 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2607 rvalue
, data
, userdata
);
2611 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2613 if (documentation_url_is_valid(*a
))
2616 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2628 static int syscall_filter_parse_one(
2630 const char *filename
,
2640 const SyscallFilterSet
*set
;
2643 set
= syscall_filter_set_find(t
);
2646 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Don't know system call group, ignoring: %s", t
);
2650 NULSTR_FOREACH(i
, set
->value
) {
2651 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, i
, false, errno_num
);
2658 id
= seccomp_syscall_resolve_name(t
);
2659 if (id
== __NR_SCMP_ERROR
) {
2661 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2665 /* If we previously wanted to forbid a syscall and now
2666 * we want to allow it, then remove it from the list
2668 if (!invert
== c
->syscall_whitelist
) {
2669 r
= hashmap_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1), INT_TO_PTR(errno_num
));
2675 (void) hashmap_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2681 int config_parse_syscall_filter(
2683 const char *filename
,
2685 const char *section
,
2686 unsigned section_line
,
2693 ExecContext
*c
= data
;
2695 bool invert
= false;
2704 if (isempty(rvalue
)) {
2705 /* Empty assignment resets the list */
2706 c
->syscall_filter
= hashmap_free(c
->syscall_filter
);
2707 c
->syscall_whitelist
= false;
2711 if (rvalue
[0] == '~') {
2716 if (!c
->syscall_filter
) {
2717 c
->syscall_filter
= hashmap_new(NULL
);
2718 if (!c
->syscall_filter
)
2722 /* Allow everything but the ones listed */
2723 c
->syscall_whitelist
= false;
2725 /* Allow nothing but the ones listed */
2726 c
->syscall_whitelist
= true;
2728 /* Accept default syscalls if we are on a whitelist */
2729 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, false, "@default", false, -1);
2737 _cleanup_free_
char *word
= NULL
, *name
= NULL
;
2740 r
= extract_first_word(&p
, &word
, NULL
, 0);
2746 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2750 r
= parse_syscall_and_errno(word
, &name
, &num
);
2752 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse syscall:errno, ignoring: %s", word
);
2756 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, name
, true, num
);
2764 int config_parse_syscall_archs(
2766 const char *filename
,
2768 const char *section
,
2769 unsigned section_line
,
2780 if (isempty(rvalue
)) {
2781 *archs
= set_free(*archs
);
2785 r
= set_ensure_allocated(archs
, NULL
);
2789 for (p
= rvalue
;;) {
2790 _cleanup_free_
char *word
= NULL
;
2793 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2799 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2800 "Invalid syntax, ignoring: %s", rvalue
);
2804 r
= seccomp_arch_from_string(word
, &a
);
2806 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2807 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
2811 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2817 int config_parse_syscall_errno(
2819 const char *filename
,
2821 const char *section
,
2822 unsigned section_line
,
2829 ExecContext
*c
= data
;
2836 if (isempty(rvalue
)) {
2837 /* Empty assignment resets to KILL */
2838 c
->syscall_errno
= 0;
2842 e
= parse_errno(rvalue
);
2844 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2848 c
->syscall_errno
= e
;
2852 int config_parse_address_families(
2854 const char *filename
,
2856 const char *section
,
2857 unsigned section_line
,
2864 ExecContext
*c
= data
;
2865 bool invert
= false;
2873 if (isempty(rvalue
)) {
2874 /* Empty assignment resets the list */
2875 c
->address_families
= set_free(c
->address_families
);
2876 c
->address_families_whitelist
= false;
2880 if (rvalue
[0] == '~') {
2885 if (!c
->address_families
) {
2886 c
->address_families
= set_new(NULL
);
2887 if (!c
->address_families
)
2890 c
->address_families_whitelist
= !invert
;
2893 for (p
= rvalue
;;) {
2894 _cleanup_free_
char *word
= NULL
;
2897 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2903 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2904 "Invalid syntax, ignoring: %s", rvalue
);
2908 af
= af_from_name(word
);
2910 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2911 "Failed to parse address family \"%s\", ignoring: %m", word
);
2915 /* If we previously wanted to forbid an address family and now
2916 * we want to allow it, then just remove it from the list.
2918 if (!invert
== c
->address_families_whitelist
) {
2919 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2923 set_remove(c
->address_families
, INT_TO_PTR(af
));
2927 int config_parse_restrict_namespaces(
2929 const char *filename
,
2931 const char *section
,
2932 unsigned section_line
,
2939 ExecContext
*c
= data
;
2940 bool invert
= false;
2943 if (isempty(rvalue
)) {
2944 /* Reset to the default. */
2945 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2949 if (rvalue
[0] == '~') {
2954 r
= parse_boolean(rvalue
);
2956 c
->restrict_namespaces
= 0;
2958 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2960 /* Not a boolean argument, in this case it's a list of namespace types. */
2962 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
2964 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
2970 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
2976 int config_parse_unit_slice(
2978 const char *filename
,
2980 const char *section
,
2981 unsigned section_line
,
2988 _cleanup_free_
char *k
= NULL
;
2989 Unit
*u
= userdata
, *slice
= NULL
;
2997 r
= unit_name_printf(u
, rvalue
, &k
);
2999 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
3003 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
3005 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
3009 r
= unit_set_slice(u
, slice
);
3011 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
3018 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
3020 int config_parse_cpu_weight(
3022 const char *filename
,
3024 const char *section
,
3025 unsigned section_line
,
3032 uint64_t *weight
= data
;
3039 r
= cg_weight_parse(rvalue
, weight
);
3041 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
3048 int config_parse_cpu_shares(
3050 const char *filename
,
3052 const char *section
,
3053 unsigned section_line
,
3060 uint64_t *shares
= data
;
3067 r
= cg_cpu_shares_parse(rvalue
, shares
);
3069 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
3076 int config_parse_cpu_quota(
3078 const char *filename
,
3080 const char *section
,
3081 unsigned section_line
,
3088 CGroupContext
*c
= data
;
3095 if (isempty(rvalue
)) {
3096 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3100 r
= parse_percent_unbounded(rvalue
);
3102 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3106 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3110 int config_parse_memory_limit(
3112 const char *filename
,
3114 const char *section
,
3115 unsigned section_line
,
3122 CGroupContext
*c
= data
;
3123 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3126 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3128 r
= parse_percent(rvalue
);
3130 r
= parse_size(rvalue
, 1024, &bytes
);
3132 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3136 bytes
= physical_memory_scale(r
, 100U);
3138 if (bytes
<= 0 || bytes
>= UINT64_MAX
) {
3139 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3144 if (streq(lvalue
, "MemoryLow"))
3145 c
->memory_low
= bytes
;
3146 else if (streq(lvalue
, "MemoryHigh"))
3147 c
->memory_high
= bytes
;
3148 else if (streq(lvalue
, "MemoryMax"))
3149 c
->memory_max
= bytes
;
3150 else if (streq(lvalue
, "MemorySwapMax"))
3151 c
->memory_swap_max
= bytes
;
3152 else if (streq(lvalue
, "MemoryLimit"))
3153 c
->memory_limit
= bytes
;
3160 int config_parse_tasks_max(
3162 const char *filename
,
3164 const char *section
,
3165 unsigned section_line
,
3172 uint64_t *tasks_max
= data
, v
;
3176 if (isempty(rvalue
)) {
3177 *tasks_max
= u
->manager
->default_tasks_max
;
3181 if (streq(rvalue
, "infinity")) {
3182 *tasks_max
= CGROUP_LIMIT_MAX
;
3186 r
= parse_percent(rvalue
);
3188 r
= safe_atou64(rvalue
, &v
);
3190 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3194 v
= system_tasks_max_scale(r
, 100U);
3196 if (v
<= 0 || v
>= UINT64_MAX
) {
3197 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3205 int config_parse_device_allow(
3207 const char *filename
,
3209 const char *section
,
3210 unsigned section_line
,
3217 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3218 CGroupContext
*c
= data
;
3219 CGroupDeviceAllow
*a
;
3220 const char *m
= NULL
;
3224 if (isempty(rvalue
)) {
3225 while (c
->device_allow
)
3226 cgroup_context_free_device_allow(c
, c
->device_allow
);
3231 r
= unit_full_printf(userdata
, rvalue
, &t
);
3233 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3234 "Failed to resolve specifiers in %s, ignoring: %m",
3238 n
= strcspn(t
, WHITESPACE
);
3240 path
= strndup(t
, n
);
3244 if (!is_deviceallow_pattern(path
)) {
3245 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3249 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3253 if (!in_charset(m
, "rwm")) {
3254 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3258 a
= new0(CGroupDeviceAllow
, 1);
3264 a
->r
= !!strchr(m
, 'r');
3265 a
->w
= !!strchr(m
, 'w');
3266 a
->m
= !!strchr(m
, 'm');
3268 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3272 int config_parse_io_weight(
3274 const char *filename
,
3276 const char *section
,
3277 unsigned section_line
,
3284 uint64_t *weight
= data
;
3291 r
= cg_weight_parse(rvalue
, weight
);
3293 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3300 int config_parse_io_device_weight(
3302 const char *filename
,
3304 const char *section
,
3305 unsigned section_line
,
3312 _cleanup_free_
char *path
= NULL
;
3313 CGroupIODeviceWeight
*w
;
3314 CGroupContext
*c
= data
;
3324 if (isempty(rvalue
)) {
3325 while (c
->io_device_weights
)
3326 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3331 n
= strcspn(rvalue
, WHITESPACE
);
3332 weight
= rvalue
+ n
;
3333 weight
+= strspn(weight
, WHITESPACE
);
3335 if (isempty(weight
)) {
3336 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3340 path
= strndup(rvalue
, n
);
3344 if (!path_startswith(path
, "/dev")) {
3345 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3349 r
= cg_weight_parse(weight
, &u
);
3351 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3355 assert(u
!= CGROUP_WEIGHT_INVALID
);
3357 w
= new0(CGroupIODeviceWeight
, 1);
3366 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3370 int config_parse_io_limit(
3372 const char *filename
,
3374 const char *section
,
3375 unsigned section_line
,
3382 _cleanup_free_
char *path
= NULL
;
3383 CGroupIODeviceLimit
*l
= NULL
, *t
;
3384 CGroupContext
*c
= data
;
3385 CGroupIOLimitType type
;
3395 type
= cgroup_io_limit_type_from_string(lvalue
);
3398 if (isempty(rvalue
)) {
3399 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3400 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3404 n
= strcspn(rvalue
, WHITESPACE
);
3406 limit
+= strspn(limit
, WHITESPACE
);
3409 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3413 path
= strndup(rvalue
, n
);
3417 if (!path_startswith(path
, "/dev")) {
3418 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3422 if (streq("infinity", limit
)) {
3423 num
= CGROUP_LIMIT_MAX
;
3425 r
= parse_size(limit
, 1000, &num
);
3426 if (r
< 0 || num
<= 0) {
3427 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3432 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3433 if (path_equal(path
, t
->path
)) {
3440 CGroupIOLimitType ttype
;
3442 l
= new0(CGroupIODeviceLimit
, 1);
3448 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3449 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3451 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3454 l
->limits
[type
] = num
;
3459 int config_parse_blockio_weight(
3461 const char *filename
,
3463 const char *section
,
3464 unsigned section_line
,
3471 uint64_t *weight
= data
;
3478 r
= cg_blkio_weight_parse(rvalue
, weight
);
3480 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3487 int config_parse_blockio_device_weight(
3489 const char *filename
,
3491 const char *section
,
3492 unsigned section_line
,
3499 _cleanup_free_
char *path
= NULL
;
3500 CGroupBlockIODeviceWeight
*w
;
3501 CGroupContext
*c
= data
;
3511 if (isempty(rvalue
)) {
3512 while (c
->blockio_device_weights
)
3513 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3518 n
= strcspn(rvalue
, WHITESPACE
);
3519 weight
= rvalue
+ n
;
3520 weight
+= strspn(weight
, WHITESPACE
);
3522 if (isempty(weight
)) {
3523 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3527 path
= strndup(rvalue
, n
);
3531 if (!path_startswith(path
, "/dev")) {
3532 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3536 r
= cg_blkio_weight_parse(weight
, &u
);
3538 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3542 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3544 w
= new0(CGroupBlockIODeviceWeight
, 1);
3553 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3557 int config_parse_blockio_bandwidth(
3559 const char *filename
,
3561 const char *section
,
3562 unsigned section_line
,
3569 _cleanup_free_
char *path
= NULL
;
3570 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3571 CGroupContext
*c
= data
;
3572 const char *bandwidth
;
3582 read
= streq("BlockIOReadBandwidth", lvalue
);
3584 if (isempty(rvalue
)) {
3585 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3586 b
->rbps
= CGROUP_LIMIT_MAX
;
3587 b
->wbps
= CGROUP_LIMIT_MAX
;
3592 n
= strcspn(rvalue
, WHITESPACE
);
3593 bandwidth
= rvalue
+ n
;
3594 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3597 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3601 path
= strndup(rvalue
, n
);
3605 if (!path_startswith(path
, "/dev")) {
3606 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3610 r
= parse_size(bandwidth
, 1000, &bytes
);
3611 if (r
< 0 || bytes
<= 0) {
3612 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3616 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3617 if (path_equal(path
, t
->path
)) {
3624 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3630 b
->rbps
= CGROUP_LIMIT_MAX
;
3631 b
->wbps
= CGROUP_LIMIT_MAX
;
3633 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3644 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3646 int config_parse_job_mode_isolate(
3648 const char *filename
,
3650 const char *section
,
3651 unsigned section_line
,
3665 r
= parse_boolean(rvalue
);
3667 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3671 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3675 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode
, exec_preserve_mode
, ExecPreserveMode
, "Failed to parse runtime directory preserve mode");
3677 int config_parse_exec_directories(
3679 const char *filename
,
3681 const char *section
,
3682 unsigned section_line
,
3699 if (isempty(rvalue
)) {
3700 /* Empty assignment resets the list */
3701 *rt
= strv_free(*rt
);
3705 for (p
= rvalue
;;) {
3706 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3708 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3712 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3713 "Invalid syntax, ignoring: %s", rvalue
);
3719 r
= unit_full_printf(u
, word
, &k
);
3721 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3722 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
3726 if (!path_is_safe(k
) || path_is_absolute(k
)) {
3727 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3728 "%s= path is not valid, ignoring assignment: %s", lvalue
, rvalue
);
3732 r
= strv_push(rt
, k
);
3739 int config_parse_set_status(
3741 const char *filename
,
3743 const char *section
,
3744 unsigned section_line
,
3752 const char *word
, *state
;
3754 ExitStatusSet
*status_set
= data
;
3761 /* Empty assignment resets the list */
3762 if (isempty(rvalue
)) {
3763 exit_status_set_free(status_set
);
3767 FOREACH_WORD(word
, l
, rvalue
, state
) {
3768 _cleanup_free_
char *temp
;
3772 temp
= strndup(word
, l
);
3776 r
= safe_atoi(temp
, &val
);
3778 val
= signal_from_string_try_harder(temp
);
3781 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3784 set
= &status_set
->signal
;
3786 if (val
< 0 || val
> 255) {
3787 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3790 set
= &status_set
->status
;
3793 r
= set_ensure_allocated(set
, NULL
);
3797 r
= set_put(*set
, INT_TO_PTR(val
));
3799 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3803 if (!isempty(state
))
3804 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3809 int config_parse_namespace_path_strv(
3811 const char *filename
,
3813 const char *section
,
3814 unsigned section_line
,
3831 if (isempty(rvalue
)) {
3832 /* Empty assignment resets the list */
3833 *sv
= strv_free(*sv
);
3839 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
3841 bool ignore_enoent
= false, shall_prefix
= false;
3843 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3849 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
3853 if (!utf8_is_valid(word
)) {
3854 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3859 if (startswith(w
, "-")) {
3860 ignore_enoent
= true;
3863 if (startswith(w
, "+")) {
3864 shall_prefix
= true;
3868 r
= unit_full_printf(u
, w
, &resolved
);
3870 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s: %m", word
);
3874 if (!path_is_absolute(resolved
)) {
3875 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
3879 path_kill_slashes(resolved
);
3881 joined
= strjoin(ignore_enoent
? "-" : "",
3882 shall_prefix
? "+" : "",
3885 r
= strv_push(sv
, joined
);
3895 int config_parse_bind_paths(
3897 const char *filename
,
3899 const char *section
,
3900 unsigned section_line
,
3907 ExecContext
*c
= data
;
3917 if (isempty(rvalue
)) {
3918 /* Empty assignment resets the list */
3919 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
3920 c
->bind_mounts
= NULL
;
3921 c
->n_bind_mounts
= 0;
3927 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
3928 _cleanup_free_
char *sresolved
= NULL
, *dresolved
= NULL
;
3929 char *s
= NULL
, *d
= NULL
;
3930 bool rbind
= true, ignore_enoent
= false;
3932 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
3938 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3942 r
= unit_full_printf(u
, source
, &sresolved
);
3944 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3945 "Failed to resolved specifiers in \"%s\", ignoring: %m", source
);
3951 ignore_enoent
= true;
3955 if (!utf8_is_valid(s
)) {
3956 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, s
);
3959 if (!path_is_absolute(s
)) {
3960 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute source path, ignoring: %s", s
);
3964 path_kill_slashes(s
);
3966 /* Optionally, the destination is specified. */
3967 if (p
&& p
[-1] == ':') {
3968 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
3972 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3976 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':': %s", rvalue
);
3980 r
= unit_full_printf(u
, destination
, &dresolved
);
3982 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3983 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination
);
3987 if (!utf8_is_valid(dresolved
)) {
3988 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, dresolved
);
3991 if (!path_is_absolute(dresolved
)) {
3992 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute destination path, ignoring: %s", dresolved
);
3996 d
= path_kill_slashes(dresolved
);
3998 /* Optionally, there's also a short option string specified */
3999 if (p
&& p
[-1] == ':') {
4000 _cleanup_free_
char *options
= NULL
;
4002 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
4006 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4010 if (isempty(options
) || streq(options
, "rbind"))
4012 else if (streq(options
, "norbind"))
4015 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
4022 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
4026 .read_only
= !!strstr(lvalue
, "ReadOnly"),
4028 .ignore_enoent
= ignore_enoent
,
4037 int config_parse_no_new_privileges(
4039 const char *filename
,
4041 const char *section
,
4042 unsigned section_line
,
4049 ExecContext
*c
= data
;
4057 k
= parse_boolean(rvalue
);
4059 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
4063 c
->no_new_privileges
= k
;
4068 int config_parse_protect_home(
4070 const char *filename
,
4072 const char *section
,
4073 unsigned section_line
,
4080 ExecContext
*c
= data
;
4088 /* Our enum shall be a superset of booleans, hence first try
4089 * to parse as boolean, and then as enum */
4091 k
= parse_boolean(rvalue
);
4093 c
->protect_home
= PROTECT_HOME_YES
;
4095 c
->protect_home
= PROTECT_HOME_NO
;
4099 h
= protect_home_from_string(rvalue
);
4101 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
4105 c
->protect_home
= h
;
4111 int config_parse_protect_system(
4113 const char *filename
,
4115 const char *section
,
4116 unsigned section_line
,
4123 ExecContext
*c
= data
;
4131 /* Our enum shall be a superset of booleans, hence first try
4132 * to parse as boolean, and then as enum */
4134 k
= parse_boolean(rvalue
);
4136 c
->protect_system
= PROTECT_SYSTEM_YES
;
4138 c
->protect_system
= PROTECT_SYSTEM_NO
;
4142 s
= protect_system_from_string(rvalue
);
4144 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
4148 c
->protect_system
= s
;
4154 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode
, exec_keyring_mode
, ExecKeyringMode
, "Failed to parse keyring mode");
4156 int config_parse_job_timeout_sec(
4158 const char *filename
,
4160 const char *section
,
4161 unsigned section_line
,
4177 r
= parse_sec_fix_0(rvalue
, &usec
);
4179 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue
);
4183 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
4184 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
4187 if (!u
->job_running_timeout_set
)
4188 u
->job_running_timeout
= usec
;
4190 u
->job_timeout
= usec
;
4195 int config_parse_job_running_timeout_sec(
4197 const char *filename
,
4199 const char *section
,
4200 unsigned section_line
,
4216 r
= parse_sec_fix_0(rvalue
, &usec
);
4218 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue
);
4222 u
->job_running_timeout
= usec
;
4223 u
->job_running_timeout_set
= true;
4228 #define FOLLOW_MAX 8
4230 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4241 /* This will update the filename pointer if the loaded file is
4242 * reached by a symlink. The old string will be freed. */
4245 char *target
, *name
;
4247 if (c
++ >= FOLLOW_MAX
)
4250 path_kill_slashes(*filename
);
4252 /* Add the file name we are currently looking at to
4253 * the names of this unit, but only if it is a valid
4255 name
= basename(*filename
);
4256 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4258 id
= set_get(names
, name
);
4264 r
= set_consume(names
, id
);
4270 /* Try to open the file name, but don't if its a symlink */
4271 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4278 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4279 r
= readlink_and_make_absolute(*filename
, &target
);
4287 f
= fdopen(fd
, "re");
4299 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4307 /* Let's try to add in all symlink names we found */
4308 while ((k
= set_steal_first(names
))) {
4310 /* First try to merge in the other name into our
4312 r
= unit_merge_by_name(*u
, k
);
4316 /* Hmm, we couldn't merge the other unit into
4317 * ours? Then let's try it the other way
4320 /* If the symlink name we are looking at is unit template, then
4321 we must search for instance of this template */
4322 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4323 _cleanup_free_
char *instance
= NULL
;
4325 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4329 other
= manager_get_unit((*u
)->manager
, instance
);
4331 other
= manager_get_unit((*u
)->manager
, k
);
4336 r
= unit_merge(other
, *u
);
4339 return merge_by_names(u
, names
, NULL
);
4347 unit_choose_id(*u
, id
);
4355 static int load_from_path(Unit
*u
, const char *path
) {
4356 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4357 _cleanup_fclose_
FILE *f
= NULL
;
4358 _cleanup_free_
char *filename
= NULL
;
4367 symlink_names
= set_new(&string_hash_ops
);
4371 if (path_is_absolute(path
)) {
4373 filename
= strdup(path
);
4377 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4379 filename
= mfree(filename
);
4387 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4389 /* Instead of opening the path right away, we manually
4390 * follow all symlinks and add their name to our unit
4391 * name set while doing so */
4392 filename
= path_make_absolute(path
, *p
);
4396 if (u
->manager
->unit_path_cache
&&
4397 !set_get(u
->manager
->unit_path_cache
, filename
))
4400 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4403 filename
= mfree(filename
);
4405 /* ENOENT means that the file is missing or is a dangling symlink.
4406 * ENOTDIR means that one of paths we expect to be is a directory
4407 * is not a directory, we should just ignore that.
4408 * EACCES means that the directory or file permissions are wrong.
4411 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4412 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4415 /* Empty the symlink names for the next run */
4416 set_clear_free(symlink_names
);
4421 /* Hmm, no suitable file found? */
4424 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4425 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4430 r
= merge_by_names(&merged
, symlink_names
, id
);
4435 u
->load_state
= UNIT_MERGED
;
4439 if (fstat(fileno(f
), &st
) < 0)
4442 if (null_or_empty(&st
)) {
4443 u
->load_state
= UNIT_MASKED
;
4444 u
->fragment_mtime
= 0;
4446 u
->load_state
= UNIT_LOADED
;
4447 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4449 /* Now, parse the file contents */
4450 r
= config_parse(u
->id
, filename
, f
,
4451 UNIT_VTABLE(u
)->sections
,
4452 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4453 CONFIG_PARSE_ALLOW_INCLUDE
, u
);
4458 free(u
->fragment_path
);
4459 u
->fragment_path
= filename
;
4462 if (u
->source_path
) {
4463 if (stat(u
->source_path
, &st
) >= 0)
4464 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4466 u
->source_mtime
= 0;
4472 int unit_load_fragment(Unit
*u
) {
4478 assert(u
->load_state
== UNIT_STUB
);
4482 u
->load_state
= UNIT_LOADED
;
4486 /* First, try to find the unit under its id. We always look
4487 * for unit files in the default directories, to make it easy
4488 * to override things by placing things in /etc/systemd/system */
4489 r
= load_from_path(u
, u
->id
);
4493 /* Try to find an alias we can load this with */
4494 if (u
->load_state
== UNIT_STUB
) {
4495 SET_FOREACH(t
, u
->names
, i
) {
4500 r
= load_from_path(u
, t
);
4504 if (u
->load_state
!= UNIT_STUB
)
4509 /* And now, try looking for it under the suggested (originally linked) path */
4510 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4512 r
= load_from_path(u
, u
->fragment_path
);
4516 if (u
->load_state
== UNIT_STUB
)
4517 /* Hmm, this didn't work? Then let's get rid
4518 * of the fragment path stored for us, so that
4519 * we don't point to an invalid location. */
4520 u
->fragment_path
= mfree(u
->fragment_path
);
4523 /* Look for a template */
4524 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4525 _cleanup_free_
char *k
= NULL
;
4527 r
= unit_name_template(u
->id
, &k
);
4531 r
= load_from_path(u
, k
);
4534 log_unit_notice(u
, "Unit configuration has fatal error, unit will not be started.");
4538 if (u
->load_state
== UNIT_STUB
) {
4539 SET_FOREACH(t
, u
->names
, i
) {
4540 _cleanup_free_
char *z
= NULL
;
4545 r
= unit_name_template(t
, &z
);
4549 r
= load_from_path(u
, z
);
4553 if (u
->load_state
!= UNIT_STUB
)
4562 void unit_dump_config_items(FILE *f
) {
4563 static const struct {
4564 const ConfigParserCallback callback
;
4567 #if !HAVE_SYSV_COMPAT || !HAVE_SECCOMP || !HAVE_PAM || !HAVE_SELINUX || !ENABLE_SMACK || !HAVE_APPARMOR
4568 { config_parse_warn_compat
, "NOTSUPPORTED" },
4570 { config_parse_int
, "INTEGER" },
4571 { config_parse_unsigned
, "UNSIGNED" },
4572 { config_parse_iec_size
, "SIZE" },
4573 { config_parse_iec_uint64
, "SIZE" },
4574 { config_parse_si_size
, "SIZE" },
4575 { config_parse_bool
, "BOOLEAN" },
4576 { config_parse_string
, "STRING" },
4577 { config_parse_path
, "PATH" },
4578 { config_parse_unit_path_printf
, "PATH" },
4579 { config_parse_strv
, "STRING [...]" },
4580 { config_parse_exec_nice
, "NICE" },
4581 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4582 { config_parse_exec_io_class
, "IOCLASS" },
4583 { config_parse_exec_io_priority
, "IOPRIORITY" },
4584 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4585 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4586 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4587 { config_parse_mode
, "MODE" },
4588 { config_parse_unit_env_file
, "FILE" },
4589 { config_parse_exec_output
, "OUTPUT" },
4590 { config_parse_exec_input
, "INPUT" },
4591 { config_parse_log_facility
, "FACILITY" },
4592 { config_parse_log_level
, "LEVEL" },
4593 { config_parse_exec_secure_bits
, "SECUREBITS" },
4594 { config_parse_capability_set
, "BOUNDINGSET" },
4595 { config_parse_limit
, "LIMIT" },
4596 { config_parse_unit_deps
, "UNIT [...]" },
4597 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4598 { config_parse_service_type
, "SERVICETYPE" },
4599 { config_parse_service_restart
, "SERVICERESTART" },
4600 #if HAVE_SYSV_COMPAT
4601 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4603 { config_parse_kill_mode
, "KILLMODE" },
4604 { config_parse_signal
, "SIGNAL" },
4605 { config_parse_socket_listen
, "SOCKET [...]" },
4606 { config_parse_socket_bind
, "SOCKETBIND" },
4607 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4608 { config_parse_sec
, "SECONDS" },
4609 { config_parse_nsec
, "NANOSECONDS" },
4610 { config_parse_namespace_path_strv
, "PATH [...]" },
4611 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4612 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4613 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4614 { config_parse_unit_string_printf
, "STRING" },
4615 { config_parse_trigger_unit
, "UNIT" },
4616 { config_parse_timer
, "TIMER" },
4617 { config_parse_path_spec
, "PATH" },
4618 { config_parse_notify_access
, "ACCESS" },
4619 { config_parse_ip_tos
, "TOS" },
4620 { config_parse_unit_condition_path
, "CONDITION" },
4621 { config_parse_unit_condition_string
, "CONDITION" },
4622 { config_parse_unit_condition_null
, "CONDITION" },
4623 { config_parse_unit_slice
, "SLICE" },
4624 { config_parse_documentation
, "URL" },
4625 { config_parse_service_timeout
, "SECONDS" },
4626 { config_parse_emergency_action
, "ACTION" },
4627 { config_parse_set_status
, "STATUS" },
4628 { config_parse_service_sockets
, "SOCKETS" },
4629 { config_parse_environ
, "ENVIRON" },
4631 { config_parse_syscall_filter
, "SYSCALLS" },
4632 { config_parse_syscall_archs
, "ARCHS" },
4633 { config_parse_syscall_errno
, "ERRNO" },
4634 { config_parse_address_families
, "FAMILIES" },
4635 { config_parse_restrict_namespaces
, "NAMESPACES" },
4637 { config_parse_cpu_shares
, "SHARES" },
4638 { config_parse_cpu_weight
, "WEIGHT" },
4639 { config_parse_memory_limit
, "LIMIT" },
4640 { config_parse_device_allow
, "DEVICE" },
4641 { config_parse_device_policy
, "POLICY" },
4642 { config_parse_io_limit
, "LIMIT" },
4643 { config_parse_io_weight
, "WEIGHT" },
4644 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4645 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4646 { config_parse_blockio_weight
, "WEIGHT" },
4647 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4648 { config_parse_long
, "LONG" },
4649 { config_parse_socket_service
, "SERVICE" },
4651 { config_parse_exec_selinux_context
, "LABEL" },
4653 { config_parse_job_mode
, "MODE" },
4654 { config_parse_job_mode_isolate
, "BOOLEAN" },
4655 { config_parse_personality
, "PERSONALITY" },
4658 const char *prev
= NULL
;
4663 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4664 const char *rvalue
= "OTHER", *lvalue
;
4668 const ConfigPerfItem
*p
;
4670 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4672 dot
= strchr(i
, '.');
4673 lvalue
= dot
? dot
+ 1 : i
;
4677 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
4681 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
4684 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
4685 if (p
->parse
== table
[j
].callback
) {
4686 rvalue
= table
[j
].rvalue
;
4690 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);