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 "parse-util.h"
53 #include "path-util.h"
54 #include "process-util.h"
55 #include "rlimit-util.h"
57 #include "seccomp-util.h"
59 #include "securebits.h"
60 #include "signal-util.h"
61 #include "stat-util.h"
62 #include "string-util.h"
64 #include "unit-name.h"
65 #include "unit-printf.h"
67 #include "user-util.h"
71 int config_parse_warn_compat(
76 unsigned section_line
,
82 Disabled reason
= ltype
;
85 case DISABLED_CONFIGURATION
:
86 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
87 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
90 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
91 "Support for option %s= has been removed and it is ignored", lvalue
);
93 case DISABLED_EXPERIMENTAL
:
94 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
95 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
102 int config_parse_unit_deps(
104 const char *filename
,
107 unsigned section_line
,
114 UnitDependency d
= ltype
;
124 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
127 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_RETAIN_ESCAPE
);
133 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
137 r
= unit_name_printf(u
, word
, &k
);
139 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
143 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
145 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
151 int config_parse_obsolete_unit_deps(
153 const char *filename
,
156 unsigned section_line
,
163 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
164 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue
, unit_dependency_to_string(ltype
));
166 return config_parse_unit_deps(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, data
, userdata
);
169 int config_parse_unit_string_printf(
171 const char *filename
,
174 unsigned section_line
,
181 _cleanup_free_
char *k
= NULL
;
190 r
= unit_full_printf(u
, rvalue
, &k
);
192 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
196 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
199 int config_parse_unit_strv_printf(
201 const char *filename
,
204 unsigned section_line
,
212 _cleanup_free_
char *k
= NULL
;
220 r
= unit_full_printf(u
, rvalue
, &k
);
222 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
226 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
229 int config_parse_unit_path_printf(
231 const char *filename
,
234 unsigned section_line
,
241 _cleanup_free_
char *k
= NULL
;
250 r
= unit_full_printf(u
, rvalue
, &k
);
252 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
256 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
259 int config_parse_unit_path_strv_printf(
261 const char *filename
,
264 unsigned section_line
,
282 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
284 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
290 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
291 "Invalid syntax, ignoring: %s", rvalue
);
295 r
= unit_full_printf(u
, word
, &k
);
297 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
298 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
302 if (!utf8_is_valid(k
)) {
303 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
307 if (!path_is_absolute(k
)) {
308 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
309 "Symlink path is not absolute: %s", k
);
313 path_kill_slashes(k
);
322 int config_parse_socket_listen(const char *unit
,
323 const char *filename
,
326 unsigned section_line
,
333 _cleanup_free_ SocketPort
*p
= NULL
;
345 if (isempty(rvalue
)) {
346 /* An empty assignment removes all ports */
347 socket_free_ports(s
);
351 p
= new0(SocketPort
, 1);
355 if (ltype
!= SOCKET_SOCKET
) {
358 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
360 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
364 path_kill_slashes(p
->path
);
366 } else if (streq(lvalue
, "ListenNetlink")) {
367 _cleanup_free_
char *k
= NULL
;
369 p
->type
= SOCKET_SOCKET
;
370 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
372 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
376 r
= socket_address_parse_netlink(&p
->address
, k
);
378 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
383 _cleanup_free_
char *k
= NULL
;
385 p
->type
= SOCKET_SOCKET
;
386 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
388 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
392 r
= socket_address_parse_and_warn(&p
->address
, k
);
394 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
398 if (streq(lvalue
, "ListenStream"))
399 p
->address
.type
= SOCK_STREAM
;
400 else if (streq(lvalue
, "ListenDatagram"))
401 p
->address
.type
= SOCK_DGRAM
;
403 assert(streq(lvalue
, "ListenSequentialPacket"));
404 p
->address
.type
= SOCK_SEQPACKET
;
407 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
408 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
414 p
->auxiliary_fds
= NULL
;
415 p
->n_auxiliary_fds
= 0;
419 LIST_FIND_TAIL(port
, s
->ports
, tail
);
420 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
422 LIST_PREPEND(port
, s
->ports
, p
);
428 int config_parse_socket_protocol(const char *unit
,
429 const char *filename
,
432 unsigned section_line
,
447 if (streq(rvalue
, "udplite"))
448 s
->socket_protocol
= IPPROTO_UDPLITE
;
449 else if (streq(rvalue
, "sctp"))
450 s
->socket_protocol
= IPPROTO_SCTP
;
452 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
459 int config_parse_socket_bind(const char *unit
,
460 const char *filename
,
463 unsigned section_line
,
471 SocketAddressBindIPv6Only b
;
480 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
484 r
= parse_boolean(rvalue
);
486 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
490 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
492 s
->bind_ipv6_only
= b
;
497 int config_parse_exec_nice(
499 const char *filename
,
502 unsigned section_line
,
509 ExecContext
*c
= data
;
517 r
= parse_nice(rvalue
, &priority
);
520 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
522 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
533 int config_parse_exec_oom_score_adjust(const char* unit
,
534 const char *filename
,
537 unsigned section_line
,
544 ExecContext
*c
= data
;
552 r
= safe_atoi(rvalue
, &oa
);
554 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
558 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
559 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
563 c
->oom_score_adjust
= oa
;
564 c
->oom_score_adjust_set
= true;
569 int config_parse_exec(
571 const char *filename
,
574 unsigned section_line
,
581 ExecCommand
**e
= data
;
592 rvalue
+= strspn(rvalue
, WHITESPACE
);
594 if (isempty(rvalue
)) {
595 /* An empty assignment resets the list */
596 *e
= exec_command_free_list(*e
);
602 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
603 bool separate_argv0
= false, ignore
= false, privileged
= false;
604 _cleanup_free_ ExecCommand
*nce
= NULL
;
605 _cleanup_strv_free_
char **n
= NULL
;
606 size_t nlen
= 0, nbufsize
= 0;
612 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
617 for (i
= 0; i
< 3; i
++) {
618 /* We accept an absolute path as first argument.
619 * If it's prefixed with - and the path doesn't exist,
620 * we ignore it instead of erroring out;
621 * if it's prefixed with @, we allow overriding of argv[0];
622 * and if it's prefixed with !, it will be run with full privileges */
623 if (*f
== '-' && !ignore
)
625 else if (*f
== '@' && !separate_argv0
)
626 separate_argv0
= true;
627 else if (*f
== '+' && !privileged
)
635 /* First word is either "-" or "@" with no command. */
636 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty path in command line, ignoring: \"%s\"", rvalue
);
639 if (!string_is_safe(f
)) {
640 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path contains special characters, ignoring: %s", rvalue
);
643 if (!path_is_absolute(f
)) {
644 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path is not absolute, ignoring: %s", rvalue
);
647 if (endswith(f
, "/")) {
648 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path specifies a directory, ignoring: %s", rvalue
);
652 if (f
== firstword
) {
661 if (!separate_argv0
) {
662 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
671 path_kill_slashes(path
);
673 while (!isempty(p
)) {
674 _cleanup_free_
char *word
= NULL
;
676 /* Check explicitly for an unquoted semicolon as
677 * command separator token. */
678 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
680 p
+= strspn(p
, WHITESPACE
);
685 /* Check for \; explicitly, to not confuse it with \\;
686 * or "\;" or "\\;" etc. extract_first_word would
687 * return the same for all of those. */
688 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
690 p
+= strspn(p
, WHITESPACE
);
691 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
701 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
707 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
715 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
719 nce
= new0(ExecCommand
, 1);
725 nce
->ignore
= ignore
;
726 nce
->privileged
= privileged
;
728 exec_command_append_list(e
, nce
);
730 /* Do not _cleanup_free_ these. */
741 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
742 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
744 int config_parse_socket_bindtodevice(
746 const char *filename
,
749 unsigned section_line
,
764 if (rvalue
[0] && !streq(rvalue
, "*")) {
765 if (!ifname_valid(rvalue
)) {
766 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
776 free(s
->bind_to_device
);
777 s
->bind_to_device
= n
;
782 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input literal specifier");
783 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output literal specifier");
785 int config_parse_exec_input(const char *unit
,
786 const char *filename
,
789 unsigned section_line
,
795 ExecContext
*c
= data
;
804 name
= startswith(rvalue
, "fd:");
806 /* Strip prefix and validate fd name */
807 if (!fdname_is_valid(name
)) {
808 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
811 c
->std_input
= EXEC_INPUT_NAMED_FD
;
812 r
= free_and_strdup(&c
->stdio_fdname
[STDIN_FILENO
], name
);
817 ExecInput ei
= exec_input_from_string(rvalue
);
818 if (ei
== _EXEC_INPUT_INVALID
)
819 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
826 int config_parse_exec_output(const char *unit
,
827 const char *filename
,
830 unsigned section_line
,
836 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 eo
= EXEC_OUTPUT_NAMED_FD
;
856 eo
= exec_output_from_string(rvalue
);
857 if (eo
== _EXEC_OUTPUT_INVALID
) {
858 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
863 if (streq(lvalue
, "StandardOutput")) {
865 r
= free_and_strdup(&c
->stdio_fdname
[STDOUT_FILENO
], name
);
869 } else if (streq(lvalue
, "StandardError")) {
871 r
= free_and_strdup(&c
->stdio_fdname
[STDERR_FILENO
], name
);
876 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output property, ignoring: %s", lvalue
);
881 int config_parse_exec_io_class(const char *unit
,
882 const char *filename
,
885 unsigned section_line
,
892 ExecContext
*c
= data
;
900 x
= ioprio_class_from_string(rvalue
);
902 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
906 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
907 c
->ioprio_set
= true;
912 int config_parse_exec_io_priority(const char *unit
,
913 const char *filename
,
916 unsigned section_line
,
923 ExecContext
*c
= data
;
931 r
= safe_atoi(rvalue
, &i
);
932 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
933 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
937 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
938 c
->ioprio_set
= true;
943 int config_parse_exec_cpu_sched_policy(const char *unit
,
944 const char *filename
,
947 unsigned section_line
,
955 ExecContext
*c
= data
;
963 x
= sched_policy_from_string(rvalue
);
965 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
969 c
->cpu_sched_policy
= x
;
970 /* Moving to or from real-time policy? We need to adjust the priority */
971 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
972 c
->cpu_sched_set
= true;
977 int config_parse_exec_cpu_sched_prio(const char *unit
,
978 const char *filename
,
981 unsigned section_line
,
988 ExecContext
*c
= data
;
996 r
= safe_atoi(rvalue
, &i
);
998 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1002 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1003 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1004 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1006 if (i
< min
|| i
> max
) {
1007 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1011 c
->cpu_sched_priority
= i
;
1012 c
->cpu_sched_set
= true;
1017 int config_parse_exec_cpu_affinity(const char *unit
,
1018 const char *filename
,
1020 const char *section
,
1021 unsigned section_line
,
1028 ExecContext
*c
= data
;
1029 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1037 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1042 CPU_FREE(c
->cpuset
);
1045 /* An empty assignment resets the CPU list */
1051 c
->cpuset_ncpus
= ncpus
;
1056 int config_parse_exec_secure_bits(const char *unit
,
1057 const char *filename
,
1059 const char *section
,
1060 unsigned section_line
,
1067 ExecContext
*c
= data
;
1076 if (isempty(rvalue
)) {
1077 /* An empty assignment resets the field */
1082 for (p
= rvalue
;;) {
1083 _cleanup_free_
char *word
= NULL
;
1085 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1091 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1092 "Invalid syntax, ignoring: %s", rvalue
);
1096 if (streq(word
, "keep-caps"))
1097 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
1098 else if (streq(word
, "keep-caps-locked"))
1099 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
1100 else if (streq(word
, "no-setuid-fixup"))
1101 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
1102 else if (streq(word
, "no-setuid-fixup-locked"))
1103 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
1104 else if (streq(word
, "noroot"))
1105 c
->secure_bits
|= 1<<SECURE_NOROOT
;
1106 else if (streq(word
, "noroot-locked"))
1107 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
1109 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
1110 "Failed to parse secure bit \"%s\", ignoring.", word
);
1116 int config_parse_capability_set(
1118 const char *filename
,
1120 const char *section
,
1121 unsigned section_line
,
1128 uint64_t *capability_set
= data
;
1129 uint64_t sum
= 0, initial
= 0;
1130 bool invert
= false;
1138 if (rvalue
[0] == '~') {
1143 if (strcmp(lvalue
, "CapabilityBoundingSet") == 0)
1144 initial
= CAP_ALL
; /* initialized to all bits on */
1145 /* else "AmbientCapabilities" initialized to all bits off */
1149 _cleanup_free_
char *word
= NULL
;
1152 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1158 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word, ignoring: %s", rvalue
);
1162 cap
= capability_from_name(word
);
1164 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word
);
1168 sum
|= ((uint64_t) UINT64_C(1)) << (uint64_t) cap
;
1171 sum
= invert
? ~sum
: sum
;
1173 if (sum
== 0 || *capability_set
== initial
)
1174 /* "" or uninitialized data -> replace */
1175 *capability_set
= sum
;
1177 /* previous data -> merge */
1178 *capability_set
|= sum
;
1183 int config_parse_limit(
1185 const char *filename
,
1187 const char *section
,
1188 unsigned section_line
,
1195 struct rlimit
**rl
= data
, d
= {};
1203 r
= rlimit_parse(ltype
, rvalue
, &d
);
1205 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1209 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1216 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1224 #ifdef HAVE_SYSV_COMPAT
1225 int config_parse_sysv_priority(const char *unit
,
1226 const char *filename
,
1228 const char *section
,
1229 unsigned section_line
,
1236 int *priority
= data
;
1244 r
= safe_atoi(rvalue
, &i
);
1245 if (r
< 0 || i
< 0) {
1246 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1250 *priority
= (int) i
;
1255 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1256 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1258 int config_parse_exec_mount_flags(const char *unit
,
1259 const char *filename
,
1261 const char *section
,
1262 unsigned section_line
,
1270 unsigned long flags
= 0;
1271 ExecContext
*c
= data
;
1278 if (streq(rvalue
, "shared"))
1280 else if (streq(rvalue
, "slave"))
1282 else if (streq(rvalue
, "private"))
1285 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1289 c
->mount_flags
= flags
;
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_name_printf(u
, rvalue
, &k
);
1331 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1335 free(c
->selinux_context
);
1336 c
->selinux_context
= k
;
1337 c
->selinux_context_ignore
= ignore
;
1342 int config_parse_exec_apparmor_profile(
1344 const char *filename
,
1346 const char *section
,
1347 unsigned section_line
,
1354 ExecContext
*c
= data
;
1365 if (isempty(rvalue
)) {
1366 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1367 c
->apparmor_profile_ignore
= false;
1371 if (rvalue
[0] == '-') {
1377 r
= unit_name_printf(u
, rvalue
, &k
);
1379 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1383 free(c
->apparmor_profile
);
1384 c
->apparmor_profile
= k
;
1385 c
->apparmor_profile_ignore
= ignore
;
1390 int config_parse_exec_smack_process_label(
1392 const char *filename
,
1394 const char *section
,
1395 unsigned section_line
,
1402 ExecContext
*c
= data
;
1413 if (isempty(rvalue
)) {
1414 c
->smack_process_label
= mfree(c
->smack_process_label
);
1415 c
->smack_process_label_ignore
= false;
1419 if (rvalue
[0] == '-') {
1425 r
= unit_name_printf(u
, rvalue
, &k
);
1427 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1431 free(c
->smack_process_label
);
1432 c
->smack_process_label
= k
;
1433 c
->smack_process_label_ignore
= ignore
;
1438 int config_parse_timer(const char *unit
,
1439 const char *filename
,
1441 const char *section
,
1442 unsigned section_line
,
1453 CalendarSpec
*c
= NULL
;
1455 _cleanup_free_
char *k
= NULL
;
1463 if (isempty(rvalue
)) {
1464 /* Empty assignment resets list */
1465 timer_free_values(t
);
1469 b
= timer_base_from_string(lvalue
);
1471 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1475 r
= unit_full_printf(u
, rvalue
, &k
);
1477 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1481 if (b
== TIMER_CALENDAR
) {
1482 if (calendar_spec_from_string(k
, &c
) < 0) {
1483 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1487 if (parse_sec(k
, &usec
) < 0) {
1488 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1493 v
= new0(TimerValue
, 1);
1495 calendar_spec_free(c
);
1501 v
->calendar_spec
= c
;
1503 LIST_PREPEND(value
, t
->values
, v
);
1508 int config_parse_trigger_unit(
1510 const char *filename
,
1512 const char *section
,
1513 unsigned section_line
,
1520 _cleanup_free_
char *p
= NULL
;
1530 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1531 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1535 r
= unit_name_printf(u
, rvalue
, &p
);
1537 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1541 type
= unit_name_to_type(p
);
1543 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1547 if (type
== u
->type
) {
1548 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1552 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1554 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1561 int config_parse_path_spec(const char *unit
,
1562 const char *filename
,
1564 const char *section
,
1565 unsigned section_line
,
1575 _cleanup_free_
char *k
= NULL
;
1583 if (isempty(rvalue
)) {
1584 /* Empty assignment clears list */
1589 b
= path_type_from_string(lvalue
);
1591 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1595 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1597 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1601 if (!path_is_absolute(k
)) {
1602 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1606 s
= new0(PathSpec
, 1);
1611 s
->path
= path_kill_slashes(k
);
1616 LIST_PREPEND(spec
, p
->specs
, s
);
1621 int config_parse_socket_service(
1623 const char *filename
,
1625 const char *section
,
1626 unsigned section_line
,
1633 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1634 _cleanup_free_
char *p
= NULL
;
1644 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1646 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1650 if (!endswith(p
, ".service")) {
1651 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1655 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1657 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1661 unit_ref_set(&s
->service
, x
);
1666 int config_parse_fdname(
1668 const char *filename
,
1670 const char *section
,
1671 unsigned section_line
,
1678 _cleanup_free_
char *p
= NULL
;
1687 if (isempty(rvalue
)) {
1688 s
->fdname
= mfree(s
->fdname
);
1692 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1694 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1698 if (!fdname_is_valid(p
)) {
1699 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1703 return free_and_replace(s
->fdname
, p
);
1706 int config_parse_service_sockets(
1708 const char *filename
,
1710 const char *section
,
1711 unsigned section_line
,
1729 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1731 r
= extract_first_word(&p
, &word
, NULL
, 0);
1737 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1741 r
= unit_name_printf(UNIT(s
), word
, &k
);
1743 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1747 if (!endswith(k
, ".socket")) {
1748 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1752 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1754 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1756 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1758 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1764 int config_parse_bus_name(
1766 const char *filename
,
1768 const char *section
,
1769 unsigned section_line
,
1776 _cleanup_free_
char *k
= NULL
;
1785 r
= unit_full_printf(u
, rvalue
, &k
);
1787 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1791 if (!service_name_is_valid(k
)) {
1792 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1796 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1799 int config_parse_service_timeout(
1801 const char *filename
,
1803 const char *section
,
1804 unsigned section_line
,
1811 Service
*s
= userdata
;
1820 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1822 r
= parse_sec(rvalue
, &usec
);
1824 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1828 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1829 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1830 * all other timeouts. */
1832 usec
= USEC_INFINITY
;
1834 if (!streq(lvalue
, "TimeoutStopSec")) {
1835 s
->start_timeout_defined
= true;
1836 s
->timeout_start_usec
= usec
;
1839 if (!streq(lvalue
, "TimeoutStartSec"))
1840 s
->timeout_stop_usec
= usec
;
1845 int config_parse_sec_fix_0(
1847 const char *filename
,
1849 const char *section
,
1850 unsigned section_line
,
1857 usec_t
*usec
= data
;
1865 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1866 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1869 r
= parse_sec(rvalue
, usec
);
1871 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1876 *usec
= USEC_INFINITY
;
1881 int config_parse_user_group(
1883 const char *filename
,
1885 const char *section
,
1886 unsigned section_line
,
1893 char **user
= data
, *n
;
1902 if (isempty(rvalue
))
1905 _cleanup_free_
char *k
= NULL
;
1907 r
= unit_full_printf(u
, rvalue
, &k
);
1909 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1913 if (!valid_user_group_name_or_id(k
)) {
1914 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID, ignoring: %s", k
);
1928 int config_parse_user_group_strv(
1930 const char *filename
,
1932 const char *section
,
1933 unsigned section_line
,
1940 char ***users
= data
;
1950 if (isempty(rvalue
)) {
1953 empty
= new0(char*, 1);
1965 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1967 r
= extract_first_word(&p
, &word
, NULL
, 0);
1973 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
1977 r
= unit_full_printf(u
, word
, &k
);
1979 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", word
);
1983 if (!valid_user_group_name_or_id(k
)) {
1984 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID, ignoring: %s", k
);
1988 r
= strv_push(users
, k
);
1998 int config_parse_busname_service(
2000 const char *filename
,
2002 const char *section
,
2003 unsigned section_line
,
2010 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2014 _cleanup_free_
char *p
= NULL
;
2021 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
2023 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2027 if (!endswith(p
, ".service")) {
2028 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
2032 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
2034 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
2038 unit_ref_set(&n
->service
, x
);
2043 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
2045 int config_parse_bus_policy(
2047 const char *filename
,
2049 const char *section
,
2050 unsigned section_line
,
2057 _cleanup_free_ BusNamePolicy
*p
= NULL
;
2058 _cleanup_free_
char *id_str
= NULL
;
2059 BusName
*busname
= data
;
2067 p
= new0(BusNamePolicy
, 1);
2071 if (streq(lvalue
, "AllowUser"))
2072 p
->type
= BUSNAME_POLICY_TYPE_USER
;
2073 else if (streq(lvalue
, "AllowGroup"))
2074 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
2076 assert_not_reached("Unknown lvalue");
2078 id_str
= strdup(rvalue
);
2082 access_str
= strpbrk(id_str
, WHITESPACE
);
2084 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy value '%s'", rvalue
);
2090 access_str
+= strspn(access_str
, WHITESPACE
);
2092 p
->access
= bus_policy_access_from_string(access_str
);
2093 if (p
->access
< 0) {
2094 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy access type '%s'", access_str
);
2101 LIST_PREPEND(policy
, busname
->policy
, p
);
2107 int config_parse_working_directory(
2109 const char *filename
,
2111 const char *section
,
2112 unsigned section_line
,
2119 ExecContext
*c
= data
;
2130 if (rvalue
[0] == '-') {
2136 if (streq(rvalue
, "~")) {
2137 c
->working_directory_home
= true;
2138 c
->working_directory
= mfree(c
->working_directory
);
2140 _cleanup_free_
char *k
= NULL
;
2142 r
= unit_full_printf(u
, rvalue
, &k
);
2144 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
2148 path_kill_slashes(k
);
2150 if (!utf8_is_valid(k
)) {
2151 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2155 if (!path_is_absolute(k
)) {
2156 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
2160 free_and_replace(c
->working_directory
, k
);
2162 c
->working_directory_home
= false;
2165 c
->working_directory_missing_ok
= missing_ok
;
2169 int config_parse_unit_env_file(const char *unit
,
2170 const char *filename
,
2172 const char *section
,
2173 unsigned section_line
,
2182 _cleanup_free_
char *n
= NULL
;
2190 if (isempty(rvalue
)) {
2191 /* Empty assignment frees the list */
2192 *env
= strv_free(*env
);
2196 r
= unit_full_printf(u
, rvalue
, &n
);
2198 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2202 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2203 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2207 r
= strv_extend(env
, n
);
2214 int config_parse_environ(const char *unit
,
2215 const char *filename
,
2217 const char *section
,
2218 unsigned section_line
,
2235 if (isempty(rvalue
)) {
2236 /* Empty assignment resets the list */
2237 *env
= strv_free(*env
);
2241 for (p
= rvalue
;; ) {
2242 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2244 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2250 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2251 "Invalid syntax, ignoring: %s", rvalue
);
2256 r
= unit_full_printf(u
, word
, &k
);
2258 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2259 "Failed to resolve specifiers, ignoring: %s", k
);
2267 if (!env_assignment_is_valid(k
)) {
2268 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2269 "Invalid environment assignment, ignoring: %s", k
);
2273 r
= strv_env_replace(env
, k
);
2280 int config_parse_pass_environ(const char *unit
,
2281 const char *filename
,
2283 const char *section
,
2284 unsigned section_line
,
2291 const char *whole_rvalue
= rvalue
;
2292 char*** passenv
= data
;
2293 _cleanup_strv_free_
char **n
= NULL
;
2294 size_t nlen
= 0, nbufsize
= 0;
2302 if (isempty(rvalue
)) {
2303 /* Empty assignment resets the list */
2304 *passenv
= strv_free(*passenv
);
2309 _cleanup_free_
char *word
= NULL
;
2311 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2317 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2318 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2322 if (!env_name_is_valid(word
)) {
2323 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2324 "Invalid environment name for %s, ignoring: %s", lvalue
, word
);
2328 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2336 r
= strv_extend_strv(passenv
, n
, true);
2344 int config_parse_ip_tos(const char *unit
,
2345 const char *filename
,
2347 const char *section
,
2348 unsigned section_line
,
2355 int *ip_tos
= data
, x
;
2362 x
= ip_tos_from_string(rvalue
);
2364 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2372 int config_parse_unit_condition_path(
2374 const char *filename
,
2376 const char *section
,
2377 unsigned section_line
,
2384 _cleanup_free_
char *p
= NULL
;
2385 Condition
**list
= data
, *c
;
2386 ConditionType t
= ltype
;
2387 bool trigger
, negate
;
2396 if (isempty(rvalue
)) {
2397 /* Empty assignment resets the list */
2398 *list
= condition_free_list(*list
);
2402 trigger
= rvalue
[0] == '|';
2406 negate
= rvalue
[0] == '!';
2410 r
= unit_full_printf(u
, rvalue
, &p
);
2412 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2416 if (!path_is_absolute(p
)) {
2417 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2421 c
= condition_new(t
, p
, trigger
, negate
);
2425 LIST_PREPEND(conditions
, *list
, c
);
2429 int config_parse_unit_condition_string(
2431 const char *filename
,
2433 const char *section
,
2434 unsigned section_line
,
2441 _cleanup_free_
char *s
= NULL
;
2442 Condition
**list
= data
, *c
;
2443 ConditionType t
= ltype
;
2444 bool trigger
, negate
;
2453 if (isempty(rvalue
)) {
2454 /* Empty assignment resets the list */
2455 *list
= condition_free_list(*list
);
2459 trigger
= rvalue
[0] == '|';
2463 negate
= rvalue
[0] == '!';
2467 r
= unit_full_printf(u
, rvalue
, &s
);
2469 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2473 c
= condition_new(t
, s
, trigger
, negate
);
2477 LIST_PREPEND(conditions
, *list
, c
);
2481 int config_parse_unit_condition_null(
2483 const char *filename
,
2485 const char *section
,
2486 unsigned section_line
,
2493 Condition
**list
= data
, *c
;
2494 bool trigger
, negate
;
2502 if (isempty(rvalue
)) {
2503 /* Empty assignment resets the list */
2504 *list
= condition_free_list(*list
);
2508 trigger
= rvalue
[0] == '|';
2512 negate
= rvalue
[0] == '!';
2516 b
= parse_boolean(rvalue
);
2518 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2525 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2529 LIST_PREPEND(conditions
, *list
, c
);
2533 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2534 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2536 int config_parse_unit_requires_mounts_for(
2538 const char *filename
,
2540 const char *section
,
2541 unsigned section_line
,
2557 for (p
= rvalue
;; ) {
2558 _cleanup_free_
char *word
= NULL
;
2560 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2566 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2567 "Invalid syntax, ignoring: %s", rvalue
);
2571 if (!utf8_is_valid(word
)) {
2572 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2576 r
= unit_require_mounts_for(u
, word
);
2578 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", word
);
2584 int config_parse_documentation(const char *unit
,
2585 const char *filename
,
2587 const char *section
,
2588 unsigned section_line
,
2604 if (isempty(rvalue
)) {
2605 /* Empty assignment resets the list */
2606 u
->documentation
= strv_free(u
->documentation
);
2610 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2611 rvalue
, data
, userdata
);
2615 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2617 if (documentation_url_is_valid(*a
))
2620 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2632 static int syscall_filter_parse_one(
2634 const char *filename
,
2643 const SyscallFilterSet
*set
;
2646 set
= syscall_filter_set_find(t
);
2649 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Don't know system call group, ignoring: %s", t
);
2653 NULSTR_FOREACH(i
, set
->value
) {
2654 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, i
, false);
2661 id
= seccomp_syscall_resolve_name(t
);
2662 if (id
== __NR_SCMP_ERROR
) {
2664 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2668 /* If we previously wanted to forbid a syscall and now
2669 * we want to allow it, then remove it from the list
2671 if (!invert
== c
->syscall_whitelist
) {
2672 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2678 (void) set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2684 int config_parse_syscall_filter(
2686 const char *filename
,
2688 const char *section
,
2689 unsigned section_line
,
2696 ExecContext
*c
= data
;
2698 bool invert
= false;
2707 if (isempty(rvalue
)) {
2708 /* Empty assignment resets the list */
2709 c
->syscall_filter
= set_free(c
->syscall_filter
);
2710 c
->syscall_whitelist
= false;
2714 if (rvalue
[0] == '~') {
2719 if (!c
->syscall_filter
) {
2720 c
->syscall_filter
= set_new(NULL
);
2721 if (!c
->syscall_filter
)
2725 /* Allow everything but the ones listed */
2726 c
->syscall_whitelist
= false;
2728 /* Allow nothing but the ones listed */
2729 c
->syscall_whitelist
= true;
2731 /* Accept default syscalls if we are on a whitelist */
2732 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, false, "@default", false);
2740 _cleanup_free_
char *word
= NULL
;
2742 r
= extract_first_word(&p
, &word
, NULL
, 0);
2748 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2752 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, word
, true);
2760 int config_parse_syscall_archs(
2762 const char *filename
,
2764 const char *section
,
2765 unsigned section_line
,
2776 if (isempty(rvalue
)) {
2777 *archs
= set_free(*archs
);
2781 r
= set_ensure_allocated(archs
, NULL
);
2785 for (p
= rvalue
;;) {
2786 _cleanup_free_
char *word
= NULL
;
2789 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2795 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2796 "Invalid syntax, ignoring: %s", rvalue
);
2800 r
= seccomp_arch_from_string(word
, &a
);
2802 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2803 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
2807 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2813 int config_parse_syscall_errno(
2815 const char *filename
,
2817 const char *section
,
2818 unsigned section_line
,
2825 ExecContext
*c
= data
;
2832 if (isempty(rvalue
)) {
2833 /* Empty assignment resets to KILL */
2834 c
->syscall_errno
= 0;
2838 e
= errno_from_name(rvalue
);
2840 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2844 c
->syscall_errno
= e
;
2848 int config_parse_address_families(
2850 const char *filename
,
2852 const char *section
,
2853 unsigned section_line
,
2860 ExecContext
*c
= data
;
2861 bool invert
= false;
2869 if (isempty(rvalue
)) {
2870 /* Empty assignment resets the list */
2871 c
->address_families
= set_free(c
->address_families
);
2872 c
->address_families_whitelist
= false;
2876 if (rvalue
[0] == '~') {
2881 if (!c
->address_families
) {
2882 c
->address_families
= set_new(NULL
);
2883 if (!c
->address_families
)
2886 c
->address_families_whitelist
= !invert
;
2889 for (p
= rvalue
;;) {
2890 _cleanup_free_
char *word
= NULL
;
2893 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2899 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2900 "Invalid syntax, ignoring: %s", rvalue
);
2904 af
= af_from_name(word
);
2906 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2907 "Failed to parse address family \"%s\", ignoring: %m", word
);
2911 /* If we previously wanted to forbid an address family and now
2912 * we want to allow it, then just remove it from the list.
2914 if (!invert
== c
->address_families_whitelist
) {
2915 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2919 set_remove(c
->address_families
, INT_TO_PTR(af
));
2923 int config_parse_restrict_namespaces(
2925 const char *filename
,
2927 const char *section
,
2928 unsigned section_line
,
2935 ExecContext
*c
= data
;
2936 bool invert
= false;
2939 if (isempty(rvalue
)) {
2940 /* Reset to the default. */
2941 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2945 if (rvalue
[0] == '~') {
2950 r
= parse_boolean(rvalue
);
2952 c
->restrict_namespaces
= 0;
2954 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2956 /* Not a boolean argument, in this case it's a list of namespace types. */
2958 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
2960 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
2966 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
2972 int config_parse_unit_slice(
2974 const char *filename
,
2976 const char *section
,
2977 unsigned section_line
,
2984 _cleanup_free_
char *k
= NULL
;
2985 Unit
*u
= userdata
, *slice
= NULL
;
2993 r
= unit_name_printf(u
, rvalue
, &k
);
2995 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2999 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
3001 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
3005 r
= unit_set_slice(u
, slice
);
3007 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
3014 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
3016 int config_parse_cpu_weight(
3018 const char *filename
,
3020 const char *section
,
3021 unsigned section_line
,
3028 uint64_t *weight
= data
;
3035 r
= cg_weight_parse(rvalue
, weight
);
3037 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
3044 int config_parse_cpu_shares(
3046 const char *filename
,
3048 const char *section
,
3049 unsigned section_line
,
3056 uint64_t *shares
= data
;
3063 r
= cg_cpu_shares_parse(rvalue
, shares
);
3065 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
3072 int config_parse_cpu_quota(
3074 const char *filename
,
3076 const char *section
,
3077 unsigned section_line
,
3084 CGroupContext
*c
= data
;
3091 if (isempty(rvalue
)) {
3092 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3096 r
= parse_percent_unbounded(rvalue
);
3098 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3102 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3106 int config_parse_memory_limit(
3108 const char *filename
,
3110 const char *section
,
3111 unsigned section_line
,
3118 CGroupContext
*c
= data
;
3119 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3122 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3124 r
= parse_percent(rvalue
);
3126 r
= parse_size(rvalue
, 1024, &bytes
);
3128 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3132 bytes
= physical_memory_scale(r
, 100U);
3134 if (bytes
<= 0 || bytes
>= UINT64_MAX
) {
3135 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3140 if (streq(lvalue
, "MemoryLow"))
3141 c
->memory_low
= bytes
;
3142 else if (streq(lvalue
, "MemoryHigh"))
3143 c
->memory_high
= bytes
;
3144 else if (streq(lvalue
, "MemoryMax"))
3145 c
->memory_max
= bytes
;
3146 else if (streq(lvalue
, "MemorySwapMax"))
3147 c
->memory_swap_max
= bytes
;
3148 else if (streq(lvalue
, "MemoryLimit"))
3149 c
->memory_limit
= bytes
;
3156 int config_parse_tasks_max(
3158 const char *filename
,
3160 const char *section
,
3161 unsigned section_line
,
3168 uint64_t *tasks_max
= data
, v
;
3172 if (isempty(rvalue
)) {
3173 *tasks_max
= u
->manager
->default_tasks_max
;
3177 if (streq(rvalue
, "infinity")) {
3178 *tasks_max
= CGROUP_LIMIT_MAX
;
3182 r
= parse_percent(rvalue
);
3184 r
= safe_atou64(rvalue
, &v
);
3186 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3190 v
= system_tasks_max_scale(r
, 100U);
3192 if (v
<= 0 || v
>= UINT64_MAX
) {
3193 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3201 int config_parse_device_allow(
3203 const char *filename
,
3205 const char *section
,
3206 unsigned section_line
,
3213 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3214 CGroupContext
*c
= data
;
3215 CGroupDeviceAllow
*a
;
3216 const char *m
= NULL
;
3220 if (isempty(rvalue
)) {
3221 while (c
->device_allow
)
3222 cgroup_context_free_device_allow(c
, c
->device_allow
);
3227 r
= unit_full_printf(userdata
, rvalue
, &t
);
3229 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3230 "Failed to resolve specifiers in %s, ignoring: %m",
3234 n
= strcspn(t
, WHITESPACE
);
3236 path
= strndup(t
, n
);
3240 if (!is_deviceallow_pattern(path
)) {
3241 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3245 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3249 if (!in_charset(m
, "rwm")) {
3250 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3254 a
= new0(CGroupDeviceAllow
, 1);
3260 a
->r
= !!strchr(m
, 'r');
3261 a
->w
= !!strchr(m
, 'w');
3262 a
->m
= !!strchr(m
, 'm');
3264 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3268 int config_parse_io_weight(
3270 const char *filename
,
3272 const char *section
,
3273 unsigned section_line
,
3280 uint64_t *weight
= data
;
3287 r
= cg_weight_parse(rvalue
, weight
);
3289 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3296 int config_parse_io_device_weight(
3298 const char *filename
,
3300 const char *section
,
3301 unsigned section_line
,
3308 _cleanup_free_
char *path
= NULL
;
3309 CGroupIODeviceWeight
*w
;
3310 CGroupContext
*c
= data
;
3320 if (isempty(rvalue
)) {
3321 while (c
->io_device_weights
)
3322 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3327 n
= strcspn(rvalue
, WHITESPACE
);
3328 weight
= rvalue
+ n
;
3329 weight
+= strspn(weight
, WHITESPACE
);
3331 if (isempty(weight
)) {
3332 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3336 path
= strndup(rvalue
, n
);
3340 if (!path_startswith(path
, "/dev")) {
3341 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3345 r
= cg_weight_parse(weight
, &u
);
3347 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3351 assert(u
!= CGROUP_WEIGHT_INVALID
);
3353 w
= new0(CGroupIODeviceWeight
, 1);
3362 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3366 int config_parse_io_limit(
3368 const char *filename
,
3370 const char *section
,
3371 unsigned section_line
,
3378 _cleanup_free_
char *path
= NULL
;
3379 CGroupIODeviceLimit
*l
= NULL
, *t
;
3380 CGroupContext
*c
= data
;
3381 CGroupIOLimitType type
;
3391 type
= cgroup_io_limit_type_from_string(lvalue
);
3394 if (isempty(rvalue
)) {
3395 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3396 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3400 n
= strcspn(rvalue
, WHITESPACE
);
3402 limit
+= strspn(limit
, WHITESPACE
);
3405 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3409 path
= strndup(rvalue
, n
);
3413 if (!path_startswith(path
, "/dev")) {
3414 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3418 if (streq("infinity", limit
)) {
3419 num
= CGROUP_LIMIT_MAX
;
3421 r
= parse_size(limit
, 1000, &num
);
3422 if (r
< 0 || num
<= 0) {
3423 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3428 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3429 if (path_equal(path
, t
->path
)) {
3436 CGroupIOLimitType ttype
;
3438 l
= new0(CGroupIODeviceLimit
, 1);
3444 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3445 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3447 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3450 l
->limits
[type
] = num
;
3455 int config_parse_blockio_weight(
3457 const char *filename
,
3459 const char *section
,
3460 unsigned section_line
,
3467 uint64_t *weight
= data
;
3474 r
= cg_blkio_weight_parse(rvalue
, weight
);
3476 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3483 int config_parse_blockio_device_weight(
3485 const char *filename
,
3487 const char *section
,
3488 unsigned section_line
,
3495 _cleanup_free_
char *path
= NULL
;
3496 CGroupBlockIODeviceWeight
*w
;
3497 CGroupContext
*c
= data
;
3507 if (isempty(rvalue
)) {
3508 while (c
->blockio_device_weights
)
3509 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3514 n
= strcspn(rvalue
, WHITESPACE
);
3515 weight
= rvalue
+ n
;
3516 weight
+= strspn(weight
, WHITESPACE
);
3518 if (isempty(weight
)) {
3519 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3523 path
= strndup(rvalue
, n
);
3527 if (!path_startswith(path
, "/dev")) {
3528 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3532 r
= cg_blkio_weight_parse(weight
, &u
);
3534 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3538 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3540 w
= new0(CGroupBlockIODeviceWeight
, 1);
3549 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3553 int config_parse_blockio_bandwidth(
3555 const char *filename
,
3557 const char *section
,
3558 unsigned section_line
,
3565 _cleanup_free_
char *path
= NULL
;
3566 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3567 CGroupContext
*c
= data
;
3568 const char *bandwidth
;
3578 read
= streq("BlockIOReadBandwidth", lvalue
);
3580 if (isempty(rvalue
)) {
3581 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3582 b
->rbps
= CGROUP_LIMIT_MAX
;
3583 b
->wbps
= CGROUP_LIMIT_MAX
;
3588 n
= strcspn(rvalue
, WHITESPACE
);
3589 bandwidth
= rvalue
+ n
;
3590 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3593 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3597 path
= strndup(rvalue
, n
);
3601 if (!path_startswith(path
, "/dev")) {
3602 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3606 r
= parse_size(bandwidth
, 1000, &bytes
);
3607 if (r
< 0 || bytes
<= 0) {
3608 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3612 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3613 if (path_equal(path
, t
->path
)) {
3620 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3626 b
->rbps
= CGROUP_LIMIT_MAX
;
3627 b
->wbps
= CGROUP_LIMIT_MAX
;
3629 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3640 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3642 int config_parse_job_mode_isolate(
3644 const char *filename
,
3646 const char *section
,
3647 unsigned section_line
,
3661 r
= parse_boolean(rvalue
);
3663 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3667 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3671 int config_parse_runtime_directory(
3673 const char *filename
,
3675 const char *section
,
3676 unsigned section_line
,
3693 if (isempty(rvalue
)) {
3694 /* Empty assignment resets the list */
3695 *rt
= strv_free(*rt
);
3699 for (p
= rvalue
;;) {
3700 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3702 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3708 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3709 "Invalid syntax, ignoring: %s", rvalue
);
3713 r
= unit_name_printf(u
, word
, &k
);
3715 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3716 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
3720 if (!filename_is_valid(k
)) {
3721 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3722 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3726 r
= strv_push(rt
, k
);
3733 int config_parse_set_status(
3735 const char *filename
,
3737 const char *section
,
3738 unsigned section_line
,
3746 const char *word
, *state
;
3748 ExitStatusSet
*status_set
= data
;
3755 /* Empty assignment resets the list */
3756 if (isempty(rvalue
)) {
3757 exit_status_set_free(status_set
);
3761 FOREACH_WORD(word
, l
, rvalue
, state
) {
3762 _cleanup_free_
char *temp
;
3766 temp
= strndup(word
, l
);
3770 r
= safe_atoi(temp
, &val
);
3772 val
= signal_from_string_try_harder(temp
);
3775 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3778 set
= &status_set
->signal
;
3780 if (val
< 0 || val
> 255) {
3781 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3784 set
= &status_set
->status
;
3787 r
= set_ensure_allocated(set
, NULL
);
3791 r
= set_put(*set
, INT_TO_PTR(val
));
3793 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3797 if (!isempty(state
))
3798 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3803 int config_parse_namespace_path_strv(
3805 const char *filename
,
3807 const char *section
,
3808 unsigned section_line
,
3825 if (isempty(rvalue
)) {
3826 /* Empty assignment resets the list */
3827 *sv
= strv_free(*sv
);
3831 prev
= cur
= rvalue
;
3833 _cleanup_free_
char *word
= NULL
;
3836 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3842 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage, ignoring: %s", prev
);
3846 if (!utf8_is_valid(word
)) {
3847 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3852 offset
= word
[0] == '-';
3853 if (!path_is_absolute(word
+ offset
)) {
3854 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", word
);
3859 path_kill_slashes(word
+ offset
);
3861 r
= strv_push(sv
, word
);
3872 int config_parse_no_new_privileges(
3874 const char *filename
,
3876 const char *section
,
3877 unsigned section_line
,
3884 ExecContext
*c
= data
;
3892 k
= parse_boolean(rvalue
);
3894 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3898 c
->no_new_privileges
= k
;
3899 c
->no_new_privileges_set
= true;
3904 int config_parse_protect_home(
3906 const char *filename
,
3908 const char *section
,
3909 unsigned section_line
,
3916 ExecContext
*c
= data
;
3924 /* Our enum shall be a superset of booleans, hence first try
3925 * to parse as boolean, and then as enum */
3927 k
= parse_boolean(rvalue
);
3929 c
->protect_home
= PROTECT_HOME_YES
;
3931 c
->protect_home
= PROTECT_HOME_NO
;
3935 h
= protect_home_from_string(rvalue
);
3937 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
3941 c
->protect_home
= h
;
3947 int config_parse_protect_system(
3949 const char *filename
,
3951 const char *section
,
3952 unsigned section_line
,
3959 ExecContext
*c
= data
;
3967 /* Our enum shall be a superset of booleans, hence first try
3968 * to parse as boolean, and then as enum */
3970 k
= parse_boolean(rvalue
);
3972 c
->protect_system
= PROTECT_SYSTEM_YES
;
3974 c
->protect_system
= PROTECT_SYSTEM_NO
;
3978 s
= protect_system_from_string(rvalue
);
3980 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
3984 c
->protect_system
= s
;
3990 #define FOLLOW_MAX 8
3992 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4003 /* This will update the filename pointer if the loaded file is
4004 * reached by a symlink. The old string will be freed. */
4007 char *target
, *name
;
4009 if (c
++ >= FOLLOW_MAX
)
4012 path_kill_slashes(*filename
);
4014 /* Add the file name we are currently looking at to
4015 * the names of this unit, but only if it is a valid
4017 name
= basename(*filename
);
4018 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4020 id
= set_get(names
, name
);
4026 r
= set_consume(names
, id
);
4032 /* Try to open the file name, but don't if its a symlink */
4033 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4040 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4041 r
= readlink_and_make_absolute(*filename
, &target
);
4049 f
= fdopen(fd
, "re");
4061 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4069 /* Let's try to add in all symlink names we found */
4070 while ((k
= set_steal_first(names
))) {
4072 /* First try to merge in the other name into our
4074 r
= unit_merge_by_name(*u
, k
);
4078 /* Hmm, we couldn't merge the other unit into
4079 * ours? Then let's try it the other way
4082 /* If the symlink name we are looking at is unit template, then
4083 we must search for instance of this template */
4084 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4085 _cleanup_free_
char *instance
= NULL
;
4087 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4091 other
= manager_get_unit((*u
)->manager
, instance
);
4093 other
= manager_get_unit((*u
)->manager
, k
);
4098 r
= unit_merge(other
, *u
);
4101 return merge_by_names(u
, names
, NULL
);
4109 unit_choose_id(*u
, id
);
4117 static int load_from_path(Unit
*u
, const char *path
) {
4118 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4119 _cleanup_fclose_
FILE *f
= NULL
;
4120 _cleanup_free_
char *filename
= NULL
;
4129 symlink_names
= set_new(&string_hash_ops
);
4133 if (path_is_absolute(path
)) {
4135 filename
= strdup(path
);
4139 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4141 filename
= mfree(filename
);
4149 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4151 /* Instead of opening the path right away, we manually
4152 * follow all symlinks and add their name to our unit
4153 * name set while doing so */
4154 filename
= path_make_absolute(path
, *p
);
4158 if (u
->manager
->unit_path_cache
&&
4159 !set_get(u
->manager
->unit_path_cache
, filename
))
4162 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4165 filename
= mfree(filename
);
4167 /* ENOENT means that the file is missing or is a dangling symlink.
4168 * ENOTDIR means that one of paths we expect to be is a directory
4169 * is not a directory, we should just ignore that.
4170 * EACCES means that the directory or file permissions are wrong.
4173 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4174 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4177 /* Empty the symlink names for the next run */
4178 set_clear_free(symlink_names
);
4183 /* Hmm, no suitable file found? */
4186 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4187 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4192 r
= merge_by_names(&merged
, symlink_names
, id
);
4197 u
->load_state
= UNIT_MERGED
;
4201 if (fstat(fileno(f
), &st
) < 0)
4204 if (null_or_empty(&st
)) {
4205 u
->load_state
= UNIT_MASKED
;
4206 u
->fragment_mtime
= 0;
4208 u
->load_state
= UNIT_LOADED
;
4209 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4211 /* Now, parse the file contents */
4212 r
= config_parse(u
->id
, filename
, f
,
4213 UNIT_VTABLE(u
)->sections
,
4214 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4215 false, true, false, u
);
4220 free(u
->fragment_path
);
4221 u
->fragment_path
= filename
;
4224 if (u
->source_path
) {
4225 if (stat(u
->source_path
, &st
) >= 0)
4226 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4228 u
->source_mtime
= 0;
4234 int unit_load_fragment(Unit
*u
) {
4240 assert(u
->load_state
== UNIT_STUB
);
4244 u
->load_state
= UNIT_LOADED
;
4248 /* First, try to find the unit under its id. We always look
4249 * for unit files in the default directories, to make it easy
4250 * to override things by placing things in /etc/systemd/system */
4251 r
= load_from_path(u
, u
->id
);
4255 /* Try to find an alias we can load this with */
4256 if (u
->load_state
== UNIT_STUB
) {
4257 SET_FOREACH(t
, u
->names
, i
) {
4262 r
= load_from_path(u
, t
);
4266 if (u
->load_state
!= UNIT_STUB
)
4271 /* And now, try looking for it under the suggested (originally linked) path */
4272 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4274 r
= load_from_path(u
, u
->fragment_path
);
4278 if (u
->load_state
== UNIT_STUB
)
4279 /* Hmm, this didn't work? Then let's get rid
4280 * of the fragment path stored for us, so that
4281 * we don't point to an invalid location. */
4282 u
->fragment_path
= mfree(u
->fragment_path
);
4285 /* Look for a template */
4286 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4287 _cleanup_free_
char *k
= NULL
;
4289 r
= unit_name_template(u
->id
, &k
);
4293 r
= load_from_path(u
, k
);
4297 if (u
->load_state
== UNIT_STUB
) {
4298 SET_FOREACH(t
, u
->names
, i
) {
4299 _cleanup_free_
char *z
= NULL
;
4304 r
= unit_name_template(t
, &z
);
4308 r
= load_from_path(u
, z
);
4312 if (u
->load_state
!= UNIT_STUB
)
4321 void unit_dump_config_items(FILE *f
) {
4322 static const struct {
4323 const ConfigParserCallback callback
;
4326 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
4327 { config_parse_warn_compat
, "NOTSUPPORTED" },
4329 { config_parse_int
, "INTEGER" },
4330 { config_parse_unsigned
, "UNSIGNED" },
4331 { config_parse_iec_size
, "SIZE" },
4332 { config_parse_iec_uint64
, "SIZE" },
4333 { config_parse_si_size
, "SIZE" },
4334 { config_parse_bool
, "BOOLEAN" },
4335 { config_parse_string
, "STRING" },
4336 { config_parse_path
, "PATH" },
4337 { config_parse_unit_path_printf
, "PATH" },
4338 { config_parse_strv
, "STRING [...]" },
4339 { config_parse_exec_nice
, "NICE" },
4340 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4341 { config_parse_exec_io_class
, "IOCLASS" },
4342 { config_parse_exec_io_priority
, "IOPRIORITY" },
4343 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4344 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4345 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4346 { config_parse_mode
, "MODE" },
4347 { config_parse_unit_env_file
, "FILE" },
4348 { config_parse_exec_output
, "OUTPUT" },
4349 { config_parse_exec_input
, "INPUT" },
4350 { config_parse_log_facility
, "FACILITY" },
4351 { config_parse_log_level
, "LEVEL" },
4352 { config_parse_exec_secure_bits
, "SECUREBITS" },
4353 { config_parse_capability_set
, "BOUNDINGSET" },
4354 { config_parse_limit
, "LIMIT" },
4355 { config_parse_unit_deps
, "UNIT [...]" },
4356 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4357 { config_parse_service_type
, "SERVICETYPE" },
4358 { config_parse_service_restart
, "SERVICERESTART" },
4359 #ifdef HAVE_SYSV_COMPAT
4360 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4362 { config_parse_kill_mode
, "KILLMODE" },
4363 { config_parse_signal
, "SIGNAL" },
4364 { config_parse_socket_listen
, "SOCKET [...]" },
4365 { config_parse_socket_bind
, "SOCKETBIND" },
4366 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4367 { config_parse_sec
, "SECONDS" },
4368 { config_parse_nsec
, "NANOSECONDS" },
4369 { config_parse_namespace_path_strv
, "PATH [...]" },
4370 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4371 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4372 { config_parse_unit_string_printf
, "STRING" },
4373 { config_parse_trigger_unit
, "UNIT" },
4374 { config_parse_timer
, "TIMER" },
4375 { config_parse_path_spec
, "PATH" },
4376 { config_parse_notify_access
, "ACCESS" },
4377 { config_parse_ip_tos
, "TOS" },
4378 { config_parse_unit_condition_path
, "CONDITION" },
4379 { config_parse_unit_condition_string
, "CONDITION" },
4380 { config_parse_unit_condition_null
, "CONDITION" },
4381 { config_parse_unit_slice
, "SLICE" },
4382 { config_parse_documentation
, "URL" },
4383 { config_parse_service_timeout
, "SECONDS" },
4384 { config_parse_emergency_action
, "ACTION" },
4385 { config_parse_set_status
, "STATUS" },
4386 { config_parse_service_sockets
, "SOCKETS" },
4387 { config_parse_environ
, "ENVIRON" },
4389 { config_parse_syscall_filter
, "SYSCALLS" },
4390 { config_parse_syscall_archs
, "ARCHS" },
4391 { config_parse_syscall_errno
, "ERRNO" },
4392 { config_parse_address_families
, "FAMILIES" },
4393 { config_parse_restrict_namespaces
, "NAMESPACES" },
4395 { config_parse_cpu_shares
, "SHARES" },
4396 { config_parse_cpu_weight
, "WEIGHT" },
4397 { config_parse_memory_limit
, "LIMIT" },
4398 { config_parse_device_allow
, "DEVICE" },
4399 { config_parse_device_policy
, "POLICY" },
4400 { config_parse_io_limit
, "LIMIT" },
4401 { config_parse_io_weight
, "WEIGHT" },
4402 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4403 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4404 { config_parse_blockio_weight
, "WEIGHT" },
4405 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4406 { config_parse_long
, "LONG" },
4407 { config_parse_socket_service
, "SERVICE" },
4409 { config_parse_exec_selinux_context
, "LABEL" },
4411 { config_parse_job_mode
, "MODE" },
4412 { config_parse_job_mode_isolate
, "BOOLEAN" },
4413 { config_parse_personality
, "PERSONALITY" },
4416 const char *prev
= NULL
;
4421 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4422 const char *rvalue
= "OTHER", *lvalue
;
4426 const ConfigPerfItem
*p
;
4428 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4430 dot
= strchr(i
, '.');
4431 lvalue
= dot
? dot
+ 1 : i
;
4435 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
4439 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
4442 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
4443 if (p
->parse
== table
[j
].callback
) {
4444 rvalue
= table
[j
].rvalue
;
4448 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);