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
,
286 if (isempty(rvalue
)) {
289 /* Empty assignment resets the list. As a special rule
290 * we actually fill in a real empty array here rather
291 * than NULL, since some code wants to know if
292 * something was set at all... */
293 empty
= new0(char*, 1);
304 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
306 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
312 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
313 "Invalid syntax, ignoring: %s", rvalue
);
317 r
= unit_full_printf(u
, word
, &k
);
319 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
320 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
324 if (!utf8_is_valid(k
)) {
325 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
329 if (!path_is_absolute(k
)) {
330 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
331 "Symlink path is not absolute: %s", k
);
335 path_kill_slashes(k
);
344 int config_parse_socket_listen(const char *unit
,
345 const char *filename
,
348 unsigned section_line
,
355 _cleanup_free_ SocketPort
*p
= NULL
;
367 if (isempty(rvalue
)) {
368 /* An empty assignment removes all ports */
369 socket_free_ports(s
);
373 p
= new0(SocketPort
, 1);
377 if (ltype
!= SOCKET_SOCKET
) {
380 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
382 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
386 path_kill_slashes(p
->path
);
388 } else if (streq(lvalue
, "ListenNetlink")) {
389 _cleanup_free_
char *k
= NULL
;
391 p
->type
= SOCKET_SOCKET
;
392 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
394 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
398 r
= socket_address_parse_netlink(&p
->address
, k
);
400 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
405 _cleanup_free_
char *k
= NULL
;
407 p
->type
= SOCKET_SOCKET
;
408 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
410 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
414 r
= socket_address_parse_and_warn(&p
->address
, k
);
416 if (r
!= -EAFNOSUPPORT
)
417 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
422 if (streq(lvalue
, "ListenStream"))
423 p
->address
.type
= SOCK_STREAM
;
424 else if (streq(lvalue
, "ListenDatagram"))
425 p
->address
.type
= SOCK_DGRAM
;
427 assert(streq(lvalue
, "ListenSequentialPacket"));
428 p
->address
.type
= SOCK_SEQPACKET
;
431 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
432 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
438 p
->auxiliary_fds
= NULL
;
439 p
->n_auxiliary_fds
= 0;
443 LIST_FIND_TAIL(port
, s
->ports
, tail
);
444 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
446 LIST_PREPEND(port
, s
->ports
, p
);
452 int config_parse_socket_protocol(const char *unit
,
453 const char *filename
,
456 unsigned section_line
,
471 if (streq(rvalue
, "udplite"))
472 s
->socket_protocol
= IPPROTO_UDPLITE
;
473 else if (streq(rvalue
, "sctp"))
474 s
->socket_protocol
= IPPROTO_SCTP
;
476 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
483 int config_parse_socket_bind(const char *unit
,
484 const char *filename
,
487 unsigned section_line
,
495 SocketAddressBindIPv6Only b
;
504 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
508 r
= parse_boolean(rvalue
);
510 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
514 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
516 s
->bind_ipv6_only
= b
;
521 int config_parse_exec_nice(
523 const char *filename
,
526 unsigned section_line
,
533 ExecContext
*c
= data
;
541 r
= parse_nice(rvalue
, &priority
);
544 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
546 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
557 int config_parse_exec_oom_score_adjust(const char* unit
,
558 const char *filename
,
561 unsigned section_line
,
568 ExecContext
*c
= data
;
576 r
= safe_atoi(rvalue
, &oa
);
578 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
582 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
583 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
587 c
->oom_score_adjust
= oa
;
588 c
->oom_score_adjust_set
= true;
593 int config_parse_exec(
595 const char *filename
,
598 unsigned section_line
,
605 ExecCommand
**e
= data
;
617 rvalue
+= strspn(rvalue
, WHITESPACE
);
619 if (isempty(rvalue
)) {
620 /* An empty assignment resets the list */
621 *e
= exec_command_free_list(*e
);
627 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
628 ExecCommandFlags flags
= 0;
629 bool ignore
= false, separate_argv0
= false;
630 _cleanup_free_ ExecCommand
*nce
= NULL
;
631 _cleanup_strv_free_
char **n
= NULL
;
632 size_t nlen
= 0, nbufsize
= 0;
637 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
643 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
644 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
645 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
646 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
647 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
648 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
649 * other sandboxing, with some special exceptions for changing UID.
651 * The idea is that '!!' may be used to write services that can take benefit of systemd's
652 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
653 * privilege dropping within the daemon if the kernel does not offer that. */
655 if (*f
== '-' && !(flags
& EXEC_COMMAND_IGNORE_FAILURE
)) {
656 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
658 } else if (*f
== '@' && !separate_argv0
)
659 separate_argv0
= true;
660 else if (*f
== '+' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
661 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
662 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
663 flags
|= EXEC_COMMAND_NO_SETUID
;
664 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
665 flags
&= ~EXEC_COMMAND_NO_SETUID
;
666 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
672 r
= unit_full_printf(u
, f
, &path
);
674 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
675 "Failed to resolve unit specifiers on %s%s: %m",
676 f
, ignore
? ", ignoring" : "");
677 return ignore
? 0 : -ENOEXEC
;
681 /* First word is either "-" or "@" with no command. */
682 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
683 "Empty path in command line%s: \"%s\"",
684 ignore
? ", ignoring" : "", rvalue
);
685 return ignore
? 0 : -ENOEXEC
;
687 if (!string_is_safe(path
)) {
688 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
689 "Executable path contains special characters%s: %s",
690 ignore
? ", ignoring" : "", rvalue
);
691 return ignore
? 0 : -ENOEXEC
;
693 if (!path_is_absolute(path
)) {
694 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
695 "Executable path is not absolute%s: %s",
696 ignore
? ", ignoring" : "", rvalue
);
697 return ignore
? 0 : -ENOEXEC
;
699 if (endswith(path
, "/")) {
700 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
701 "Executable path specifies a directory%s: %s",
702 ignore
? ", ignoring" : "", rvalue
);
703 return ignore
? 0 : -ENOEXEC
;
706 if (!separate_argv0
) {
709 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
719 path_kill_slashes(path
);
721 while (!isempty(p
)) {
722 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
724 /* Check explicitly for an unquoted semicolon as
725 * command separator token. */
726 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
728 p
+= strspn(p
, WHITESPACE
);
733 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
734 * extract_first_word() would return the same for all of those. */
735 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
739 p
+= strspn(p
, WHITESPACE
);
741 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
752 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
756 return ignore
? 0 : -ENOEXEC
;
758 r
= unit_full_printf(u
, word
, &resolved
);
760 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
761 "Failed to resolve unit specifiers on %s%s: %m",
762 word
, ignore
? ", ignoring" : "");
763 return ignore
? 0 : -ENOEXEC
;
766 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
768 n
[nlen
++] = resolved
;
774 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
775 "Empty executable name or zeroeth argument%s: %s",
776 ignore
? ", ignoring" : "", rvalue
);
777 return ignore
? 0 : -ENOEXEC
;
780 nce
= new0(ExecCommand
, 1);
788 exec_command_append_list(e
, nce
);
790 /* Do not _cleanup_free_ these. */
801 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
802 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
804 int config_parse_socket_bindtodevice(
806 const char *filename
,
809 unsigned section_line
,
824 if (rvalue
[0] && !streq(rvalue
, "*")) {
825 if (!ifname_valid(rvalue
)) {
826 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
836 free(s
->bind_to_device
);
837 s
->bind_to_device
= n
;
842 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input literal specifier");
843 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output literal specifier");
845 int config_parse_exec_input(const char *unit
,
846 const char *filename
,
849 unsigned section_line
,
855 ExecContext
*c
= data
;
864 name
= startswith(rvalue
, "fd:");
866 /* Strip prefix and validate fd name */
867 if (!fdname_is_valid(name
)) {
868 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
871 c
->std_input
= EXEC_INPUT_NAMED_FD
;
872 r
= free_and_strdup(&c
->stdio_fdname
[STDIN_FILENO
], name
);
877 ExecInput ei
= exec_input_from_string(rvalue
);
878 if (ei
== _EXEC_INPUT_INVALID
)
879 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
886 int config_parse_exec_output(const char *unit
,
887 const char *filename
,
890 unsigned section_line
,
896 ExecContext
*c
= data
;
907 name
= startswith(rvalue
, "fd:");
909 /* Strip prefix and validate fd name */
910 if (!fdname_is_valid(name
)) {
911 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
914 eo
= EXEC_OUTPUT_NAMED_FD
;
916 eo
= exec_output_from_string(rvalue
);
917 if (eo
== _EXEC_OUTPUT_INVALID
) {
918 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
923 if (streq(lvalue
, "StandardOutput")) {
925 r
= free_and_strdup(&c
->stdio_fdname
[STDOUT_FILENO
], name
);
929 } else if (streq(lvalue
, "StandardError")) {
931 r
= free_and_strdup(&c
->stdio_fdname
[STDERR_FILENO
], name
);
936 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output property, ignoring: %s", lvalue
);
941 int config_parse_exec_io_class(const char *unit
,
942 const char *filename
,
945 unsigned section_line
,
952 ExecContext
*c
= data
;
960 x
= ioprio_class_from_string(rvalue
);
962 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
966 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
967 c
->ioprio_set
= true;
972 int config_parse_exec_io_priority(const char *unit
,
973 const char *filename
,
976 unsigned section_line
,
983 ExecContext
*c
= data
;
991 r
= ioprio_parse_priority(rvalue
, &i
);
993 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
997 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
998 c
->ioprio_set
= true;
1003 int config_parse_exec_cpu_sched_policy(const char *unit
,
1004 const char *filename
,
1006 const char *section
,
1007 unsigned section_line
,
1015 ExecContext
*c
= data
;
1023 x
= sched_policy_from_string(rvalue
);
1025 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1029 c
->cpu_sched_policy
= x
;
1030 /* Moving to or from real-time policy? We need to adjust the priority */
1031 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
1032 c
->cpu_sched_set
= true;
1037 int config_parse_exec_cpu_sched_prio(const char *unit
,
1038 const char *filename
,
1040 const char *section
,
1041 unsigned section_line
,
1048 ExecContext
*c
= data
;
1056 r
= safe_atoi(rvalue
, &i
);
1058 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1062 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1063 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1064 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1066 if (i
< min
|| i
> max
) {
1067 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1071 c
->cpu_sched_priority
= i
;
1072 c
->cpu_sched_set
= true;
1077 int config_parse_exec_cpu_affinity(const char *unit
,
1078 const char *filename
,
1080 const char *section
,
1081 unsigned section_line
,
1088 ExecContext
*c
= data
;
1089 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1097 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1102 CPU_FREE(c
->cpuset
);
1105 /* An empty assignment resets the CPU list */
1111 c
->cpuset_ncpus
= ncpus
;
1116 int config_parse_exec_secure_bits(const char *unit
,
1117 const char *filename
,
1119 const char *section
,
1120 unsigned section_line
,
1127 ExecContext
*c
= data
;
1135 if (isempty(rvalue
)) {
1136 /* An empty assignment resets the field */
1141 r
= secure_bits_from_string(rvalue
);
1145 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1146 "Invalid syntax, ignoring: %s", rvalue
);
1155 int config_parse_capability_set(
1157 const char *filename
,
1159 const char *section
,
1160 unsigned section_line
,
1167 uint64_t *capability_set
= data
;
1168 uint64_t sum
= 0, initial
= 0;
1169 bool invert
= false;
1177 if (rvalue
[0] == '~') {
1182 if (streq(lvalue
, "CapabilityBoundingSet"))
1183 initial
= CAP_ALL
; /* initialized to all bits on */
1184 /* else "AmbientCapabilities" initialized to all bits off */
1186 r
= capability_set_from_string(rvalue
, &sum
);
1190 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word: %s", rvalue
);
1194 if (sum
== 0 || *capability_set
== initial
)
1195 /* "", "~" or uninitialized data -> replace */
1196 *capability_set
= invert
? ~sum
: sum
;
1198 /* previous data -> merge */
1200 *capability_set
&= ~sum
;
1202 *capability_set
|= sum
;
1208 int config_parse_limit(
1210 const char *filename
,
1212 const char *section
,
1213 unsigned section_line
,
1220 struct rlimit
**rl
= data
, d
= {};
1228 r
= rlimit_parse(ltype
, rvalue
, &d
);
1230 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1234 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1241 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1249 #ifdef HAVE_SYSV_COMPAT
1250 int config_parse_sysv_priority(const char *unit
,
1251 const char *filename
,
1253 const char *section
,
1254 unsigned section_line
,
1261 int *priority
= data
;
1269 r
= safe_atoi(rvalue
, &i
);
1270 if (r
< 0 || i
< 0) {
1271 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1275 *priority
= (int) i
;
1280 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1281 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1283 int config_parse_exec_mount_flags(
1285 const char *filename
,
1287 const char *section
,
1288 unsigned section_line
,
1296 ExecContext
*c
= data
;
1304 r
= mount_propagation_flags_from_string(rvalue
, &c
->mount_flags
);
1306 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1311 int config_parse_exec_selinux_context(
1313 const char *filename
,
1315 const char *section
,
1316 unsigned section_line
,
1323 ExecContext
*c
= data
;
1334 if (isempty(rvalue
)) {
1335 c
->selinux_context
= mfree(c
->selinux_context
);
1336 c
->selinux_context_ignore
= false;
1340 if (rvalue
[0] == '-') {
1346 r
= unit_full_printf(u
, rvalue
, &k
);
1348 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1349 "Failed to resolve specifiers%s: %m",
1350 ignore
? ", ignoring" : "");
1351 return ignore
? 0 : -ENOEXEC
;
1354 free(c
->selinux_context
);
1355 c
->selinux_context
= k
;
1356 c
->selinux_context_ignore
= ignore
;
1361 int config_parse_exec_apparmor_profile(
1363 const char *filename
,
1365 const char *section
,
1366 unsigned section_line
,
1373 ExecContext
*c
= data
;
1384 if (isempty(rvalue
)) {
1385 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1386 c
->apparmor_profile_ignore
= false;
1390 if (rvalue
[0] == '-') {
1396 r
= unit_full_printf(u
, rvalue
, &k
);
1398 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1399 "Failed to resolve specifiers%s: %m",
1400 ignore
? ", ignoring" : "");
1401 return ignore
? 0 : -ENOEXEC
;
1404 free(c
->apparmor_profile
);
1405 c
->apparmor_profile
= k
;
1406 c
->apparmor_profile_ignore
= ignore
;
1411 int config_parse_exec_smack_process_label(
1413 const char *filename
,
1415 const char *section
,
1416 unsigned section_line
,
1423 ExecContext
*c
= data
;
1434 if (isempty(rvalue
)) {
1435 c
->smack_process_label
= mfree(c
->smack_process_label
);
1436 c
->smack_process_label_ignore
= false;
1440 if (rvalue
[0] == '-') {
1446 r
= unit_full_printf(u
, rvalue
, &k
);
1448 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1449 "Failed to resolve specifiers%s: %m",
1450 ignore
? ", ignoring" : "");
1451 return ignore
? 0 : -ENOEXEC
;
1454 free(c
->smack_process_label
);
1455 c
->smack_process_label
= k
;
1456 c
->smack_process_label_ignore
= ignore
;
1461 int config_parse_timer(const char *unit
,
1462 const char *filename
,
1464 const char *section
,
1465 unsigned section_line
,
1476 CalendarSpec
*c
= NULL
;
1478 _cleanup_free_
char *k
= NULL
;
1486 if (isempty(rvalue
)) {
1487 /* Empty assignment resets list */
1488 timer_free_values(t
);
1492 b
= timer_base_from_string(lvalue
);
1494 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1498 r
= unit_full_printf(u
, rvalue
, &k
);
1500 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1504 if (b
== TIMER_CALENDAR
) {
1505 if (calendar_spec_from_string(k
, &c
) < 0) {
1506 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1510 if (parse_sec(k
, &usec
) < 0) {
1511 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1516 v
= new0(TimerValue
, 1);
1518 calendar_spec_free(c
);
1524 v
->calendar_spec
= c
;
1526 LIST_PREPEND(value
, t
->values
, v
);
1531 int config_parse_trigger_unit(
1533 const char *filename
,
1535 const char *section
,
1536 unsigned section_line
,
1543 _cleanup_free_
char *p
= NULL
;
1553 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1554 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1558 r
= unit_name_printf(u
, rvalue
, &p
);
1560 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1564 type
= unit_name_to_type(p
);
1566 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1570 if (type
== u
->type
) {
1571 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1575 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1577 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1584 int config_parse_path_spec(const char *unit
,
1585 const char *filename
,
1587 const char *section
,
1588 unsigned section_line
,
1598 _cleanup_free_
char *k
= NULL
;
1606 if (isempty(rvalue
)) {
1607 /* Empty assignment clears list */
1612 b
= path_type_from_string(lvalue
);
1614 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1618 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1620 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1624 if (!path_is_absolute(k
)) {
1625 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1629 s
= new0(PathSpec
, 1);
1634 s
->path
= path_kill_slashes(k
);
1639 LIST_PREPEND(spec
, p
->specs
, s
);
1644 int config_parse_socket_service(
1646 const char *filename
,
1648 const char *section
,
1649 unsigned section_line
,
1656 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1657 _cleanup_free_
char *p
= NULL
;
1667 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1669 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers: %s", rvalue
);
1673 if (!endswith(p
, ".service")) {
1674 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service: %s", rvalue
);
1678 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1680 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s: %s", rvalue
, bus_error_message(&error
, r
));
1684 unit_ref_set(&s
->service
, x
);
1689 int config_parse_fdname(
1691 const char *filename
,
1693 const char *section
,
1694 unsigned section_line
,
1701 _cleanup_free_
char *p
= NULL
;
1710 if (isempty(rvalue
)) {
1711 s
->fdname
= mfree(s
->fdname
);
1715 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1717 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1721 if (!fdname_is_valid(p
)) {
1722 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1726 return free_and_replace(s
->fdname
, p
);
1729 int config_parse_service_sockets(
1731 const char *filename
,
1733 const char *section
,
1734 unsigned section_line
,
1752 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1754 r
= extract_first_word(&p
, &word
, NULL
, 0);
1760 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1764 r
= unit_name_printf(UNIT(s
), word
, &k
);
1766 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1770 if (!endswith(k
, ".socket")) {
1771 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1775 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1777 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1779 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1781 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1787 int config_parse_bus_name(
1789 const char *filename
,
1791 const char *section
,
1792 unsigned section_line
,
1799 _cleanup_free_
char *k
= NULL
;
1808 r
= unit_full_printf(u
, rvalue
, &k
);
1810 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1814 if (!service_name_is_valid(k
)) {
1815 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1819 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1822 int config_parse_service_timeout(
1824 const char *filename
,
1826 const char *section
,
1827 unsigned section_line
,
1834 Service
*s
= userdata
;
1843 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1845 r
= parse_sec(rvalue
, &usec
);
1847 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1851 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1852 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1853 * all other timeouts. */
1855 usec
= USEC_INFINITY
;
1857 if (!streq(lvalue
, "TimeoutStopSec")) {
1858 s
->start_timeout_defined
= true;
1859 s
->timeout_start_usec
= usec
;
1862 if (!streq(lvalue
, "TimeoutStartSec"))
1863 s
->timeout_stop_usec
= usec
;
1868 int config_parse_sec_fix_0(
1870 const char *filename
,
1872 const char *section
,
1873 unsigned section_line
,
1880 usec_t
*usec
= data
;
1888 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1889 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1892 r
= parse_sec_fix_0(rvalue
, usec
);
1894 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1901 int config_parse_user_group(
1903 const char *filename
,
1905 const char *section
,
1906 unsigned section_line
,
1913 char **user
= data
, *n
;
1922 if (isempty(rvalue
))
1925 _cleanup_free_
char *k
= NULL
;
1927 r
= unit_full_printf(u
, rvalue
, &k
);
1929 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
1933 if (!valid_user_group_name_or_id(k
)) {
1934 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1948 int config_parse_user_group_strv(
1950 const char *filename
,
1952 const char *section
,
1953 unsigned section_line
,
1960 char ***users
= data
;
1970 if (isempty(rvalue
)) {
1973 empty
= new0(char*, 1);
1985 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1987 r
= extract_first_word(&p
, &word
, NULL
, 0);
1993 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax: %s", rvalue
);
1997 r
= unit_full_printf(u
, word
, &k
);
1999 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", word
);
2003 if (!valid_user_group_name_or_id(k
)) {
2004 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
2008 r
= strv_push(users
, k
);
2018 int config_parse_working_directory(
2020 const char *filename
,
2022 const char *section
,
2023 unsigned section_line
,
2030 ExecContext
*c
= data
;
2041 if (rvalue
[0] == '-') {
2047 if (streq(rvalue
, "~")) {
2048 c
->working_directory_home
= true;
2049 c
->working_directory
= mfree(c
->working_directory
);
2051 _cleanup_free_
char *k
= NULL
;
2053 r
= unit_full_printf(u
, rvalue
, &k
);
2055 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2056 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2057 rvalue
, missing_ok
? ", ignoring" : "");
2058 return missing_ok
? 0 : -ENOEXEC
;
2061 path_kill_slashes(k
);
2063 if (!utf8_is_valid(k
)) {
2064 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2065 return missing_ok
? 0 : -ENOEXEC
;
2068 if (!path_is_absolute(k
)) {
2069 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2070 "Working directory path '%s' is not absolute%s.",
2071 rvalue
, missing_ok
? ", ignoring" : "");
2072 return missing_ok
? 0 : -ENOEXEC
;
2075 c
->working_directory_home
= false;
2076 free_and_replace(c
->working_directory
, k
);
2079 c
->working_directory_missing_ok
= missing_ok
;
2083 int config_parse_unit_env_file(const char *unit
,
2084 const char *filename
,
2086 const char *section
,
2087 unsigned section_line
,
2096 _cleanup_free_
char *n
= NULL
;
2104 if (isempty(rvalue
)) {
2105 /* Empty assignment frees the list */
2106 *env
= strv_free(*env
);
2110 r
= unit_full_printf(u
, rvalue
, &n
);
2112 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2116 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2117 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2121 r
= strv_extend(env
, n
);
2128 int config_parse_environ(const char *unit
,
2129 const char *filename
,
2131 const char *section
,
2132 unsigned section_line
,
2149 if (isempty(rvalue
)) {
2150 /* Empty assignment resets the list */
2151 *env
= strv_free(*env
);
2155 for (p
= rvalue
;; ) {
2156 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2158 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2164 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2165 "Invalid syntax, ignoring: %s", rvalue
);
2170 r
= unit_full_printf(u
, word
, &k
);
2172 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2173 "Failed to resolve specifiers, ignoring: %s", k
);
2181 if (!env_assignment_is_valid(k
)) {
2182 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2183 "Invalid environment assignment, ignoring: %s", k
);
2187 r
= strv_env_replace(env
, k
);
2194 int config_parse_pass_environ(const char *unit
,
2195 const char *filename
,
2197 const char *section
,
2198 unsigned section_line
,
2205 const char *whole_rvalue
= rvalue
;
2206 char*** passenv
= data
;
2207 _cleanup_strv_free_
char **n
= NULL
;
2208 size_t nlen
= 0, nbufsize
= 0;
2216 if (isempty(rvalue
)) {
2217 /* Empty assignment resets the list */
2218 *passenv
= strv_free(*passenv
);
2223 _cleanup_free_
char *word
= NULL
;
2225 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2231 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2232 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2236 if (!env_name_is_valid(word
)) {
2237 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2238 "Invalid environment name for %s, ignoring: %s", lvalue
, word
);
2242 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2250 r
= strv_extend_strv(passenv
, n
, true);
2258 int config_parse_ip_tos(const char *unit
,
2259 const char *filename
,
2261 const char *section
,
2262 unsigned section_line
,
2269 int *ip_tos
= data
, x
;
2276 x
= ip_tos_from_string(rvalue
);
2278 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2286 int config_parse_unit_condition_path(
2288 const char *filename
,
2290 const char *section
,
2291 unsigned section_line
,
2298 _cleanup_free_
char *p
= NULL
;
2299 Condition
**list
= data
, *c
;
2300 ConditionType t
= ltype
;
2301 bool trigger
, negate
;
2310 if (isempty(rvalue
)) {
2311 /* Empty assignment resets the list */
2312 *list
= condition_free_list(*list
);
2316 trigger
= rvalue
[0] == '|';
2320 negate
= rvalue
[0] == '!';
2324 r
= unit_full_printf(u
, rvalue
, &p
);
2326 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2330 if (!path_is_absolute(p
)) {
2331 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2335 c
= condition_new(t
, p
, trigger
, negate
);
2339 LIST_PREPEND(conditions
, *list
, c
);
2343 int config_parse_unit_condition_string(
2345 const char *filename
,
2347 const char *section
,
2348 unsigned section_line
,
2355 _cleanup_free_
char *s
= NULL
;
2356 Condition
**list
= data
, *c
;
2357 ConditionType t
= ltype
;
2358 bool trigger
, negate
;
2367 if (isempty(rvalue
)) {
2368 /* Empty assignment resets the list */
2369 *list
= condition_free_list(*list
);
2373 trigger
= rvalue
[0] == '|';
2377 negate
= rvalue
[0] == '!';
2381 r
= unit_full_printf(u
, rvalue
, &s
);
2383 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2387 c
= condition_new(t
, s
, trigger
, negate
);
2391 LIST_PREPEND(conditions
, *list
, c
);
2395 int config_parse_unit_condition_null(
2397 const char *filename
,
2399 const char *section
,
2400 unsigned section_line
,
2407 Condition
**list
= data
, *c
;
2408 bool trigger
, negate
;
2416 if (isempty(rvalue
)) {
2417 /* Empty assignment resets the list */
2418 *list
= condition_free_list(*list
);
2422 trigger
= rvalue
[0] == '|';
2426 negate
= rvalue
[0] == '!';
2430 b
= parse_boolean(rvalue
);
2432 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2439 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2443 LIST_PREPEND(conditions
, *list
, c
);
2447 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2448 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2450 int config_parse_unit_requires_mounts_for(
2452 const char *filename
,
2454 const char *section
,
2455 unsigned section_line
,
2471 for (p
= rvalue
;; ) {
2472 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2474 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2480 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2481 "Invalid syntax, ignoring: %s", rvalue
);
2485 if (!utf8_is_valid(word
)) {
2486 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2490 r
= unit_full_printf(u
, word
, &resolved
);
2492 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit name \"%s\", ignoring: %m", word
);
2496 r
= unit_require_mounts_for(u
, resolved
);
2498 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", resolved
);
2504 int config_parse_documentation(const char *unit
,
2505 const char *filename
,
2507 const char *section
,
2508 unsigned section_line
,
2524 if (isempty(rvalue
)) {
2525 /* Empty assignment resets the list */
2526 u
->documentation
= strv_free(u
->documentation
);
2530 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2531 rvalue
, data
, userdata
);
2535 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2537 if (documentation_url_is_valid(*a
))
2540 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2552 static int syscall_filter_parse_one(
2554 const char *filename
,
2563 const SyscallFilterSet
*set
;
2566 set
= syscall_filter_set_find(t
);
2569 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Don't know system call group, ignoring: %s", t
);
2573 NULSTR_FOREACH(i
, set
->value
) {
2574 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, i
, false);
2581 id
= seccomp_syscall_resolve_name(t
);
2582 if (id
== __NR_SCMP_ERROR
) {
2584 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2588 /* If we previously wanted to forbid a syscall and now
2589 * we want to allow it, then remove it from the list
2591 if (!invert
== c
->syscall_whitelist
) {
2592 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2598 (void) set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2604 int config_parse_syscall_filter(
2606 const char *filename
,
2608 const char *section
,
2609 unsigned section_line
,
2616 ExecContext
*c
= data
;
2618 bool invert
= false;
2627 if (isempty(rvalue
)) {
2628 /* Empty assignment resets the list */
2629 c
->syscall_filter
= set_free(c
->syscall_filter
);
2630 c
->syscall_whitelist
= false;
2634 if (rvalue
[0] == '~') {
2639 if (!c
->syscall_filter
) {
2640 c
->syscall_filter
= set_new(NULL
);
2641 if (!c
->syscall_filter
)
2645 /* Allow everything but the ones listed */
2646 c
->syscall_whitelist
= false;
2648 /* Allow nothing but the ones listed */
2649 c
->syscall_whitelist
= true;
2651 /* Accept default syscalls if we are on a whitelist */
2652 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, false, "@default", false);
2660 _cleanup_free_
char *word
= NULL
;
2662 r
= extract_first_word(&p
, &word
, NULL
, 0);
2668 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2672 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, word
, true);
2680 int config_parse_syscall_archs(
2682 const char *filename
,
2684 const char *section
,
2685 unsigned section_line
,
2696 if (isempty(rvalue
)) {
2697 *archs
= set_free(*archs
);
2701 r
= set_ensure_allocated(archs
, NULL
);
2705 for (p
= rvalue
;;) {
2706 _cleanup_free_
char *word
= NULL
;
2709 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2715 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2716 "Invalid syntax, ignoring: %s", rvalue
);
2720 r
= seccomp_arch_from_string(word
, &a
);
2722 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2723 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
2727 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2733 int config_parse_syscall_errno(
2735 const char *filename
,
2737 const char *section
,
2738 unsigned section_line
,
2745 ExecContext
*c
= data
;
2752 if (isempty(rvalue
)) {
2753 /* Empty assignment resets to KILL */
2754 c
->syscall_errno
= 0;
2758 e
= errno_from_name(rvalue
);
2760 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2764 c
->syscall_errno
= e
;
2768 int config_parse_address_families(
2770 const char *filename
,
2772 const char *section
,
2773 unsigned section_line
,
2780 ExecContext
*c
= data
;
2781 bool invert
= false;
2789 if (isempty(rvalue
)) {
2790 /* Empty assignment resets the list */
2791 c
->address_families
= set_free(c
->address_families
);
2792 c
->address_families_whitelist
= false;
2796 if (rvalue
[0] == '~') {
2801 if (!c
->address_families
) {
2802 c
->address_families
= set_new(NULL
);
2803 if (!c
->address_families
)
2806 c
->address_families_whitelist
= !invert
;
2809 for (p
= rvalue
;;) {
2810 _cleanup_free_
char *word
= NULL
;
2813 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2819 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2820 "Invalid syntax, ignoring: %s", rvalue
);
2824 af
= af_from_name(word
);
2826 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2827 "Failed to parse address family \"%s\", ignoring: %m", word
);
2831 /* If we previously wanted to forbid an address family and now
2832 * we want to allow it, then just remove it from the list.
2834 if (!invert
== c
->address_families_whitelist
) {
2835 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2839 set_remove(c
->address_families
, INT_TO_PTR(af
));
2843 int config_parse_restrict_namespaces(
2845 const char *filename
,
2847 const char *section
,
2848 unsigned section_line
,
2855 ExecContext
*c
= data
;
2856 bool invert
= false;
2859 if (isempty(rvalue
)) {
2860 /* Reset to the default. */
2861 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2865 if (rvalue
[0] == '~') {
2870 r
= parse_boolean(rvalue
);
2872 c
->restrict_namespaces
= 0;
2874 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2876 /* Not a boolean argument, in this case it's a list of namespace types. */
2878 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
2880 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
2886 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
2892 int config_parse_unit_slice(
2894 const char *filename
,
2896 const char *section
,
2897 unsigned section_line
,
2904 _cleanup_free_
char *k
= NULL
;
2905 Unit
*u
= userdata
, *slice
= NULL
;
2913 r
= unit_name_printf(u
, rvalue
, &k
);
2915 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2919 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2921 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2925 r
= unit_set_slice(u
, slice
);
2927 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2934 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2936 int config_parse_cpu_weight(
2938 const char *filename
,
2940 const char *section
,
2941 unsigned section_line
,
2948 uint64_t *weight
= data
;
2955 r
= cg_weight_parse(rvalue
, weight
);
2957 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
2964 int config_parse_cpu_shares(
2966 const char *filename
,
2968 const char *section
,
2969 unsigned section_line
,
2976 uint64_t *shares
= data
;
2983 r
= cg_cpu_shares_parse(rvalue
, shares
);
2985 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2992 int config_parse_cpu_quota(
2994 const char *filename
,
2996 const char *section
,
2997 unsigned section_line
,
3004 CGroupContext
*c
= data
;
3011 if (isempty(rvalue
)) {
3012 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3016 r
= parse_percent_unbounded(rvalue
);
3018 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3022 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3026 int config_parse_memory_limit(
3028 const char *filename
,
3030 const char *section
,
3031 unsigned section_line
,
3038 CGroupContext
*c
= data
;
3039 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3042 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3044 r
= parse_percent(rvalue
);
3046 r
= parse_size(rvalue
, 1024, &bytes
);
3048 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3052 bytes
= physical_memory_scale(r
, 100U);
3054 if (bytes
<= 0 || bytes
>= UINT64_MAX
) {
3055 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3060 if (streq(lvalue
, "MemoryLow"))
3061 c
->memory_low
= bytes
;
3062 else if (streq(lvalue
, "MemoryHigh"))
3063 c
->memory_high
= bytes
;
3064 else if (streq(lvalue
, "MemoryMax"))
3065 c
->memory_max
= bytes
;
3066 else if (streq(lvalue
, "MemorySwapMax"))
3067 c
->memory_swap_max
= bytes
;
3068 else if (streq(lvalue
, "MemoryLimit"))
3069 c
->memory_limit
= bytes
;
3076 int config_parse_tasks_max(
3078 const char *filename
,
3080 const char *section
,
3081 unsigned section_line
,
3088 uint64_t *tasks_max
= data
, v
;
3092 if (isempty(rvalue
)) {
3093 *tasks_max
= u
->manager
->default_tasks_max
;
3097 if (streq(rvalue
, "infinity")) {
3098 *tasks_max
= CGROUP_LIMIT_MAX
;
3102 r
= parse_percent(rvalue
);
3104 r
= safe_atou64(rvalue
, &v
);
3106 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3110 v
= system_tasks_max_scale(r
, 100U);
3112 if (v
<= 0 || v
>= UINT64_MAX
) {
3113 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3121 int config_parse_device_allow(
3123 const char *filename
,
3125 const char *section
,
3126 unsigned section_line
,
3133 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3134 CGroupContext
*c
= data
;
3135 CGroupDeviceAllow
*a
;
3136 const char *m
= NULL
;
3140 if (isempty(rvalue
)) {
3141 while (c
->device_allow
)
3142 cgroup_context_free_device_allow(c
, c
->device_allow
);
3147 r
= unit_full_printf(userdata
, rvalue
, &t
);
3149 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3150 "Failed to resolve specifiers in %s, ignoring: %m",
3154 n
= strcspn(t
, WHITESPACE
);
3156 path
= strndup(t
, n
);
3160 if (!is_deviceallow_pattern(path
)) {
3161 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3165 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3169 if (!in_charset(m
, "rwm")) {
3170 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3174 a
= new0(CGroupDeviceAllow
, 1);
3180 a
->r
= !!strchr(m
, 'r');
3181 a
->w
= !!strchr(m
, 'w');
3182 a
->m
= !!strchr(m
, 'm');
3184 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3188 int config_parse_io_weight(
3190 const char *filename
,
3192 const char *section
,
3193 unsigned section_line
,
3200 uint64_t *weight
= data
;
3207 r
= cg_weight_parse(rvalue
, weight
);
3209 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3216 int config_parse_io_device_weight(
3218 const char *filename
,
3220 const char *section
,
3221 unsigned section_line
,
3228 _cleanup_free_
char *path
= NULL
;
3229 CGroupIODeviceWeight
*w
;
3230 CGroupContext
*c
= data
;
3240 if (isempty(rvalue
)) {
3241 while (c
->io_device_weights
)
3242 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3247 n
= strcspn(rvalue
, WHITESPACE
);
3248 weight
= rvalue
+ n
;
3249 weight
+= strspn(weight
, WHITESPACE
);
3251 if (isempty(weight
)) {
3252 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3256 path
= strndup(rvalue
, n
);
3260 if (!path_startswith(path
, "/dev")) {
3261 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3265 r
= cg_weight_parse(weight
, &u
);
3267 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3271 assert(u
!= CGROUP_WEIGHT_INVALID
);
3273 w
= new0(CGroupIODeviceWeight
, 1);
3282 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3286 int config_parse_io_limit(
3288 const char *filename
,
3290 const char *section
,
3291 unsigned section_line
,
3298 _cleanup_free_
char *path
= NULL
;
3299 CGroupIODeviceLimit
*l
= NULL
, *t
;
3300 CGroupContext
*c
= data
;
3301 CGroupIOLimitType type
;
3311 type
= cgroup_io_limit_type_from_string(lvalue
);
3314 if (isempty(rvalue
)) {
3315 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3316 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3320 n
= strcspn(rvalue
, WHITESPACE
);
3322 limit
+= strspn(limit
, WHITESPACE
);
3325 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3329 path
= strndup(rvalue
, n
);
3333 if (!path_startswith(path
, "/dev")) {
3334 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3338 if (streq("infinity", limit
)) {
3339 num
= CGROUP_LIMIT_MAX
;
3341 r
= parse_size(limit
, 1000, &num
);
3342 if (r
< 0 || num
<= 0) {
3343 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3348 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3349 if (path_equal(path
, t
->path
)) {
3356 CGroupIOLimitType ttype
;
3358 l
= new0(CGroupIODeviceLimit
, 1);
3364 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3365 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3367 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3370 l
->limits
[type
] = num
;
3375 int config_parse_blockio_weight(
3377 const char *filename
,
3379 const char *section
,
3380 unsigned section_line
,
3387 uint64_t *weight
= data
;
3394 r
= cg_blkio_weight_parse(rvalue
, weight
);
3396 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3403 int config_parse_blockio_device_weight(
3405 const char *filename
,
3407 const char *section
,
3408 unsigned section_line
,
3415 _cleanup_free_
char *path
= NULL
;
3416 CGroupBlockIODeviceWeight
*w
;
3417 CGroupContext
*c
= data
;
3427 if (isempty(rvalue
)) {
3428 while (c
->blockio_device_weights
)
3429 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3434 n
= strcspn(rvalue
, WHITESPACE
);
3435 weight
= rvalue
+ n
;
3436 weight
+= strspn(weight
, WHITESPACE
);
3438 if (isempty(weight
)) {
3439 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3443 path
= strndup(rvalue
, n
);
3447 if (!path_startswith(path
, "/dev")) {
3448 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3452 r
= cg_blkio_weight_parse(weight
, &u
);
3454 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3458 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3460 w
= new0(CGroupBlockIODeviceWeight
, 1);
3469 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3473 int config_parse_blockio_bandwidth(
3475 const char *filename
,
3477 const char *section
,
3478 unsigned section_line
,
3485 _cleanup_free_
char *path
= NULL
;
3486 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3487 CGroupContext
*c
= data
;
3488 const char *bandwidth
;
3498 read
= streq("BlockIOReadBandwidth", lvalue
);
3500 if (isempty(rvalue
)) {
3501 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3502 b
->rbps
= CGROUP_LIMIT_MAX
;
3503 b
->wbps
= CGROUP_LIMIT_MAX
;
3508 n
= strcspn(rvalue
, WHITESPACE
);
3509 bandwidth
= rvalue
+ n
;
3510 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3513 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3517 path
= strndup(rvalue
, n
);
3521 if (!path_startswith(path
, "/dev")) {
3522 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3526 r
= parse_size(bandwidth
, 1000, &bytes
);
3527 if (r
< 0 || bytes
<= 0) {
3528 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3532 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3533 if (path_equal(path
, t
->path
)) {
3540 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3546 b
->rbps
= CGROUP_LIMIT_MAX
;
3547 b
->wbps
= CGROUP_LIMIT_MAX
;
3549 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3560 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3562 int config_parse_job_mode_isolate(
3564 const char *filename
,
3566 const char *section
,
3567 unsigned section_line
,
3581 r
= parse_boolean(rvalue
);
3583 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3587 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3591 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode
, exec_preserve_mode
, ExecPreserveMode
, "Failed to parse runtime directory preserve mode");
3593 int config_parse_exec_directories(
3595 const char *filename
,
3597 const char *section
,
3598 unsigned section_line
,
3615 if (isempty(rvalue
)) {
3616 /* Empty assignment resets the list */
3617 *rt
= strv_free(*rt
);
3621 for (p
= rvalue
;;) {
3622 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3624 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3630 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3631 "Invalid syntax, ignoring: %s", rvalue
);
3635 r
= unit_full_printf(u
, word
, &k
);
3637 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3638 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
3642 if (!path_is_safe(k
) || path_is_absolute(k
)) {
3643 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3644 "%s is not valid, ignoring assignment: %s", lvalue
, rvalue
);
3648 r
= strv_push(rt
, k
);
3655 int config_parse_set_status(
3657 const char *filename
,
3659 const char *section
,
3660 unsigned section_line
,
3668 const char *word
, *state
;
3670 ExitStatusSet
*status_set
= data
;
3677 /* Empty assignment resets the list */
3678 if (isempty(rvalue
)) {
3679 exit_status_set_free(status_set
);
3683 FOREACH_WORD(word
, l
, rvalue
, state
) {
3684 _cleanup_free_
char *temp
;
3688 temp
= strndup(word
, l
);
3692 r
= safe_atoi(temp
, &val
);
3694 val
= signal_from_string_try_harder(temp
);
3697 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3700 set
= &status_set
->signal
;
3702 if (val
< 0 || val
> 255) {
3703 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3706 set
= &status_set
->status
;
3709 r
= set_ensure_allocated(set
, NULL
);
3713 r
= set_put(*set
, INT_TO_PTR(val
));
3715 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3719 if (!isempty(state
))
3720 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3725 int config_parse_namespace_path_strv(
3727 const char *filename
,
3729 const char *section
,
3730 unsigned section_line
,
3747 if (isempty(rvalue
)) {
3748 /* Empty assignment resets the list */
3749 *sv
= strv_free(*sv
);
3755 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
3757 bool ignore_enoent
= false, shall_prefix
= false;
3759 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3765 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
3769 if (!utf8_is_valid(word
)) {
3770 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3775 if (startswith(w
, "-")) {
3776 ignore_enoent
= true;
3779 if (startswith(w
, "+")) {
3780 shall_prefix
= true;
3784 r
= unit_full_printf(u
, w
, &resolved
);
3786 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s: %m", word
);
3790 if (!path_is_absolute(resolved
)) {
3791 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
3795 path_kill_slashes(resolved
);
3797 joined
= strjoin(ignore_enoent
? "-" : "",
3798 shall_prefix
? "+" : "",
3801 r
= strv_push(sv
, joined
);
3811 int config_parse_bind_paths(
3813 const char *filename
,
3815 const char *section
,
3816 unsigned section_line
,
3823 ExecContext
*c
= data
;
3833 if (isempty(rvalue
)) {
3834 /* Empty assignment resets the list */
3835 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
3836 c
->bind_mounts
= NULL
;
3837 c
->n_bind_mounts
= 0;
3843 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
3844 _cleanup_free_
char *sresolved
= NULL
, *dresolved
= NULL
;
3845 char *s
= NULL
, *d
= NULL
;
3846 bool rbind
= true, ignore_enoent
= false;
3848 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
3854 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3858 r
= unit_full_printf(u
, source
, &sresolved
);
3860 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3861 "Failed to resolved specifiers in \"%s\", ignoring: %m", source
);
3867 ignore_enoent
= true;
3871 if (!utf8_is_valid(s
)) {
3872 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, s
);
3875 if (!path_is_absolute(s
)) {
3876 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute source path, ignoring: %s", s
);
3880 path_kill_slashes(s
);
3882 /* Optionally, the destination is specified. */
3883 if (p
&& p
[-1] == ':') {
3884 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
3888 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3892 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':': %s", rvalue
);
3896 r
= unit_full_printf(u
, destination
, &dresolved
);
3898 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3899 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination
);
3903 if (!utf8_is_valid(dresolved
)) {
3904 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, dresolved
);
3907 if (!path_is_absolute(dresolved
)) {
3908 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute destination path, ignoring: %s", dresolved
);
3912 d
= path_kill_slashes(dresolved
);
3914 /* Optionally, there's also a short option string specified */
3915 if (p
&& p
[-1] == ':') {
3916 _cleanup_free_
char *options
= NULL
;
3918 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
3922 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3926 if (isempty(options
) || streq(options
, "rbind"))
3928 else if (streq(options
, "norbind"))
3931 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
3938 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
3942 .read_only
= !!strstr(lvalue
, "ReadOnly"),
3944 .ignore_enoent
= ignore_enoent
,
3953 int config_parse_no_new_privileges(
3955 const char *filename
,
3957 const char *section
,
3958 unsigned section_line
,
3965 ExecContext
*c
= data
;
3973 k
= parse_boolean(rvalue
);
3975 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3979 c
->no_new_privileges
= k
;
3984 int config_parse_protect_home(
3986 const char *filename
,
3988 const char *section
,
3989 unsigned section_line
,
3996 ExecContext
*c
= data
;
4004 /* Our enum shall be a superset of booleans, hence first try
4005 * to parse as boolean, and then as enum */
4007 k
= parse_boolean(rvalue
);
4009 c
->protect_home
= PROTECT_HOME_YES
;
4011 c
->protect_home
= PROTECT_HOME_NO
;
4015 h
= protect_home_from_string(rvalue
);
4017 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
4021 c
->protect_home
= h
;
4027 int config_parse_protect_system(
4029 const char *filename
,
4031 const char *section
,
4032 unsigned section_line
,
4039 ExecContext
*c
= data
;
4047 /* Our enum shall be a superset of booleans, hence first try
4048 * to parse as boolean, and then as enum */
4050 k
= parse_boolean(rvalue
);
4052 c
->protect_system
= PROTECT_SYSTEM_YES
;
4054 c
->protect_system
= PROTECT_SYSTEM_NO
;
4058 s
= protect_system_from_string(rvalue
);
4060 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
4064 c
->protect_system
= s
;
4070 #define FOLLOW_MAX 8
4072 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4083 /* This will update the filename pointer if the loaded file is
4084 * reached by a symlink. The old string will be freed. */
4087 char *target
, *name
;
4089 if (c
++ >= FOLLOW_MAX
)
4092 path_kill_slashes(*filename
);
4094 /* Add the file name we are currently looking at to
4095 * the names of this unit, but only if it is a valid
4097 name
= basename(*filename
);
4098 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4100 id
= set_get(names
, name
);
4106 r
= set_consume(names
, id
);
4112 /* Try to open the file name, but don't if its a symlink */
4113 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4120 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4121 r
= readlink_and_make_absolute(*filename
, &target
);
4129 f
= fdopen(fd
, "re");
4141 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4149 /* Let's try to add in all symlink names we found */
4150 while ((k
= set_steal_first(names
))) {
4152 /* First try to merge in the other name into our
4154 r
= unit_merge_by_name(*u
, k
);
4158 /* Hmm, we couldn't merge the other unit into
4159 * ours? Then let's try it the other way
4162 /* If the symlink name we are looking at is unit template, then
4163 we must search for instance of this template */
4164 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4165 _cleanup_free_
char *instance
= NULL
;
4167 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4171 other
= manager_get_unit((*u
)->manager
, instance
);
4173 other
= manager_get_unit((*u
)->manager
, k
);
4178 r
= unit_merge(other
, *u
);
4181 return merge_by_names(u
, names
, NULL
);
4189 unit_choose_id(*u
, id
);
4197 static int load_from_path(Unit
*u
, const char *path
) {
4198 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4199 _cleanup_fclose_
FILE *f
= NULL
;
4200 _cleanup_free_
char *filename
= NULL
;
4209 symlink_names
= set_new(&string_hash_ops
);
4213 if (path_is_absolute(path
)) {
4215 filename
= strdup(path
);
4219 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4221 filename
= mfree(filename
);
4229 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4231 /* Instead of opening the path right away, we manually
4232 * follow all symlinks and add their name to our unit
4233 * name set while doing so */
4234 filename
= path_make_absolute(path
, *p
);
4238 if (u
->manager
->unit_path_cache
&&
4239 !set_get(u
->manager
->unit_path_cache
, filename
))
4242 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4245 filename
= mfree(filename
);
4247 /* ENOENT means that the file is missing or is a dangling symlink.
4248 * ENOTDIR means that one of paths we expect to be is a directory
4249 * is not a directory, we should just ignore that.
4250 * EACCES means that the directory or file permissions are wrong.
4253 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4254 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4257 /* Empty the symlink names for the next run */
4258 set_clear_free(symlink_names
);
4263 /* Hmm, no suitable file found? */
4266 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4267 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4272 r
= merge_by_names(&merged
, symlink_names
, id
);
4277 u
->load_state
= UNIT_MERGED
;
4281 if (fstat(fileno(f
), &st
) < 0)
4284 if (null_or_empty(&st
)) {
4285 u
->load_state
= UNIT_MASKED
;
4286 u
->fragment_mtime
= 0;
4288 u
->load_state
= UNIT_LOADED
;
4289 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4291 /* Now, parse the file contents */
4292 r
= config_parse(u
->id
, filename
, f
,
4293 UNIT_VTABLE(u
)->sections
,
4294 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4295 false, true, false, u
);
4300 free(u
->fragment_path
);
4301 u
->fragment_path
= filename
;
4304 if (u
->source_path
) {
4305 if (stat(u
->source_path
, &st
) >= 0)
4306 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4308 u
->source_mtime
= 0;
4314 int unit_load_fragment(Unit
*u
) {
4320 assert(u
->load_state
== UNIT_STUB
);
4324 u
->load_state
= UNIT_LOADED
;
4328 /* First, try to find the unit under its id. We always look
4329 * for unit files in the default directories, to make it easy
4330 * to override things by placing things in /etc/systemd/system */
4331 r
= load_from_path(u
, u
->id
);
4335 /* Try to find an alias we can load this with */
4336 if (u
->load_state
== UNIT_STUB
) {
4337 SET_FOREACH(t
, u
->names
, i
) {
4342 r
= load_from_path(u
, t
);
4346 if (u
->load_state
!= UNIT_STUB
)
4351 /* And now, try looking for it under the suggested (originally linked) path */
4352 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4354 r
= load_from_path(u
, u
->fragment_path
);
4358 if (u
->load_state
== UNIT_STUB
)
4359 /* Hmm, this didn't work? Then let's get rid
4360 * of the fragment path stored for us, so that
4361 * we don't point to an invalid location. */
4362 u
->fragment_path
= mfree(u
->fragment_path
);
4365 /* Look for a template */
4366 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4367 _cleanup_free_
char *k
= NULL
;
4369 r
= unit_name_template(u
->id
, &k
);
4373 r
= load_from_path(u
, k
);
4376 log_unit_notice(u
, "Unit configuration has fatal error, unit will not be started.");
4380 if (u
->load_state
== UNIT_STUB
) {
4381 SET_FOREACH(t
, u
->names
, i
) {
4382 _cleanup_free_
char *z
= NULL
;
4387 r
= unit_name_template(t
, &z
);
4391 r
= load_from_path(u
, z
);
4395 if (u
->load_state
!= UNIT_STUB
)
4404 void unit_dump_config_items(FILE *f
) {
4405 static const struct {
4406 const ConfigParserCallback callback
;
4409 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
4410 { config_parse_warn_compat
, "NOTSUPPORTED" },
4412 { config_parse_int
, "INTEGER" },
4413 { config_parse_unsigned
, "UNSIGNED" },
4414 { config_parse_iec_size
, "SIZE" },
4415 { config_parse_iec_uint64
, "SIZE" },
4416 { config_parse_si_size
, "SIZE" },
4417 { config_parse_bool
, "BOOLEAN" },
4418 { config_parse_string
, "STRING" },
4419 { config_parse_path
, "PATH" },
4420 { config_parse_unit_path_printf
, "PATH" },
4421 { config_parse_strv
, "STRING [...]" },
4422 { config_parse_exec_nice
, "NICE" },
4423 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4424 { config_parse_exec_io_class
, "IOCLASS" },
4425 { config_parse_exec_io_priority
, "IOPRIORITY" },
4426 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4427 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4428 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4429 { config_parse_mode
, "MODE" },
4430 { config_parse_unit_env_file
, "FILE" },
4431 { config_parse_exec_output
, "OUTPUT" },
4432 { config_parse_exec_input
, "INPUT" },
4433 { config_parse_log_facility
, "FACILITY" },
4434 { config_parse_log_level
, "LEVEL" },
4435 { config_parse_exec_secure_bits
, "SECUREBITS" },
4436 { config_parse_capability_set
, "BOUNDINGSET" },
4437 { config_parse_limit
, "LIMIT" },
4438 { config_parse_unit_deps
, "UNIT [...]" },
4439 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4440 { config_parse_service_type
, "SERVICETYPE" },
4441 { config_parse_service_restart
, "SERVICERESTART" },
4442 #ifdef HAVE_SYSV_COMPAT
4443 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4445 { config_parse_kill_mode
, "KILLMODE" },
4446 { config_parse_signal
, "SIGNAL" },
4447 { config_parse_socket_listen
, "SOCKET [...]" },
4448 { config_parse_socket_bind
, "SOCKETBIND" },
4449 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4450 { config_parse_sec
, "SECONDS" },
4451 { config_parse_nsec
, "NANOSECONDS" },
4452 { config_parse_namespace_path_strv
, "PATH [...]" },
4453 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4454 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4455 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4456 { config_parse_unit_string_printf
, "STRING" },
4457 { config_parse_trigger_unit
, "UNIT" },
4458 { config_parse_timer
, "TIMER" },
4459 { config_parse_path_spec
, "PATH" },
4460 { config_parse_notify_access
, "ACCESS" },
4461 { config_parse_ip_tos
, "TOS" },
4462 { config_parse_unit_condition_path
, "CONDITION" },
4463 { config_parse_unit_condition_string
, "CONDITION" },
4464 { config_parse_unit_condition_null
, "CONDITION" },
4465 { config_parse_unit_slice
, "SLICE" },
4466 { config_parse_documentation
, "URL" },
4467 { config_parse_service_timeout
, "SECONDS" },
4468 { config_parse_emergency_action
, "ACTION" },
4469 { config_parse_set_status
, "STATUS" },
4470 { config_parse_service_sockets
, "SOCKETS" },
4471 { config_parse_environ
, "ENVIRON" },
4473 { config_parse_syscall_filter
, "SYSCALLS" },
4474 { config_parse_syscall_archs
, "ARCHS" },
4475 { config_parse_syscall_errno
, "ERRNO" },
4476 { config_parse_address_families
, "FAMILIES" },
4477 { config_parse_restrict_namespaces
, "NAMESPACES" },
4479 { config_parse_cpu_shares
, "SHARES" },
4480 { config_parse_cpu_weight
, "WEIGHT" },
4481 { config_parse_memory_limit
, "LIMIT" },
4482 { config_parse_device_allow
, "DEVICE" },
4483 { config_parse_device_policy
, "POLICY" },
4484 { config_parse_io_limit
, "LIMIT" },
4485 { config_parse_io_weight
, "WEIGHT" },
4486 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4487 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4488 { config_parse_blockio_weight
, "WEIGHT" },
4489 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4490 { config_parse_long
, "LONG" },
4491 { config_parse_socket_service
, "SERVICE" },
4493 { config_parse_exec_selinux_context
, "LABEL" },
4495 { config_parse_job_mode
, "MODE" },
4496 { config_parse_job_mode_isolate
, "BOOLEAN" },
4497 { config_parse_personality
, "PERSONALITY" },
4500 const char *prev
= NULL
;
4505 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4506 const char *rvalue
= "OTHER", *lvalue
;
4510 const ConfigPerfItem
*p
;
4512 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4514 dot
= strchr(i
, '.');
4515 lvalue
= dot
? dot
+ 1 : i
;
4519 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
4523 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
4526 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
4527 if (p
->parse
== table
[j
].callback
) {
4528 rvalue
= table
[j
].rvalue
;
4532 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);