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);
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
,
287 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
289 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
295 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
296 "Invalid syntax, ignoring: %s", rvalue
);
300 r
= unit_full_printf(u
, word
, &k
);
302 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
303 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
307 if (!utf8_is_valid(k
)) {
308 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
312 if (!path_is_absolute(k
)) {
313 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
314 "Symlink path is not absolute: %s", k
);
318 path_kill_slashes(k
);
327 int config_parse_socket_listen(const char *unit
,
328 const char *filename
,
331 unsigned section_line
,
338 _cleanup_free_ SocketPort
*p
= NULL
;
350 if (isempty(rvalue
)) {
351 /* An empty assignment removes all ports */
352 socket_free_ports(s
);
356 p
= new0(SocketPort
, 1);
360 if (ltype
!= SOCKET_SOCKET
) {
363 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
365 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
369 path_kill_slashes(p
->path
);
371 } else if (streq(lvalue
, "ListenNetlink")) {
372 _cleanup_free_
char *k
= NULL
;
374 p
->type
= SOCKET_SOCKET
;
375 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
377 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
381 r
= socket_address_parse_netlink(&p
->address
, k
);
383 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
388 _cleanup_free_
char *k
= NULL
;
390 p
->type
= SOCKET_SOCKET
;
391 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
393 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
397 r
= socket_address_parse_and_warn(&p
->address
, k
);
399 if (r
!= -EAFNOSUPPORT
)
400 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
405 if (streq(lvalue
, "ListenStream"))
406 p
->address
.type
= SOCK_STREAM
;
407 else if (streq(lvalue
, "ListenDatagram"))
408 p
->address
.type
= SOCK_DGRAM
;
410 assert(streq(lvalue
, "ListenSequentialPacket"));
411 p
->address
.type
= SOCK_SEQPACKET
;
414 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
415 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
421 p
->auxiliary_fds
= NULL
;
422 p
->n_auxiliary_fds
= 0;
426 LIST_FIND_TAIL(port
, s
->ports
, tail
);
427 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
429 LIST_PREPEND(port
, s
->ports
, p
);
435 int config_parse_socket_protocol(const char *unit
,
436 const char *filename
,
439 unsigned section_line
,
454 if (streq(rvalue
, "udplite"))
455 s
->socket_protocol
= IPPROTO_UDPLITE
;
456 else if (streq(rvalue
, "sctp"))
457 s
->socket_protocol
= IPPROTO_SCTP
;
459 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
466 int config_parse_socket_bind(const char *unit
,
467 const char *filename
,
470 unsigned section_line
,
478 SocketAddressBindIPv6Only b
;
487 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
491 r
= parse_boolean(rvalue
);
493 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
497 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
499 s
->bind_ipv6_only
= b
;
504 int config_parse_exec_nice(
506 const char *filename
,
509 unsigned section_line
,
516 ExecContext
*c
= data
;
524 r
= parse_nice(rvalue
, &priority
);
527 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
529 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
540 int config_parse_exec_oom_score_adjust(const char* unit
,
541 const char *filename
,
544 unsigned section_line
,
551 ExecContext
*c
= data
;
559 r
= safe_atoi(rvalue
, &oa
);
561 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
565 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
566 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
570 c
->oom_score_adjust
= oa
;
571 c
->oom_score_adjust_set
= true;
576 int config_parse_exec(
578 const char *filename
,
581 unsigned section_line
,
588 ExecCommand
**e
= data
;
600 rvalue
+= strspn(rvalue
, WHITESPACE
);
602 if (isempty(rvalue
)) {
603 /* An empty assignment resets the list */
604 *e
= exec_command_free_list(*e
);
610 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
611 ExecCommandFlags flags
= 0;
612 bool ignore
= false, separate_argv0
= false;
613 _cleanup_free_ ExecCommand
*nce
= NULL
;
614 _cleanup_strv_free_
char **n
= NULL
;
615 size_t nlen
= 0, nbufsize
= 0;
620 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
626 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
627 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
628 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
629 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
630 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
631 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
632 * other sandboxing, with some special exceptions for changing UID.
634 * The idea is that '!!' may be used to write services that can take benefit of systemd's
635 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
636 * privilege dropping within the daemon if the kernel does not offer that. */
638 if (*f
== '-' && !(flags
& EXEC_COMMAND_IGNORE_FAILURE
)) {
639 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
641 } else if (*f
== '@' && !separate_argv0
)
642 separate_argv0
= true;
643 else if (*f
== '+' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
644 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
645 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
646 flags
|= EXEC_COMMAND_NO_SETUID
;
647 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
648 flags
&= ~EXEC_COMMAND_NO_SETUID
;
649 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
655 r
= unit_full_printf(u
, f
, &path
);
657 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
658 "Failed to resolve unit specifiers on %s%s: %m",
659 f
, ignore
? ", ignoring" : "");
660 return ignore
? 0 : -ENOEXEC
;
664 /* First word is either "-" or "@" with no command. */
665 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
666 "Empty path in command line%s: \"%s\"",
667 ignore
? ", ignoring" : "", rvalue
);
668 return ignore
? 0 : -ENOEXEC
;
670 if (!string_is_safe(path
)) {
671 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
672 "Executable path contains special characters%s: %s",
673 ignore
? ", ignoring" : "", rvalue
);
674 return ignore
? 0 : -ENOEXEC
;
676 if (!path_is_absolute(path
)) {
677 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
678 "Executable path is not absolute%s: %s",
679 ignore
? ", ignoring" : "", rvalue
);
680 return ignore
? 0 : -ENOEXEC
;
682 if (endswith(path
, "/")) {
683 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
684 "Executable path specifies a directory%s: %s",
685 ignore
? ", ignoring" : "", rvalue
);
686 return ignore
? 0 : -ENOEXEC
;
689 if (!separate_argv0
) {
692 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
702 path_kill_slashes(path
);
704 while (!isempty(p
)) {
705 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
707 /* Check explicitly for an unquoted semicolon as
708 * command separator token. */
709 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
711 p
+= strspn(p
, WHITESPACE
);
716 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
717 * extract_first_word() would return the same for all of those. */
718 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
722 p
+= strspn(p
, WHITESPACE
);
724 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
735 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
739 return ignore
? 0 : -ENOEXEC
;
741 r
= unit_full_printf(u
, word
, &resolved
);
743 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
744 "Failed to resolve unit specifiers on %s%s: %m",
745 word
, ignore
? ", ignoring" : "");
746 return ignore
? 0 : -ENOEXEC
;
749 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
751 n
[nlen
++] = resolved
;
757 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
758 "Empty executable name or zeroeth argument%s: %s",
759 ignore
? ", ignoring" : "", rvalue
);
760 return ignore
? 0 : -ENOEXEC
;
763 nce
= new0(ExecCommand
, 1);
771 exec_command_append_list(e
, nce
);
773 /* Do not _cleanup_free_ these. */
784 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
785 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
787 int config_parse_socket_bindtodevice(
789 const char *filename
,
792 unsigned section_line
,
807 if (rvalue
[0] && !streq(rvalue
, "*")) {
808 if (!ifname_valid(rvalue
)) {
809 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
819 free(s
->bind_to_device
);
820 s
->bind_to_device
= n
;
825 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input literal specifier");
826 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output literal specifier");
828 int config_parse_exec_input(const char *unit
,
829 const char *filename
,
832 unsigned section_line
,
838 ExecContext
*c
= data
;
847 name
= startswith(rvalue
, "fd:");
849 /* Strip prefix and validate fd name */
850 if (!fdname_is_valid(name
)) {
851 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
854 c
->std_input
= EXEC_INPUT_NAMED_FD
;
855 r
= free_and_strdup(&c
->stdio_fdname
[STDIN_FILENO
], name
);
860 ExecInput ei
= exec_input_from_string(rvalue
);
861 if (ei
== _EXEC_INPUT_INVALID
)
862 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
869 int config_parse_exec_output(const char *unit
,
870 const char *filename
,
873 unsigned section_line
,
879 ExecContext
*c
= data
;
890 name
= startswith(rvalue
, "fd:");
892 /* Strip prefix and validate fd name */
893 if (!fdname_is_valid(name
)) {
894 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
897 eo
= EXEC_OUTPUT_NAMED_FD
;
899 eo
= exec_output_from_string(rvalue
);
900 if (eo
== _EXEC_OUTPUT_INVALID
) {
901 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
906 if (streq(lvalue
, "StandardOutput")) {
908 r
= free_and_strdup(&c
->stdio_fdname
[STDOUT_FILENO
], name
);
912 } else if (streq(lvalue
, "StandardError")) {
914 r
= free_and_strdup(&c
->stdio_fdname
[STDERR_FILENO
], name
);
919 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output property, ignoring: %s", lvalue
);
924 int config_parse_exec_io_class(const char *unit
,
925 const char *filename
,
928 unsigned section_line
,
935 ExecContext
*c
= data
;
943 x
= ioprio_class_from_string(rvalue
);
945 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
949 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
950 c
->ioprio_set
= true;
955 int config_parse_exec_io_priority(const char *unit
,
956 const char *filename
,
959 unsigned section_line
,
966 ExecContext
*c
= data
;
974 r
= ioprio_parse_priority(rvalue
, &i
);
976 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
980 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
981 c
->ioprio_set
= true;
986 int config_parse_exec_cpu_sched_policy(const char *unit
,
987 const char *filename
,
990 unsigned section_line
,
998 ExecContext
*c
= data
;
1006 x
= sched_policy_from_string(rvalue
);
1008 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1012 c
->cpu_sched_policy
= x
;
1013 /* Moving to or from real-time policy? We need to adjust the priority */
1014 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
1015 c
->cpu_sched_set
= true;
1020 int config_parse_exec_cpu_sched_prio(const char *unit
,
1021 const char *filename
,
1023 const char *section
,
1024 unsigned section_line
,
1031 ExecContext
*c
= data
;
1039 r
= safe_atoi(rvalue
, &i
);
1041 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1045 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1046 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1047 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1049 if (i
< min
|| i
> max
) {
1050 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1054 c
->cpu_sched_priority
= i
;
1055 c
->cpu_sched_set
= true;
1060 int config_parse_exec_cpu_affinity(const char *unit
,
1061 const char *filename
,
1063 const char *section
,
1064 unsigned section_line
,
1071 ExecContext
*c
= data
;
1072 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1080 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1085 CPU_FREE(c
->cpuset
);
1088 /* An empty assignment resets the CPU list */
1094 c
->cpuset_ncpus
= ncpus
;
1099 int config_parse_exec_secure_bits(const char *unit
,
1100 const char *filename
,
1102 const char *section
,
1103 unsigned section_line
,
1110 ExecContext
*c
= data
;
1118 if (isempty(rvalue
)) {
1119 /* An empty assignment resets the field */
1124 r
= secure_bits_from_string(rvalue
);
1128 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1129 "Invalid syntax, ignoring: %s", rvalue
);
1138 int config_parse_capability_set(
1140 const char *filename
,
1142 const char *section
,
1143 unsigned section_line
,
1150 uint64_t *capability_set
= data
;
1151 uint64_t sum
= 0, initial
= 0;
1152 bool invert
= false;
1160 if (rvalue
[0] == '~') {
1165 if (streq(lvalue
, "CapabilityBoundingSet"))
1166 initial
= CAP_ALL
; /* initialized to all bits on */
1167 /* else "AmbientCapabilities" initialized to all bits off */
1169 r
= capability_set_from_string(rvalue
, &sum
);
1173 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word: %s", rvalue
);
1177 if (sum
== 0 || *capability_set
== initial
)
1178 /* "", "~" or uninitialized data -> replace */
1179 *capability_set
= invert
? ~sum
: sum
;
1181 /* previous data -> merge */
1183 *capability_set
&= ~sum
;
1185 *capability_set
|= sum
;
1191 int config_parse_limit(
1193 const char *filename
,
1195 const char *section
,
1196 unsigned section_line
,
1203 struct rlimit
**rl
= data
, d
= {};
1211 r
= rlimit_parse(ltype
, rvalue
, &d
);
1213 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1217 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1224 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1232 #ifdef HAVE_SYSV_COMPAT
1233 int config_parse_sysv_priority(const char *unit
,
1234 const char *filename
,
1236 const char *section
,
1237 unsigned section_line
,
1244 int *priority
= data
;
1252 r
= safe_atoi(rvalue
, &i
);
1253 if (r
< 0 || i
< 0) {
1254 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1258 *priority
= (int) i
;
1263 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1264 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1266 int config_parse_exec_mount_flags(
1268 const char *filename
,
1270 const char *section
,
1271 unsigned section_line
,
1279 ExecContext
*c
= data
;
1287 r
= mount_propagation_flags_from_string(rvalue
, &c
->mount_flags
);
1289 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1294 int config_parse_exec_selinux_context(
1296 const char *filename
,
1298 const char *section
,
1299 unsigned section_line
,
1306 ExecContext
*c
= data
;
1317 if (isempty(rvalue
)) {
1318 c
->selinux_context
= mfree(c
->selinux_context
);
1319 c
->selinux_context_ignore
= false;
1323 if (rvalue
[0] == '-') {
1329 r
= unit_full_printf(u
, rvalue
, &k
);
1331 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1332 "Failed to resolve specifiers%s: %m",
1333 ignore
? ", ignoring" : "");
1334 return ignore
? 0 : -ENOEXEC
;
1337 free(c
->selinux_context
);
1338 c
->selinux_context
= k
;
1339 c
->selinux_context_ignore
= ignore
;
1344 int config_parse_exec_apparmor_profile(
1346 const char *filename
,
1348 const char *section
,
1349 unsigned section_line
,
1356 ExecContext
*c
= data
;
1367 if (isempty(rvalue
)) {
1368 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1369 c
->apparmor_profile_ignore
= false;
1373 if (rvalue
[0] == '-') {
1379 r
= unit_full_printf(u
, rvalue
, &k
);
1381 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1382 "Failed to resolve specifiers%s: %m",
1383 ignore
? ", ignoring" : "");
1384 return ignore
? 0 : -ENOEXEC
;
1387 free(c
->apparmor_profile
);
1388 c
->apparmor_profile
= k
;
1389 c
->apparmor_profile_ignore
= ignore
;
1394 int config_parse_exec_smack_process_label(
1396 const char *filename
,
1398 const char *section
,
1399 unsigned section_line
,
1406 ExecContext
*c
= data
;
1417 if (isempty(rvalue
)) {
1418 c
->smack_process_label
= mfree(c
->smack_process_label
);
1419 c
->smack_process_label_ignore
= false;
1423 if (rvalue
[0] == '-') {
1429 r
= unit_full_printf(u
, rvalue
, &k
);
1431 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1432 "Failed to resolve specifiers%s: %m",
1433 ignore
? ", ignoring" : "");
1434 return ignore
? 0 : -ENOEXEC
;
1437 free(c
->smack_process_label
);
1438 c
->smack_process_label
= k
;
1439 c
->smack_process_label_ignore
= ignore
;
1444 int config_parse_timer(const char *unit
,
1445 const char *filename
,
1447 const char *section
,
1448 unsigned section_line
,
1459 CalendarSpec
*c
= NULL
;
1461 _cleanup_free_
char *k
= NULL
;
1469 if (isempty(rvalue
)) {
1470 /* Empty assignment resets list */
1471 timer_free_values(t
);
1475 b
= timer_base_from_string(lvalue
);
1477 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1481 r
= unit_full_printf(u
, rvalue
, &k
);
1483 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1487 if (b
== TIMER_CALENDAR
) {
1488 if (calendar_spec_from_string(k
, &c
) < 0) {
1489 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1493 if (parse_sec(k
, &usec
) < 0) {
1494 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1499 v
= new0(TimerValue
, 1);
1501 calendar_spec_free(c
);
1507 v
->calendar_spec
= c
;
1509 LIST_PREPEND(value
, t
->values
, v
);
1514 int config_parse_trigger_unit(
1516 const char *filename
,
1518 const char *section
,
1519 unsigned section_line
,
1526 _cleanup_free_
char *p
= NULL
;
1536 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1537 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1541 r
= unit_name_printf(u
, rvalue
, &p
);
1543 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1547 type
= unit_name_to_type(p
);
1549 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1553 if (type
== u
->type
) {
1554 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1558 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1560 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1567 int config_parse_path_spec(const char *unit
,
1568 const char *filename
,
1570 const char *section
,
1571 unsigned section_line
,
1581 _cleanup_free_
char *k
= NULL
;
1589 if (isempty(rvalue
)) {
1590 /* Empty assignment clears list */
1595 b
= path_type_from_string(lvalue
);
1597 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1601 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1603 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1607 if (!path_is_absolute(k
)) {
1608 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1612 s
= new0(PathSpec
, 1);
1617 s
->path
= path_kill_slashes(k
);
1622 LIST_PREPEND(spec
, p
->specs
, s
);
1627 int config_parse_socket_service(
1629 const char *filename
,
1631 const char *section
,
1632 unsigned section_line
,
1639 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1640 _cleanup_free_
char *p
= NULL
;
1650 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1652 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", rvalue
);
1656 if (!endswith(p
, ".service")) {
1657 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service: %s", rvalue
);
1661 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1663 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s: %s", rvalue
, bus_error_message(&error
, r
));
1667 unit_ref_set(&s
->service
, x
);
1672 int config_parse_fdname(
1674 const char *filename
,
1676 const char *section
,
1677 unsigned section_line
,
1684 _cleanup_free_
char *p
= NULL
;
1693 if (isempty(rvalue
)) {
1694 s
->fdname
= mfree(s
->fdname
);
1698 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1700 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1704 if (!fdname_is_valid(p
)) {
1705 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1709 return free_and_replace(s
->fdname
, p
);
1712 int config_parse_service_sockets(
1714 const char *filename
,
1716 const char *section
,
1717 unsigned section_line
,
1735 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1737 r
= extract_first_word(&p
, &word
, NULL
, 0);
1743 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1747 r
= unit_name_printf(UNIT(s
), word
, &k
);
1749 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1753 if (!endswith(k
, ".socket")) {
1754 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1758 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1760 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1762 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1764 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1770 int config_parse_bus_name(
1772 const char *filename
,
1774 const char *section
,
1775 unsigned section_line
,
1782 _cleanup_free_
char *k
= NULL
;
1791 r
= unit_full_printf(u
, rvalue
, &k
);
1793 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1797 if (!service_name_is_valid(k
)) {
1798 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1802 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1805 int config_parse_service_timeout(
1807 const char *filename
,
1809 const char *section
,
1810 unsigned section_line
,
1817 Service
*s
= userdata
;
1826 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1828 r
= parse_sec(rvalue
, &usec
);
1830 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1834 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1835 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1836 * all other timeouts. */
1838 usec
= USEC_INFINITY
;
1840 if (!streq(lvalue
, "TimeoutStopSec")) {
1841 s
->start_timeout_defined
= true;
1842 s
->timeout_start_usec
= usec
;
1845 if (!streq(lvalue
, "TimeoutStartSec"))
1846 s
->timeout_stop_usec
= usec
;
1851 int config_parse_sec_fix_0(
1853 const char *filename
,
1855 const char *section
,
1856 unsigned section_line
,
1863 usec_t
*usec
= data
;
1871 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1872 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1875 r
= parse_sec_fix_0(rvalue
, usec
);
1877 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1884 int config_parse_user_group(
1886 const char *filename
,
1888 const char *section
,
1889 unsigned section_line
,
1896 char **user
= data
, *n
;
1905 if (isempty(rvalue
))
1908 _cleanup_free_
char *k
= NULL
;
1910 r
= unit_full_printf(u
, rvalue
, &k
);
1912 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
1916 if (!valid_user_group_name_or_id(k
)) {
1917 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1931 int config_parse_user_group_strv(
1933 const char *filename
,
1935 const char *section
,
1936 unsigned section_line
,
1943 char ***users
= data
;
1953 if (isempty(rvalue
)) {
1956 empty
= new0(char*, 1);
1968 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1970 r
= extract_first_word(&p
, &word
, NULL
, 0);
1976 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax: %s", rvalue
);
1980 r
= unit_full_printf(u
, word
, &k
);
1982 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", word
);
1986 if (!valid_user_group_name_or_id(k
)) {
1987 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1991 r
= strv_push(users
, k
);
2001 int config_parse_working_directory(
2003 const char *filename
,
2005 const char *section
,
2006 unsigned section_line
,
2013 ExecContext
*c
= data
;
2024 if (rvalue
[0] == '-') {
2030 if (streq(rvalue
, "~")) {
2031 c
->working_directory_home
= true;
2032 c
->working_directory
= mfree(c
->working_directory
);
2034 _cleanup_free_
char *k
= NULL
;
2036 r
= unit_full_printf(u
, rvalue
, &k
);
2038 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2039 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2040 rvalue
, missing_ok
? ", ignoring" : "");
2041 return missing_ok
? 0 : -ENOEXEC
;
2044 path_kill_slashes(k
);
2046 if (!utf8_is_valid(k
)) {
2047 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2048 return missing_ok
? 0 : -ENOEXEC
;
2051 if (!path_is_absolute(k
)) {
2052 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2053 "Working directory path '%s' is not absolute%s.",
2054 rvalue
, missing_ok
? ", ignoring" : "");
2055 return missing_ok
? 0 : -ENOEXEC
;
2058 c
->working_directory_home
= false;
2059 free_and_replace(c
->working_directory
, k
);
2062 c
->working_directory_missing_ok
= missing_ok
;
2066 int config_parse_unit_env_file(const char *unit
,
2067 const char *filename
,
2069 const char *section
,
2070 unsigned section_line
,
2079 _cleanup_free_
char *n
= NULL
;
2087 if (isempty(rvalue
)) {
2088 /* Empty assignment frees the list */
2089 *env
= strv_free(*env
);
2093 r
= unit_full_printf(u
, rvalue
, &n
);
2095 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2099 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2100 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2104 r
= strv_extend(env
, n
);
2111 int config_parse_environ(const char *unit
,
2112 const char *filename
,
2114 const char *section
,
2115 unsigned section_line
,
2132 if (isempty(rvalue
)) {
2133 /* Empty assignment resets the list */
2134 *env
= strv_free(*env
);
2138 for (p
= rvalue
;; ) {
2139 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2141 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2147 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2148 "Invalid syntax, ignoring: %s", rvalue
);
2153 r
= unit_full_printf(u
, word
, &k
);
2155 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2156 "Failed to resolve specifiers, ignoring: %s", k
);
2164 if (!env_assignment_is_valid(k
)) {
2165 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2166 "Invalid environment assignment, ignoring: %s", k
);
2170 r
= strv_env_replace(env
, k
);
2177 int config_parse_pass_environ(const char *unit
,
2178 const char *filename
,
2180 const char *section
,
2181 unsigned section_line
,
2188 const char *whole_rvalue
= rvalue
;
2189 char*** passenv
= data
;
2190 _cleanup_strv_free_
char **n
= NULL
;
2191 size_t nlen
= 0, nbufsize
= 0;
2199 if (isempty(rvalue
)) {
2200 /* Empty assignment resets the list */
2201 *passenv
= strv_free(*passenv
);
2206 _cleanup_free_
char *word
= NULL
;
2208 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2214 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2215 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2219 if (!env_name_is_valid(word
)) {
2220 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2221 "Invalid environment name for %s, ignoring: %s", lvalue
, word
);
2225 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2233 r
= strv_extend_strv(passenv
, n
, true);
2241 int config_parse_ip_tos(const char *unit
,
2242 const char *filename
,
2244 const char *section
,
2245 unsigned section_line
,
2252 int *ip_tos
= data
, x
;
2259 x
= ip_tos_from_string(rvalue
);
2261 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2269 int config_parse_unit_condition_path(
2271 const char *filename
,
2273 const char *section
,
2274 unsigned section_line
,
2281 _cleanup_free_
char *p
= NULL
;
2282 Condition
**list
= data
, *c
;
2283 ConditionType t
= ltype
;
2284 bool trigger
, negate
;
2293 if (isempty(rvalue
)) {
2294 /* Empty assignment resets the list */
2295 *list
= condition_free_list(*list
);
2299 trigger
= rvalue
[0] == '|';
2303 negate
= rvalue
[0] == '!';
2307 r
= unit_full_printf(u
, rvalue
, &p
);
2309 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2313 if (!path_is_absolute(p
)) {
2314 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2318 c
= condition_new(t
, p
, trigger
, negate
);
2322 LIST_PREPEND(conditions
, *list
, c
);
2326 int config_parse_unit_condition_string(
2328 const char *filename
,
2330 const char *section
,
2331 unsigned section_line
,
2338 _cleanup_free_
char *s
= NULL
;
2339 Condition
**list
= data
, *c
;
2340 ConditionType t
= ltype
;
2341 bool trigger
, negate
;
2350 if (isempty(rvalue
)) {
2351 /* Empty assignment resets the list */
2352 *list
= condition_free_list(*list
);
2356 trigger
= rvalue
[0] == '|';
2360 negate
= rvalue
[0] == '!';
2364 r
= unit_full_printf(u
, rvalue
, &s
);
2366 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2370 c
= condition_new(t
, s
, trigger
, negate
);
2374 LIST_PREPEND(conditions
, *list
, c
);
2378 int config_parse_unit_condition_null(
2380 const char *filename
,
2382 const char *section
,
2383 unsigned section_line
,
2390 Condition
**list
= data
, *c
;
2391 bool trigger
, negate
;
2399 if (isempty(rvalue
)) {
2400 /* Empty assignment resets the list */
2401 *list
= condition_free_list(*list
);
2405 trigger
= rvalue
[0] == '|';
2409 negate
= rvalue
[0] == '!';
2413 b
= parse_boolean(rvalue
);
2415 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2422 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2426 LIST_PREPEND(conditions
, *list
, c
);
2430 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2431 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2433 int config_parse_unit_requires_mounts_for(
2435 const char *filename
,
2437 const char *section
,
2438 unsigned section_line
,
2454 for (p
= rvalue
;; ) {
2455 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2457 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2463 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2464 "Invalid syntax, ignoring: %s", rvalue
);
2468 if (!utf8_is_valid(word
)) {
2469 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2473 r
= unit_full_printf(u
, word
, &resolved
);
2475 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit name \"%s\", ignoring: %m", word
);
2479 r
= unit_require_mounts_for(u
, resolved
);
2481 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", resolved
);
2487 int config_parse_documentation(const char *unit
,
2488 const char *filename
,
2490 const char *section
,
2491 unsigned section_line
,
2507 if (isempty(rvalue
)) {
2508 /* Empty assignment resets the list */
2509 u
->documentation
= strv_free(u
->documentation
);
2513 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2514 rvalue
, data
, userdata
);
2518 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2520 if (documentation_url_is_valid(*a
))
2523 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2535 static int syscall_filter_parse_one(
2537 const char *filename
,
2546 const SyscallFilterSet
*set
;
2549 set
= syscall_filter_set_find(t
);
2552 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Don't know system call group, ignoring: %s", t
);
2556 NULSTR_FOREACH(i
, set
->value
) {
2557 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, i
, false);
2564 id
= seccomp_syscall_resolve_name(t
);
2565 if (id
== __NR_SCMP_ERROR
) {
2567 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2571 /* If we previously wanted to forbid a syscall and now
2572 * we want to allow it, then remove it from the list
2574 if (!invert
== c
->syscall_whitelist
) {
2575 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2581 (void) set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2587 int config_parse_syscall_filter(
2589 const char *filename
,
2591 const char *section
,
2592 unsigned section_line
,
2599 ExecContext
*c
= data
;
2601 bool invert
= false;
2610 if (isempty(rvalue
)) {
2611 /* Empty assignment resets the list */
2612 c
->syscall_filter
= set_free(c
->syscall_filter
);
2613 c
->syscall_whitelist
= false;
2617 if (rvalue
[0] == '~') {
2622 if (!c
->syscall_filter
) {
2623 c
->syscall_filter
= set_new(NULL
);
2624 if (!c
->syscall_filter
)
2628 /* Allow everything but the ones listed */
2629 c
->syscall_whitelist
= false;
2631 /* Allow nothing but the ones listed */
2632 c
->syscall_whitelist
= true;
2634 /* Accept default syscalls if we are on a whitelist */
2635 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, false, "@default", false);
2643 _cleanup_free_
char *word
= NULL
;
2645 r
= extract_first_word(&p
, &word
, NULL
, 0);
2651 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2655 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, word
, true);
2663 int config_parse_syscall_archs(
2665 const char *filename
,
2667 const char *section
,
2668 unsigned section_line
,
2679 if (isempty(rvalue
)) {
2680 *archs
= set_free(*archs
);
2684 r
= set_ensure_allocated(archs
, NULL
);
2688 for (p
= rvalue
;;) {
2689 _cleanup_free_
char *word
= NULL
;
2692 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2698 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2699 "Invalid syntax, ignoring: %s", rvalue
);
2703 r
= seccomp_arch_from_string(word
, &a
);
2705 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2706 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
2710 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2716 int config_parse_syscall_errno(
2718 const char *filename
,
2720 const char *section
,
2721 unsigned section_line
,
2728 ExecContext
*c
= data
;
2735 if (isempty(rvalue
)) {
2736 /* Empty assignment resets to KILL */
2737 c
->syscall_errno
= 0;
2741 e
= errno_from_name(rvalue
);
2743 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2747 c
->syscall_errno
= e
;
2751 int config_parse_address_families(
2753 const char *filename
,
2755 const char *section
,
2756 unsigned section_line
,
2763 ExecContext
*c
= data
;
2764 bool invert
= false;
2772 if (isempty(rvalue
)) {
2773 /* Empty assignment resets the list */
2774 c
->address_families
= set_free(c
->address_families
);
2775 c
->address_families_whitelist
= false;
2779 if (rvalue
[0] == '~') {
2784 if (!c
->address_families
) {
2785 c
->address_families
= set_new(NULL
);
2786 if (!c
->address_families
)
2789 c
->address_families_whitelist
= !invert
;
2792 for (p
= rvalue
;;) {
2793 _cleanup_free_
char *word
= NULL
;
2796 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2802 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2803 "Invalid syntax, ignoring: %s", rvalue
);
2807 af
= af_from_name(word
);
2809 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2810 "Failed to parse address family \"%s\", ignoring: %m", word
);
2814 /* If we previously wanted to forbid an address family and now
2815 * we want to allow it, then just remove it from the list.
2817 if (!invert
== c
->address_families_whitelist
) {
2818 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2822 set_remove(c
->address_families
, INT_TO_PTR(af
));
2826 int config_parse_restrict_namespaces(
2828 const char *filename
,
2830 const char *section
,
2831 unsigned section_line
,
2838 ExecContext
*c
= data
;
2839 bool invert
= false;
2842 if (isempty(rvalue
)) {
2843 /* Reset to the default. */
2844 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2848 if (rvalue
[0] == '~') {
2853 r
= parse_boolean(rvalue
);
2855 c
->restrict_namespaces
= 0;
2857 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2859 /* Not a boolean argument, in this case it's a list of namespace types. */
2861 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
2863 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
2869 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
2875 int config_parse_unit_slice(
2877 const char *filename
,
2879 const char *section
,
2880 unsigned section_line
,
2887 _cleanup_free_
char *k
= NULL
;
2888 Unit
*u
= userdata
, *slice
= NULL
;
2896 r
= unit_name_printf(u
, rvalue
, &k
);
2898 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2902 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2904 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2908 r
= unit_set_slice(u
, slice
);
2910 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2917 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2919 int config_parse_cpu_weight(
2921 const char *filename
,
2923 const char *section
,
2924 unsigned section_line
,
2931 uint64_t *weight
= data
;
2938 r
= cg_weight_parse(rvalue
, weight
);
2940 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
2947 int config_parse_cpu_shares(
2949 const char *filename
,
2951 const char *section
,
2952 unsigned section_line
,
2959 uint64_t *shares
= data
;
2966 r
= cg_cpu_shares_parse(rvalue
, shares
);
2968 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2975 int config_parse_cpu_quota(
2977 const char *filename
,
2979 const char *section
,
2980 unsigned section_line
,
2987 CGroupContext
*c
= data
;
2994 if (isempty(rvalue
)) {
2995 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2999 r
= parse_percent_unbounded(rvalue
);
3001 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3005 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3009 int config_parse_memory_limit(
3011 const char *filename
,
3013 const char *section
,
3014 unsigned section_line
,
3021 CGroupContext
*c
= data
;
3022 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3025 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3027 r
= parse_percent(rvalue
);
3029 r
= parse_size(rvalue
, 1024, &bytes
);
3031 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3035 bytes
= physical_memory_scale(r
, 100U);
3037 if (bytes
<= 0 || bytes
>= UINT64_MAX
) {
3038 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3043 if (streq(lvalue
, "MemoryLow"))
3044 c
->memory_low
= bytes
;
3045 else if (streq(lvalue
, "MemoryHigh"))
3046 c
->memory_high
= bytes
;
3047 else if (streq(lvalue
, "MemoryMax"))
3048 c
->memory_max
= bytes
;
3049 else if (streq(lvalue
, "MemorySwapMax"))
3050 c
->memory_swap_max
= bytes
;
3051 else if (streq(lvalue
, "MemoryLimit"))
3052 c
->memory_limit
= bytes
;
3059 int config_parse_tasks_max(
3061 const char *filename
,
3063 const char *section
,
3064 unsigned section_line
,
3071 uint64_t *tasks_max
= data
, v
;
3075 if (isempty(rvalue
)) {
3076 *tasks_max
= u
->manager
->default_tasks_max
;
3080 if (streq(rvalue
, "infinity")) {
3081 *tasks_max
= CGROUP_LIMIT_MAX
;
3085 r
= parse_percent(rvalue
);
3087 r
= safe_atou64(rvalue
, &v
);
3089 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3093 v
= system_tasks_max_scale(r
, 100U);
3095 if (v
<= 0 || v
>= UINT64_MAX
) {
3096 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3104 int config_parse_device_allow(
3106 const char *filename
,
3108 const char *section
,
3109 unsigned section_line
,
3116 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3117 CGroupContext
*c
= data
;
3118 CGroupDeviceAllow
*a
;
3119 const char *m
= NULL
;
3123 if (isempty(rvalue
)) {
3124 while (c
->device_allow
)
3125 cgroup_context_free_device_allow(c
, c
->device_allow
);
3130 r
= unit_full_printf(userdata
, rvalue
, &t
);
3132 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3133 "Failed to resolve specifiers in %s, ignoring: %m",
3137 n
= strcspn(t
, WHITESPACE
);
3139 path
= strndup(t
, n
);
3143 if (!is_deviceallow_pattern(path
)) {
3144 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3148 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3152 if (!in_charset(m
, "rwm")) {
3153 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3157 a
= new0(CGroupDeviceAllow
, 1);
3163 a
->r
= !!strchr(m
, 'r');
3164 a
->w
= !!strchr(m
, 'w');
3165 a
->m
= !!strchr(m
, 'm');
3167 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3171 int config_parse_io_weight(
3173 const char *filename
,
3175 const char *section
,
3176 unsigned section_line
,
3183 uint64_t *weight
= data
;
3190 r
= cg_weight_parse(rvalue
, weight
);
3192 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3199 int config_parse_io_device_weight(
3201 const char *filename
,
3203 const char *section
,
3204 unsigned section_line
,
3211 _cleanup_free_
char *path
= NULL
;
3212 CGroupIODeviceWeight
*w
;
3213 CGroupContext
*c
= data
;
3223 if (isempty(rvalue
)) {
3224 while (c
->io_device_weights
)
3225 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3230 n
= strcspn(rvalue
, WHITESPACE
);
3231 weight
= rvalue
+ n
;
3232 weight
+= strspn(weight
, WHITESPACE
);
3234 if (isempty(weight
)) {
3235 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3239 path
= strndup(rvalue
, n
);
3243 if (!path_startswith(path
, "/dev")) {
3244 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3248 r
= cg_weight_parse(weight
, &u
);
3250 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3254 assert(u
!= CGROUP_WEIGHT_INVALID
);
3256 w
= new0(CGroupIODeviceWeight
, 1);
3265 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3269 int config_parse_io_limit(
3271 const char *filename
,
3273 const char *section
,
3274 unsigned section_line
,
3281 _cleanup_free_
char *path
= NULL
;
3282 CGroupIODeviceLimit
*l
= NULL
, *t
;
3283 CGroupContext
*c
= data
;
3284 CGroupIOLimitType type
;
3294 type
= cgroup_io_limit_type_from_string(lvalue
);
3297 if (isempty(rvalue
)) {
3298 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3299 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3303 n
= strcspn(rvalue
, WHITESPACE
);
3305 limit
+= strspn(limit
, WHITESPACE
);
3308 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3312 path
= strndup(rvalue
, n
);
3316 if (!path_startswith(path
, "/dev")) {
3317 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3321 if (streq("infinity", limit
)) {
3322 num
= CGROUP_LIMIT_MAX
;
3324 r
= parse_size(limit
, 1000, &num
);
3325 if (r
< 0 || num
<= 0) {
3326 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3331 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3332 if (path_equal(path
, t
->path
)) {
3339 CGroupIOLimitType ttype
;
3341 l
= new0(CGroupIODeviceLimit
, 1);
3347 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3348 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3350 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3353 l
->limits
[type
] = num
;
3358 int config_parse_blockio_weight(
3360 const char *filename
,
3362 const char *section
,
3363 unsigned section_line
,
3370 uint64_t *weight
= data
;
3377 r
= cg_blkio_weight_parse(rvalue
, weight
);
3379 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3386 int config_parse_blockio_device_weight(
3388 const char *filename
,
3390 const char *section
,
3391 unsigned section_line
,
3398 _cleanup_free_
char *path
= NULL
;
3399 CGroupBlockIODeviceWeight
*w
;
3400 CGroupContext
*c
= data
;
3410 if (isempty(rvalue
)) {
3411 while (c
->blockio_device_weights
)
3412 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3417 n
= strcspn(rvalue
, WHITESPACE
);
3418 weight
= rvalue
+ n
;
3419 weight
+= strspn(weight
, WHITESPACE
);
3421 if (isempty(weight
)) {
3422 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3426 path
= strndup(rvalue
, n
);
3430 if (!path_startswith(path
, "/dev")) {
3431 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3435 r
= cg_blkio_weight_parse(weight
, &u
);
3437 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3441 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3443 w
= new0(CGroupBlockIODeviceWeight
, 1);
3452 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3456 int config_parse_blockio_bandwidth(
3458 const char *filename
,
3460 const char *section
,
3461 unsigned section_line
,
3468 _cleanup_free_
char *path
= NULL
;
3469 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3470 CGroupContext
*c
= data
;
3471 const char *bandwidth
;
3481 read
= streq("BlockIOReadBandwidth", lvalue
);
3483 if (isempty(rvalue
)) {
3484 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3485 b
->rbps
= CGROUP_LIMIT_MAX
;
3486 b
->wbps
= CGROUP_LIMIT_MAX
;
3491 n
= strcspn(rvalue
, WHITESPACE
);
3492 bandwidth
= rvalue
+ n
;
3493 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3496 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3500 path
= strndup(rvalue
, n
);
3504 if (!path_startswith(path
, "/dev")) {
3505 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3509 r
= parse_size(bandwidth
, 1000, &bytes
);
3510 if (r
< 0 || bytes
<= 0) {
3511 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3515 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3516 if (path_equal(path
, t
->path
)) {
3523 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3529 b
->rbps
= CGROUP_LIMIT_MAX
;
3530 b
->wbps
= CGROUP_LIMIT_MAX
;
3532 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3543 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3545 int config_parse_job_mode_isolate(
3547 const char *filename
,
3549 const char *section
,
3550 unsigned section_line
,
3564 r
= parse_boolean(rvalue
);
3566 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3570 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3574 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode
, exec_preserve_mode
, ExecPreserveMode
, "Failed to parse runtime directory preserve mode");
3576 int config_parse_exec_directories(
3578 const char *filename
,
3580 const char *section
,
3581 unsigned section_line
,
3598 if (isempty(rvalue
)) {
3599 /* Empty assignment resets the list */
3600 *rt
= strv_free(*rt
);
3604 for (p
= rvalue
;;) {
3605 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3607 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3613 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3614 "Invalid syntax, ignoring: %s", rvalue
);
3618 r
= unit_full_printf(u
, word
, &k
);
3620 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3621 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
3625 if (!path_is_safe(k
) || path_is_absolute(k
)) {
3626 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3627 "%s is not valid, ignoring assignment: %s", lvalue
, rvalue
);
3631 r
= strv_push(rt
, k
);
3638 int config_parse_set_status(
3640 const char *filename
,
3642 const char *section
,
3643 unsigned section_line
,
3651 const char *word
, *state
;
3653 ExitStatusSet
*status_set
= data
;
3660 /* Empty assignment resets the list */
3661 if (isempty(rvalue
)) {
3662 exit_status_set_free(status_set
);
3666 FOREACH_WORD(word
, l
, rvalue
, state
) {
3667 _cleanup_free_
char *temp
;
3671 temp
= strndup(word
, l
);
3675 r
= safe_atoi(temp
, &val
);
3677 val
= signal_from_string_try_harder(temp
);
3680 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3683 set
= &status_set
->signal
;
3685 if (val
< 0 || val
> 255) {
3686 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3689 set
= &status_set
->status
;
3692 r
= set_ensure_allocated(set
, NULL
);
3696 r
= set_put(*set
, INT_TO_PTR(val
));
3698 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3702 if (!isempty(state
))
3703 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3708 int config_parse_namespace_path_strv(
3710 const char *filename
,
3712 const char *section
,
3713 unsigned section_line
,
3730 if (isempty(rvalue
)) {
3731 /* Empty assignment resets the list */
3732 *sv
= strv_free(*sv
);
3738 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
3740 bool ignore_enoent
= false, shall_prefix
= false;
3742 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3748 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
3752 if (!utf8_is_valid(word
)) {
3753 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3758 if (startswith(w
, "-")) {
3759 ignore_enoent
= true;
3762 if (startswith(w
, "+")) {
3763 shall_prefix
= true;
3767 r
= unit_full_printf(u
, w
, &resolved
);
3769 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s: %m", word
);
3773 if (!path_is_absolute(resolved
)) {
3774 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
3778 path_kill_slashes(resolved
);
3780 joined
= strjoin(ignore_enoent
? "-" : "",
3781 shall_prefix
? "+" : "",
3784 r
= strv_push(sv
, joined
);
3794 int config_parse_bind_paths(
3796 const char *filename
,
3798 const char *section
,
3799 unsigned section_line
,
3806 ExecContext
*c
= data
;
3816 if (isempty(rvalue
)) {
3817 /* Empty assignment resets the list */
3818 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
3819 c
->bind_mounts
= NULL
;
3820 c
->n_bind_mounts
= 0;
3826 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
3827 _cleanup_free_
char *sresolved
= NULL
, *dresolved
= NULL
;
3828 char *s
= NULL
, *d
= NULL
;
3829 bool rbind
= true, ignore_enoent
= false;
3831 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
3837 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3841 r
= unit_full_printf(u
, source
, &sresolved
);
3843 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3844 "Failed to resolved specifiers in \"%s\", ignoring: %m", source
);
3850 ignore_enoent
= true;
3854 if (!utf8_is_valid(s
)) {
3855 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, s
);
3858 if (!path_is_absolute(s
)) {
3859 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute source path, ignoring: %s", s
);
3863 path_kill_slashes(s
);
3865 /* Optionally, the destination is specified. */
3866 if (p
&& p
[-1] == ':') {
3867 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
3871 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3875 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':': %s", rvalue
);
3879 r
= unit_full_printf(u
, destination
, &dresolved
);
3881 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3882 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination
);
3886 if (!utf8_is_valid(dresolved
)) {
3887 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, dresolved
);
3890 if (!path_is_absolute(dresolved
)) {
3891 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute destination path, ignoring: %s", dresolved
);
3895 d
= path_kill_slashes(dresolved
);
3897 /* Optionally, there's also a short option string specified */
3898 if (p
&& p
[-1] == ':') {
3899 _cleanup_free_
char *options
= NULL
;
3901 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
3905 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3909 if (isempty(options
) || streq(options
, "rbind"))
3911 else if (streq(options
, "norbind"))
3914 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
3921 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
3925 .read_only
= !!strstr(lvalue
, "ReadOnly"),
3927 .ignore_enoent
= ignore_enoent
,
3936 int config_parse_no_new_privileges(
3938 const char *filename
,
3940 const char *section
,
3941 unsigned section_line
,
3948 ExecContext
*c
= data
;
3956 k
= parse_boolean(rvalue
);
3958 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3962 c
->no_new_privileges
= k
;
3967 int config_parse_protect_home(
3969 const char *filename
,
3971 const char *section
,
3972 unsigned section_line
,
3979 ExecContext
*c
= data
;
3987 /* Our enum shall be a superset of booleans, hence first try
3988 * to parse as boolean, and then as enum */
3990 k
= parse_boolean(rvalue
);
3992 c
->protect_home
= PROTECT_HOME_YES
;
3994 c
->protect_home
= PROTECT_HOME_NO
;
3998 h
= protect_home_from_string(rvalue
);
4000 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
4004 c
->protect_home
= h
;
4010 int config_parse_protect_system(
4012 const char *filename
,
4014 const char *section
,
4015 unsigned section_line
,
4022 ExecContext
*c
= data
;
4030 /* Our enum shall be a superset of booleans, hence first try
4031 * to parse as boolean, and then as enum */
4033 k
= parse_boolean(rvalue
);
4035 c
->protect_system
= PROTECT_SYSTEM_YES
;
4037 c
->protect_system
= PROTECT_SYSTEM_NO
;
4041 s
= protect_system_from_string(rvalue
);
4043 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
4047 c
->protect_system
= s
;
4053 #define FOLLOW_MAX 8
4055 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4066 /* This will update the filename pointer if the loaded file is
4067 * reached by a symlink. The old string will be freed. */
4070 char *target
, *name
;
4072 if (c
++ >= FOLLOW_MAX
)
4075 path_kill_slashes(*filename
);
4077 /* Add the file name we are currently looking at to
4078 * the names of this unit, but only if it is a valid
4080 name
= basename(*filename
);
4081 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4083 id
= set_get(names
, name
);
4089 r
= set_consume(names
, id
);
4095 /* Try to open the file name, but don't if its a symlink */
4096 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4103 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4104 r
= readlink_and_make_absolute(*filename
, &target
);
4112 f
= fdopen(fd
, "re");
4124 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4132 /* Let's try to add in all symlink names we found */
4133 while ((k
= set_steal_first(names
))) {
4135 /* First try to merge in the other name into our
4137 r
= unit_merge_by_name(*u
, k
);
4141 /* Hmm, we couldn't merge the other unit into
4142 * ours? Then let's try it the other way
4145 /* If the symlink name we are looking at is unit template, then
4146 we must search for instance of this template */
4147 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4148 _cleanup_free_
char *instance
= NULL
;
4150 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4154 other
= manager_get_unit((*u
)->manager
, instance
);
4156 other
= manager_get_unit((*u
)->manager
, k
);
4161 r
= unit_merge(other
, *u
);
4164 return merge_by_names(u
, names
, NULL
);
4172 unit_choose_id(*u
, id
);
4180 static int load_from_path(Unit
*u
, const char *path
) {
4181 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4182 _cleanup_fclose_
FILE *f
= NULL
;
4183 _cleanup_free_
char *filename
= NULL
;
4192 symlink_names
= set_new(&string_hash_ops
);
4196 if (path_is_absolute(path
)) {
4198 filename
= strdup(path
);
4202 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4204 filename
= mfree(filename
);
4212 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4214 /* Instead of opening the path right away, we manually
4215 * follow all symlinks and add their name to our unit
4216 * name set while doing so */
4217 filename
= path_make_absolute(path
, *p
);
4221 if (u
->manager
->unit_path_cache
&&
4222 !set_get(u
->manager
->unit_path_cache
, filename
))
4225 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4228 filename
= mfree(filename
);
4230 /* ENOENT means that the file is missing or is a dangling symlink.
4231 * ENOTDIR means that one of paths we expect to be is a directory
4232 * is not a directory, we should just ignore that.
4233 * EACCES means that the directory or file permissions are wrong.
4236 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4237 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4240 /* Empty the symlink names for the next run */
4241 set_clear_free(symlink_names
);
4246 /* Hmm, no suitable file found? */
4249 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4250 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4255 r
= merge_by_names(&merged
, symlink_names
, id
);
4260 u
->load_state
= UNIT_MERGED
;
4264 if (fstat(fileno(f
), &st
) < 0)
4267 if (null_or_empty(&st
)) {
4268 u
->load_state
= UNIT_MASKED
;
4269 u
->fragment_mtime
= 0;
4271 u
->load_state
= UNIT_LOADED
;
4272 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4274 /* Now, parse the file contents */
4275 r
= config_parse(u
->id
, filename
, f
,
4276 UNIT_VTABLE(u
)->sections
,
4277 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4278 false, true, false, u
);
4283 free(u
->fragment_path
);
4284 u
->fragment_path
= filename
;
4287 if (u
->source_path
) {
4288 if (stat(u
->source_path
, &st
) >= 0)
4289 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4291 u
->source_mtime
= 0;
4297 int unit_load_fragment(Unit
*u
) {
4303 assert(u
->load_state
== UNIT_STUB
);
4307 u
->load_state
= UNIT_LOADED
;
4311 /* First, try to find the unit under its id. We always look
4312 * for unit files in the default directories, to make it easy
4313 * to override things by placing things in /etc/systemd/system */
4314 r
= load_from_path(u
, u
->id
);
4318 /* Try to find an alias we can load this with */
4319 if (u
->load_state
== UNIT_STUB
) {
4320 SET_FOREACH(t
, u
->names
, i
) {
4325 r
= load_from_path(u
, t
);
4329 if (u
->load_state
!= UNIT_STUB
)
4334 /* And now, try looking for it under the suggested (originally linked) path */
4335 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4337 r
= load_from_path(u
, u
->fragment_path
);
4341 if (u
->load_state
== UNIT_STUB
)
4342 /* Hmm, this didn't work? Then let's get rid
4343 * of the fragment path stored for us, so that
4344 * we don't point to an invalid location. */
4345 u
->fragment_path
= mfree(u
->fragment_path
);
4348 /* Look for a template */
4349 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4350 _cleanup_free_
char *k
= NULL
;
4352 r
= unit_name_template(u
->id
, &k
);
4356 r
= load_from_path(u
, k
);
4359 log_unit_notice(u
, "Unit configuration has fatal error, unit will not be started.");
4363 if (u
->load_state
== UNIT_STUB
) {
4364 SET_FOREACH(t
, u
->names
, i
) {
4365 _cleanup_free_
char *z
= NULL
;
4370 r
= unit_name_template(t
, &z
);
4374 r
= load_from_path(u
, z
);
4378 if (u
->load_state
!= UNIT_STUB
)
4387 void unit_dump_config_items(FILE *f
) {
4388 static const struct {
4389 const ConfigParserCallback callback
;
4392 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
4393 { config_parse_warn_compat
, "NOTSUPPORTED" },
4395 { config_parse_int
, "INTEGER" },
4396 { config_parse_unsigned
, "UNSIGNED" },
4397 { config_parse_iec_size
, "SIZE" },
4398 { config_parse_iec_uint64
, "SIZE" },
4399 { config_parse_si_size
, "SIZE" },
4400 { config_parse_bool
, "BOOLEAN" },
4401 { config_parse_string
, "STRING" },
4402 { config_parse_path
, "PATH" },
4403 { config_parse_unit_path_printf
, "PATH" },
4404 { config_parse_strv
, "STRING [...]" },
4405 { config_parse_exec_nice
, "NICE" },
4406 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4407 { config_parse_exec_io_class
, "IOCLASS" },
4408 { config_parse_exec_io_priority
, "IOPRIORITY" },
4409 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4410 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4411 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4412 { config_parse_mode
, "MODE" },
4413 { config_parse_unit_env_file
, "FILE" },
4414 { config_parse_exec_output
, "OUTPUT" },
4415 { config_parse_exec_input
, "INPUT" },
4416 { config_parse_log_facility
, "FACILITY" },
4417 { config_parse_log_level
, "LEVEL" },
4418 { config_parse_exec_secure_bits
, "SECUREBITS" },
4419 { config_parse_capability_set
, "BOUNDINGSET" },
4420 { config_parse_limit
, "LIMIT" },
4421 { config_parse_unit_deps
, "UNIT [...]" },
4422 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4423 { config_parse_service_type
, "SERVICETYPE" },
4424 { config_parse_service_restart
, "SERVICERESTART" },
4425 #ifdef HAVE_SYSV_COMPAT
4426 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4428 { config_parse_kill_mode
, "KILLMODE" },
4429 { config_parse_signal
, "SIGNAL" },
4430 { config_parse_socket_listen
, "SOCKET [...]" },
4431 { config_parse_socket_bind
, "SOCKETBIND" },
4432 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4433 { config_parse_sec
, "SECONDS" },
4434 { config_parse_nsec
, "NANOSECONDS" },
4435 { config_parse_namespace_path_strv
, "PATH [...]" },
4436 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4437 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4438 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4439 { config_parse_unit_string_printf
, "STRING" },
4440 { config_parse_trigger_unit
, "UNIT" },
4441 { config_parse_timer
, "TIMER" },
4442 { config_parse_path_spec
, "PATH" },
4443 { config_parse_notify_access
, "ACCESS" },
4444 { config_parse_ip_tos
, "TOS" },
4445 { config_parse_unit_condition_path
, "CONDITION" },
4446 { config_parse_unit_condition_string
, "CONDITION" },
4447 { config_parse_unit_condition_null
, "CONDITION" },
4448 { config_parse_unit_slice
, "SLICE" },
4449 { config_parse_documentation
, "URL" },
4450 { config_parse_service_timeout
, "SECONDS" },
4451 { config_parse_emergency_action
, "ACTION" },
4452 { config_parse_set_status
, "STATUS" },
4453 { config_parse_service_sockets
, "SOCKETS" },
4454 { config_parse_environ
, "ENVIRON" },
4456 { config_parse_syscall_filter
, "SYSCALLS" },
4457 { config_parse_syscall_archs
, "ARCHS" },
4458 { config_parse_syscall_errno
, "ERRNO" },
4459 { config_parse_address_families
, "FAMILIES" },
4460 { config_parse_restrict_namespaces
, "NAMESPACES" },
4462 { config_parse_cpu_shares
, "SHARES" },
4463 { config_parse_cpu_weight
, "WEIGHT" },
4464 { config_parse_memory_limit
, "LIMIT" },
4465 { config_parse_device_allow
, "DEVICE" },
4466 { config_parse_device_policy
, "POLICY" },
4467 { config_parse_io_limit
, "LIMIT" },
4468 { config_parse_io_weight
, "WEIGHT" },
4469 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4470 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4471 { config_parse_blockio_weight
, "WEIGHT" },
4472 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4473 { config_parse_long
, "LONG" },
4474 { config_parse_socket_service
, "SERVICE" },
4476 { config_parse_exec_selinux_context
, "LABEL" },
4478 { config_parse_job_mode
, "MODE" },
4479 { config_parse_job_mode_isolate
, "BOOLEAN" },
4480 { config_parse_personality
, "PERSONALITY" },
4483 const char *prev
= NULL
;
4488 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4489 const char *rvalue
= "OTHER", *lvalue
;
4493 const ConfigPerfItem
*p
;
4495 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4497 dot
= strchr(i
, '.');
4498 lvalue
= dot
? dot
+ 1 : i
;
4502 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
4506 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
4509 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
4510 if (p
->parse
== table
[j
].callback
) {
4511 rvalue
= table
[j
].rvalue
;
4515 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);