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 "signal-util.h"
62 #include "stat-util.h"
63 #include "string-util.h"
65 #include "unit-name.h"
66 #include "unit-printf.h"
68 #include "user-util.h"
72 int config_parse_warn_compat(
77 unsigned section_line
,
83 Disabled reason
= ltype
;
86 case DISABLED_CONFIGURATION
:
87 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
88 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
91 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
92 "Support for option %s= has been removed and it is ignored", lvalue
);
94 case DISABLED_EXPERIMENTAL
:
95 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
96 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
103 int config_parse_unit_deps(
105 const char *filename
,
108 unsigned section_line
,
115 UnitDependency d
= ltype
;
125 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
128 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_RETAIN_ESCAPE
);
134 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
138 r
= unit_name_printf(u
, word
, &k
);
140 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
144 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
146 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
152 int config_parse_obsolete_unit_deps(
154 const char *filename
,
157 unsigned section_line
,
164 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
165 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue
, unit_dependency_to_string(ltype
));
167 return config_parse_unit_deps(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, data
, userdata
);
170 int config_parse_unit_string_printf(
172 const char *filename
,
175 unsigned section_line
,
182 _cleanup_free_
char *k
= NULL
;
191 r
= unit_full_printf(u
, rvalue
, &k
);
193 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
197 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
200 int config_parse_unit_strv_printf(
202 const char *filename
,
205 unsigned section_line
,
213 _cleanup_free_
char *k
= NULL
;
221 r
= unit_full_printf(u
, rvalue
, &k
);
223 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
227 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
230 int config_parse_unit_path_printf(
232 const char *filename
,
235 unsigned section_line
,
242 _cleanup_free_
char *k
= NULL
;
251 r
= unit_full_printf(u
, rvalue
, &k
);
253 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
257 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
260 int config_parse_unit_path_strv_printf(
262 const char *filename
,
265 unsigned section_line
,
283 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
285 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
291 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
292 "Invalid syntax, ignoring: %s", rvalue
);
296 r
= unit_full_printf(u
, word
, &k
);
298 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
299 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word
);
303 if (!utf8_is_valid(k
)) {
304 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
308 if (!path_is_absolute(k
)) {
309 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
310 "Symlink path is not absolute: %s", k
);
314 path_kill_slashes(k
);
323 int config_parse_socket_listen(const char *unit
,
324 const char *filename
,
327 unsigned section_line
,
334 _cleanup_free_ SocketPort
*p
= NULL
;
346 if (isempty(rvalue
)) {
347 /* An empty assignment removes all ports */
348 socket_free_ports(s
);
352 p
= new0(SocketPort
, 1);
356 if (ltype
!= SOCKET_SOCKET
) {
359 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
361 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
365 path_kill_slashes(p
->path
);
367 } else if (streq(lvalue
, "ListenNetlink")) {
368 _cleanup_free_
char *k
= NULL
;
370 p
->type
= SOCKET_SOCKET
;
371 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
373 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
377 r
= socket_address_parse_netlink(&p
->address
, k
);
379 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
384 _cleanup_free_
char *k
= NULL
;
386 p
->type
= SOCKET_SOCKET
;
387 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
389 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
393 r
= socket_address_parse_and_warn(&p
->address
, k
);
395 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
399 if (streq(lvalue
, "ListenStream"))
400 p
->address
.type
= SOCK_STREAM
;
401 else if (streq(lvalue
, "ListenDatagram"))
402 p
->address
.type
= SOCK_DGRAM
;
404 assert(streq(lvalue
, "ListenSequentialPacket"));
405 p
->address
.type
= SOCK_SEQPACKET
;
408 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
409 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
415 p
->auxiliary_fds
= NULL
;
416 p
->n_auxiliary_fds
= 0;
420 LIST_FIND_TAIL(port
, s
->ports
, tail
);
421 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
423 LIST_PREPEND(port
, s
->ports
, p
);
429 int config_parse_socket_protocol(const char *unit
,
430 const char *filename
,
433 unsigned section_line
,
448 if (streq(rvalue
, "udplite"))
449 s
->socket_protocol
= IPPROTO_UDPLITE
;
450 else if (streq(rvalue
, "sctp"))
451 s
->socket_protocol
= IPPROTO_SCTP
;
453 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Socket protocol not supported, ignoring: %s", rvalue
);
460 int config_parse_socket_bind(const char *unit
,
461 const char *filename
,
464 unsigned section_line
,
472 SocketAddressBindIPv6Only b
;
481 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
485 r
= parse_boolean(rvalue
);
487 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
491 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
493 s
->bind_ipv6_only
= b
;
498 int config_parse_exec_nice(
500 const char *filename
,
503 unsigned section_line
,
510 ExecContext
*c
= data
;
518 r
= parse_nice(rvalue
, &priority
);
521 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
523 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
534 int config_parse_exec_oom_score_adjust(const char* unit
,
535 const char *filename
,
538 unsigned section_line
,
545 ExecContext
*c
= data
;
553 r
= safe_atoi(rvalue
, &oa
);
555 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
559 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
560 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", rvalue
);
564 c
->oom_score_adjust
= oa
;
565 c
->oom_score_adjust_set
= true;
570 int config_parse_exec(
572 const char *filename
,
575 unsigned section_line
,
582 ExecCommand
**e
= data
;
594 rvalue
+= strspn(rvalue
, WHITESPACE
);
596 if (isempty(rvalue
)) {
597 /* An empty assignment resets the list */
598 *e
= exec_command_free_list(*e
);
604 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
605 bool separate_argv0
= false, ignore
= false, privileged
= false;
606 _cleanup_free_ ExecCommand
*nce
= NULL
;
607 _cleanup_strv_free_
char **n
= NULL
;
608 size_t nlen
= 0, nbufsize
= 0;
613 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
619 /* We accept an absolute path as first argument.
620 * If it's prefixed with - and the path doesn't exist,
621 * we ignore it instead of erroring out;
622 * if it's prefixed with @, we allow overriding of argv[0];
623 * and if it's prefixed with +, it will be run with full privileges */
624 if (*f
== '-' && !ignore
)
626 else if (*f
== '@' && !separate_argv0
)
627 separate_argv0
= true;
628 else if (*f
== '+' && !privileged
)
635 r
= unit_full_printf(u
, f
, &path
);
637 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", f
);
642 /* First word is either "-" or "@" with no command. */
643 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty path in command line, ignoring: \"%s\"", rvalue
);
646 if (!string_is_safe(path
)) {
647 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path contains special characters, ignoring: %s", rvalue
);
650 if (!path_is_absolute(path
)) {
651 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path is not absolute, ignoring: %s", rvalue
);
654 if (endswith(path
, "/")) {
655 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path specifies a directory, ignoring: %s", rvalue
);
659 if (!separate_argv0
) {
662 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
672 path_kill_slashes(path
);
674 while (!isempty(p
)) {
675 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
677 /* Check explicitly for an unquoted semicolon as
678 * command separator token. */
679 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
681 p
+= strspn(p
, WHITESPACE
);
686 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
687 * extract_first_word() would return the same for all of those. */
688 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
692 p
+= strspn(p
, WHITESPACE
);
694 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
705 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
711 r
= unit_full_printf(u
, word
, &resolved
);
713 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to resolve unit specifiers on %s, ignoring: %m", word
);
717 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
719 n
[nlen
++] = resolved
;
725 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
729 nce
= new0(ExecCommand
, 1);
735 nce
->ignore
= ignore
;
736 nce
->privileged
= privileged
;
738 exec_command_append_list(e
, nce
);
740 /* Do not _cleanup_free_ these. */
751 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
752 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
754 int config_parse_socket_bindtodevice(
756 const char *filename
,
759 unsigned section_line
,
774 if (rvalue
[0] && !streq(rvalue
, "*")) {
775 if (!ifname_valid(rvalue
)) {
776 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Interface name is invalid, ignoring: %s", rvalue
);
786 free(s
->bind_to_device
);
787 s
->bind_to_device
= n
;
792 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input literal specifier");
793 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output literal specifier");
795 int config_parse_exec_input(const char *unit
,
796 const char *filename
,
799 unsigned section_line
,
805 ExecContext
*c
= data
;
814 name
= startswith(rvalue
, "fd:");
816 /* Strip prefix and validate fd name */
817 if (!fdname_is_valid(name
)) {
818 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
821 c
->std_input
= EXEC_INPUT_NAMED_FD
;
822 r
= free_and_strdup(&c
->stdio_fdname
[STDIN_FILENO
], name
);
827 ExecInput ei
= exec_input_from_string(rvalue
);
828 if (ei
== _EXEC_INPUT_INVALID
)
829 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
836 int config_parse_exec_output(const char *unit
,
837 const char *filename
,
840 unsigned section_line
,
846 ExecContext
*c
= data
;
857 name
= startswith(rvalue
, "fd:");
859 /* Strip prefix and validate fd name */
860 if (!fdname_is_valid(name
)) {
861 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", name
);
864 eo
= EXEC_OUTPUT_NAMED_FD
;
866 eo
= exec_output_from_string(rvalue
);
867 if (eo
== _EXEC_OUTPUT_INVALID
) {
868 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
873 if (streq(lvalue
, "StandardOutput")) {
875 r
= free_and_strdup(&c
->stdio_fdname
[STDOUT_FILENO
], name
);
879 } else if (streq(lvalue
, "StandardError")) {
881 r
= free_and_strdup(&c
->stdio_fdname
[STDERR_FILENO
], name
);
886 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output property, ignoring: %s", lvalue
);
891 int config_parse_exec_io_class(const char *unit
,
892 const char *filename
,
895 unsigned section_line
,
902 ExecContext
*c
= data
;
910 x
= ioprio_class_from_string(rvalue
);
912 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
916 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
917 c
->ioprio_set
= true;
922 int config_parse_exec_io_priority(const char *unit
,
923 const char *filename
,
926 unsigned section_line
,
933 ExecContext
*c
= data
;
941 r
= safe_atoi(rvalue
, &i
);
942 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
943 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
947 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
948 c
->ioprio_set
= true;
953 int config_parse_exec_cpu_sched_policy(const char *unit
,
954 const char *filename
,
957 unsigned section_line
,
965 ExecContext
*c
= data
;
973 x
= sched_policy_from_string(rvalue
);
975 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
979 c
->cpu_sched_policy
= x
;
980 /* Moving to or from real-time policy? We need to adjust the priority */
981 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
982 c
->cpu_sched_set
= true;
987 int config_parse_exec_cpu_sched_prio(const char *unit
,
988 const char *filename
,
991 unsigned section_line
,
998 ExecContext
*c
= data
;
1006 r
= safe_atoi(rvalue
, &i
);
1008 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1012 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1013 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1014 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1016 if (i
< min
|| i
> max
) {
1017 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1021 c
->cpu_sched_priority
= i
;
1022 c
->cpu_sched_set
= true;
1027 int config_parse_exec_cpu_affinity(const char *unit
,
1028 const char *filename
,
1030 const char *section
,
1031 unsigned section_line
,
1038 ExecContext
*c
= data
;
1039 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1047 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1052 CPU_FREE(c
->cpuset
);
1055 /* An empty assignment resets the CPU list */
1061 c
->cpuset_ncpus
= ncpus
;
1066 int config_parse_exec_secure_bits(const char *unit
,
1067 const char *filename
,
1069 const char *section
,
1070 unsigned section_line
,
1077 ExecContext
*c
= data
;
1086 if (isempty(rvalue
)) {
1087 /* An empty assignment resets the field */
1092 for (p
= rvalue
;;) {
1093 _cleanup_free_
char *word
= NULL
;
1095 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1101 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1102 "Invalid syntax, ignoring: %s", rvalue
);
1106 if (streq(word
, "keep-caps"))
1107 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
1108 else if (streq(word
, "keep-caps-locked"))
1109 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
1110 else if (streq(word
, "no-setuid-fixup"))
1111 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
1112 else if (streq(word
, "no-setuid-fixup-locked"))
1113 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
1114 else if (streq(word
, "noroot"))
1115 c
->secure_bits
|= 1<<SECURE_NOROOT
;
1116 else if (streq(word
, "noroot-locked"))
1117 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
1119 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
1120 "Failed to parse secure bit \"%s\", ignoring.", word
);
1126 int config_parse_capability_set(
1128 const char *filename
,
1130 const char *section
,
1131 unsigned section_line
,
1138 uint64_t *capability_set
= data
;
1139 uint64_t sum
= 0, initial
= 0;
1140 bool invert
= false;
1148 if (rvalue
[0] == '~') {
1153 if (strcmp(lvalue
, "CapabilityBoundingSet") == 0)
1154 initial
= CAP_ALL
; /* initialized to all bits on */
1155 /* else "AmbientCapabilities" initialized to all bits off */
1159 _cleanup_free_
char *word
= NULL
;
1162 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1168 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word, ignoring: %s", rvalue
);
1172 cap
= capability_from_name(word
);
1174 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word
);
1178 sum
|= ((uint64_t) UINT64_C(1)) << (uint64_t) cap
;
1181 sum
= invert
? ~sum
: sum
;
1183 if (sum
== 0 || *capability_set
== initial
)
1184 /* "" or uninitialized data -> replace */
1185 *capability_set
= sum
;
1187 /* previous data -> merge */
1188 *capability_set
|= sum
;
1193 int config_parse_limit(
1195 const char *filename
,
1197 const char *section
,
1198 unsigned section_line
,
1205 struct rlimit
**rl
= data
, d
= {};
1213 r
= rlimit_parse(ltype
, rvalue
, &d
);
1215 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue
);
1219 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1226 rl
[ltype
] = newdup(struct rlimit
, &d
, 1);
1234 #ifdef HAVE_SYSV_COMPAT
1235 int config_parse_sysv_priority(const char *unit
,
1236 const char *filename
,
1238 const char *section
,
1239 unsigned section_line
,
1246 int *priority
= data
;
1254 r
= safe_atoi(rvalue
, &i
);
1255 if (r
< 0 || i
< 0) {
1256 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1260 *priority
= (int) i
;
1265 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1266 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1268 int config_parse_exec_mount_flags(
1270 const char *filename
,
1272 const char *section
,
1273 unsigned section_line
,
1281 ExecContext
*c
= data
;
1289 r
= mount_propagation_flags_from_string(rvalue
, &c
->mount_flags
);
1291 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring.", rvalue
);
1296 int config_parse_exec_selinux_context(
1298 const char *filename
,
1300 const char *section
,
1301 unsigned section_line
,
1308 ExecContext
*c
= data
;
1319 if (isempty(rvalue
)) {
1320 c
->selinux_context
= mfree(c
->selinux_context
);
1321 c
->selinux_context_ignore
= false;
1325 if (rvalue
[0] == '-') {
1331 r
= unit_full_printf(u
, rvalue
, &k
);
1333 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1337 free(c
->selinux_context
);
1338 c
->selinux_context
= k
;
1339 c
->selinux_context_ignore
= ignore
;
1344 int config_parse_exec_apparmor_profile(
1346 const char *filename
,
1348 const char *section
,
1349 unsigned section_line
,
1356 ExecContext
*c
= data
;
1367 if (isempty(rvalue
)) {
1368 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1369 c
->apparmor_profile_ignore
= false;
1373 if (rvalue
[0] == '-') {
1379 r
= unit_full_printf(u
, rvalue
, &k
);
1381 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1385 free(c
->apparmor_profile
);
1386 c
->apparmor_profile
= k
;
1387 c
->apparmor_profile_ignore
= ignore
;
1392 int config_parse_exec_smack_process_label(
1394 const char *filename
,
1396 const char *section
,
1397 unsigned section_line
,
1404 ExecContext
*c
= data
;
1415 if (isempty(rvalue
)) {
1416 c
->smack_process_label
= mfree(c
->smack_process_label
);
1417 c
->smack_process_label_ignore
= false;
1421 if (rvalue
[0] == '-') {
1427 r
= unit_full_printf(u
, rvalue
, &k
);
1429 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1433 free(c
->smack_process_label
);
1434 c
->smack_process_label
= k
;
1435 c
->smack_process_label_ignore
= ignore
;
1440 int config_parse_timer(const char *unit
,
1441 const char *filename
,
1443 const char *section
,
1444 unsigned section_line
,
1455 CalendarSpec
*c
= NULL
;
1457 _cleanup_free_
char *k
= NULL
;
1465 if (isempty(rvalue
)) {
1466 /* Empty assignment resets list */
1467 timer_free_values(t
);
1471 b
= timer_base_from_string(lvalue
);
1473 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1477 r
= unit_full_printf(u
, rvalue
, &k
);
1479 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1483 if (b
== TIMER_CALENDAR
) {
1484 if (calendar_spec_from_string(k
, &c
) < 0) {
1485 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1489 if (parse_sec(k
, &usec
) < 0) {
1490 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1495 v
= new0(TimerValue
, 1);
1497 calendar_spec_free(c
);
1503 v
->calendar_spec
= c
;
1505 LIST_PREPEND(value
, t
->values
, v
);
1510 int config_parse_trigger_unit(
1512 const char *filename
,
1514 const char *section
,
1515 unsigned section_line
,
1522 _cleanup_free_
char *p
= NULL
;
1532 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1533 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1537 r
= unit_name_printf(u
, rvalue
, &p
);
1539 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1543 type
= unit_name_to_type(p
);
1545 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1549 if (type
== u
->type
) {
1550 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1554 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1556 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1563 int config_parse_path_spec(const char *unit
,
1564 const char *filename
,
1566 const char *section
,
1567 unsigned section_line
,
1577 _cleanup_free_
char *k
= NULL
;
1585 if (isempty(rvalue
)) {
1586 /* Empty assignment clears list */
1591 b
= path_type_from_string(lvalue
);
1593 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1597 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1599 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1603 if (!path_is_absolute(k
)) {
1604 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1608 s
= new0(PathSpec
, 1);
1613 s
->path
= path_kill_slashes(k
);
1618 LIST_PREPEND(spec
, p
->specs
, s
);
1623 int config_parse_socket_service(
1625 const char *filename
,
1627 const char *section
,
1628 unsigned section_line
,
1635 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1636 _cleanup_free_
char *p
= NULL
;
1646 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1648 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1652 if (!endswith(p
, ".service")) {
1653 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1657 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1659 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1663 unit_ref_set(&s
->service
, x
);
1668 int config_parse_fdname(
1670 const char *filename
,
1672 const char *section
,
1673 unsigned section_line
,
1680 _cleanup_free_
char *p
= NULL
;
1689 if (isempty(rvalue
)) {
1690 s
->fdname
= mfree(s
->fdname
);
1694 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1696 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1700 if (!fdname_is_valid(p
)) {
1701 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1705 return free_and_replace(s
->fdname
, p
);
1708 int config_parse_service_sockets(
1710 const char *filename
,
1712 const char *section
,
1713 unsigned section_line
,
1731 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1733 r
= extract_first_word(&p
, &word
, NULL
, 0);
1739 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1743 r
= unit_name_printf(UNIT(s
), word
, &k
);
1745 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1749 if (!endswith(k
, ".socket")) {
1750 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1754 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1756 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1758 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1760 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1766 int config_parse_bus_name(
1768 const char *filename
,
1770 const char *section
,
1771 unsigned section_line
,
1778 _cleanup_free_
char *k
= NULL
;
1787 r
= unit_full_printf(u
, rvalue
, &k
);
1789 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1793 if (!service_name_is_valid(k
)) {
1794 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1798 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1801 int config_parse_service_timeout(
1803 const char *filename
,
1805 const char *section
,
1806 unsigned section_line
,
1813 Service
*s
= userdata
;
1822 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1824 r
= parse_sec(rvalue
, &usec
);
1826 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1830 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1831 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1832 * all other timeouts. */
1834 usec
= USEC_INFINITY
;
1836 if (!streq(lvalue
, "TimeoutStopSec")) {
1837 s
->start_timeout_defined
= true;
1838 s
->timeout_start_usec
= usec
;
1841 if (!streq(lvalue
, "TimeoutStartSec"))
1842 s
->timeout_stop_usec
= usec
;
1847 int config_parse_sec_fix_0(
1849 const char *filename
,
1851 const char *section
,
1852 unsigned section_line
,
1859 usec_t
*usec
= data
;
1867 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1868 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1871 r
= parse_sec(rvalue
, usec
);
1873 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1878 *usec
= USEC_INFINITY
;
1883 int config_parse_user_group(
1885 const char *filename
,
1887 const char *section
,
1888 unsigned section_line
,
1895 char **user
= data
, *n
;
1904 if (isempty(rvalue
))
1907 _cleanup_free_
char *k
= NULL
;
1909 r
= unit_full_printf(u
, rvalue
, &k
);
1911 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1915 if (!valid_user_group_name_or_id(k
)) {
1916 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID, ignoring: %s", k
);
1930 int config_parse_user_group_strv(
1932 const char *filename
,
1934 const char *section
,
1935 unsigned section_line
,
1942 char ***users
= data
;
1952 if (isempty(rvalue
)) {
1955 empty
= new0(char*, 1);
1967 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1969 r
= extract_first_word(&p
, &word
, NULL
, 0);
1975 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
1979 r
= unit_full_printf(u
, word
, &k
);
1981 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", word
);
1985 if (!valid_user_group_name_or_id(k
)) {
1986 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID, ignoring: %s", k
);
1990 r
= strv_push(users
, k
);
2000 int config_parse_busname_service(
2002 const char *filename
,
2004 const char *section
,
2005 unsigned section_line
,
2012 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2016 _cleanup_free_
char *p
= NULL
;
2023 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
2025 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2029 if (!endswith(p
, ".service")) {
2030 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
2034 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
2036 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
2040 unit_ref_set(&n
->service
, x
);
2045 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
2047 int config_parse_bus_policy(
2049 const char *filename
,
2051 const char *section
,
2052 unsigned section_line
,
2059 _cleanup_free_ BusNamePolicy
*p
= NULL
;
2060 _cleanup_free_
char *id_str
= NULL
;
2061 BusName
*busname
= data
;
2069 p
= new0(BusNamePolicy
, 1);
2073 if (streq(lvalue
, "AllowUser"))
2074 p
->type
= BUSNAME_POLICY_TYPE_USER
;
2075 else if (streq(lvalue
, "AllowGroup"))
2076 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
2078 assert_not_reached("Unknown lvalue");
2080 id_str
= strdup(rvalue
);
2084 access_str
= strpbrk(id_str
, WHITESPACE
);
2086 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy value '%s'", rvalue
);
2092 access_str
+= strspn(access_str
, WHITESPACE
);
2094 p
->access
= bus_policy_access_from_string(access_str
);
2095 if (p
->access
< 0) {
2096 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy access type '%s'", access_str
);
2103 LIST_PREPEND(policy
, busname
->policy
, p
);
2109 int config_parse_working_directory(
2111 const char *filename
,
2113 const char *section
,
2114 unsigned section_line
,
2121 ExecContext
*c
= data
;
2132 if (rvalue
[0] == '-') {
2138 if (streq(rvalue
, "~")) {
2139 c
->working_directory_home
= true;
2140 c
->working_directory
= mfree(c
->working_directory
);
2142 _cleanup_free_
char *k
= NULL
;
2144 r
= unit_full_printf(u
, rvalue
, &k
);
2146 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
2150 path_kill_slashes(k
);
2152 if (!utf8_is_valid(k
)) {
2153 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2157 if (!path_is_absolute(k
)) {
2158 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
2162 free_and_replace(c
->working_directory
, k
);
2164 c
->working_directory_home
= false;
2167 c
->working_directory_missing_ok
= missing_ok
;
2171 int config_parse_unit_env_file(const char *unit
,
2172 const char *filename
,
2174 const char *section
,
2175 unsigned section_line
,
2184 _cleanup_free_
char *n
= NULL
;
2192 if (isempty(rvalue
)) {
2193 /* Empty assignment frees the list */
2194 *env
= strv_free(*env
);
2198 r
= unit_full_printf(u
, rvalue
, &n
);
2200 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2204 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2205 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2209 r
= strv_extend(env
, n
);
2216 int config_parse_environ(const char *unit
,
2217 const char *filename
,
2219 const char *section
,
2220 unsigned section_line
,
2237 if (isempty(rvalue
)) {
2238 /* Empty assignment resets the list */
2239 *env
= strv_free(*env
);
2243 for (p
= rvalue
;; ) {
2244 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2246 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2252 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2253 "Invalid syntax, ignoring: %s", rvalue
);
2258 r
= unit_full_printf(u
, word
, &k
);
2260 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2261 "Failed to resolve specifiers, ignoring: %s", k
);
2269 if (!env_assignment_is_valid(k
)) {
2270 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2271 "Invalid environment assignment, ignoring: %s", k
);
2275 r
= strv_env_replace(env
, k
);
2282 int config_parse_pass_environ(const char *unit
,
2283 const char *filename
,
2285 const char *section
,
2286 unsigned section_line
,
2293 const char *whole_rvalue
= rvalue
;
2294 char*** passenv
= data
;
2295 _cleanup_strv_free_
char **n
= NULL
;
2296 size_t nlen
= 0, nbufsize
= 0;
2304 if (isempty(rvalue
)) {
2305 /* Empty assignment resets the list */
2306 *passenv
= strv_free(*passenv
);
2311 _cleanup_free_
char *word
= NULL
;
2313 r
= extract_first_word(&rvalue
, &word
, NULL
, EXTRACT_QUOTES
);
2319 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2320 "Trailing garbage in %s, ignoring: %s", lvalue
, whole_rvalue
);
2324 if (!env_name_is_valid(word
)) {
2325 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2326 "Invalid environment name for %s, ignoring: %s", lvalue
, word
);
2330 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2338 r
= strv_extend_strv(passenv
, n
, true);
2346 int config_parse_ip_tos(const char *unit
,
2347 const char *filename
,
2349 const char *section
,
2350 unsigned section_line
,
2357 int *ip_tos
= data
, x
;
2364 x
= ip_tos_from_string(rvalue
);
2366 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2374 int config_parse_unit_condition_path(
2376 const char *filename
,
2378 const char *section
,
2379 unsigned section_line
,
2386 _cleanup_free_
char *p
= NULL
;
2387 Condition
**list
= data
, *c
;
2388 ConditionType t
= ltype
;
2389 bool trigger
, negate
;
2398 if (isempty(rvalue
)) {
2399 /* Empty assignment resets the list */
2400 *list
= condition_free_list(*list
);
2404 trigger
= rvalue
[0] == '|';
2408 negate
= rvalue
[0] == '!';
2412 r
= unit_full_printf(u
, rvalue
, &p
);
2414 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2418 if (!path_is_absolute(p
)) {
2419 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2423 c
= condition_new(t
, p
, trigger
, negate
);
2427 LIST_PREPEND(conditions
, *list
, c
);
2431 int config_parse_unit_condition_string(
2433 const char *filename
,
2435 const char *section
,
2436 unsigned section_line
,
2443 _cleanup_free_
char *s
= NULL
;
2444 Condition
**list
= data
, *c
;
2445 ConditionType t
= ltype
;
2446 bool trigger
, negate
;
2455 if (isempty(rvalue
)) {
2456 /* Empty assignment resets the list */
2457 *list
= condition_free_list(*list
);
2461 trigger
= rvalue
[0] == '|';
2465 negate
= rvalue
[0] == '!';
2469 r
= unit_full_printf(u
, rvalue
, &s
);
2471 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2475 c
= condition_new(t
, s
, trigger
, negate
);
2479 LIST_PREPEND(conditions
, *list
, c
);
2483 int config_parse_unit_condition_null(
2485 const char *filename
,
2487 const char *section
,
2488 unsigned section_line
,
2495 Condition
**list
= data
, *c
;
2496 bool trigger
, negate
;
2504 if (isempty(rvalue
)) {
2505 /* Empty assignment resets the list */
2506 *list
= condition_free_list(*list
);
2510 trigger
= rvalue
[0] == '|';
2514 negate
= rvalue
[0] == '!';
2518 b
= parse_boolean(rvalue
);
2520 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2527 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2531 LIST_PREPEND(conditions
, *list
, c
);
2535 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2536 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action
, emergency_action
, EmergencyAction
, "Failed to parse failure action specifier");
2538 int config_parse_unit_requires_mounts_for(
2540 const char *filename
,
2542 const char *section
,
2543 unsigned section_line
,
2559 for (p
= rvalue
;; ) {
2560 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2562 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2568 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2569 "Invalid syntax, ignoring: %s", rvalue
);
2573 if (!utf8_is_valid(word
)) {
2574 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2578 r
= unit_full_printf(u
, word
, &resolved
);
2580 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit name \"%s\", ignoring: %m", word
);
2584 r
= unit_require_mounts_for(u
, resolved
);
2586 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount \"%s\", ignoring: %m", resolved
);
2592 int config_parse_documentation(const char *unit
,
2593 const char *filename
,
2595 const char *section
,
2596 unsigned section_line
,
2612 if (isempty(rvalue
)) {
2613 /* Empty assignment resets the list */
2614 u
->documentation
= strv_free(u
->documentation
);
2618 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2619 rvalue
, data
, userdata
);
2623 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2625 if (documentation_url_is_valid(*a
))
2628 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2640 static int syscall_filter_parse_one(
2642 const char *filename
,
2651 const SyscallFilterSet
*set
;
2654 set
= syscall_filter_set_find(t
);
2657 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Don't know system call group, ignoring: %s", t
);
2661 NULSTR_FOREACH(i
, set
->value
) {
2662 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, i
, false);
2669 id
= seccomp_syscall_resolve_name(t
);
2670 if (id
== __NR_SCMP_ERROR
) {
2672 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2676 /* If we previously wanted to forbid a syscall and now
2677 * we want to allow it, then remove it from the list
2679 if (!invert
== c
->syscall_whitelist
) {
2680 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2686 (void) set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2692 int config_parse_syscall_filter(
2694 const char *filename
,
2696 const char *section
,
2697 unsigned section_line
,
2704 ExecContext
*c
= data
;
2706 bool invert
= false;
2715 if (isempty(rvalue
)) {
2716 /* Empty assignment resets the list */
2717 c
->syscall_filter
= set_free(c
->syscall_filter
);
2718 c
->syscall_whitelist
= false;
2722 if (rvalue
[0] == '~') {
2727 if (!c
->syscall_filter
) {
2728 c
->syscall_filter
= set_new(NULL
);
2729 if (!c
->syscall_filter
)
2733 /* Allow everything but the ones listed */
2734 c
->syscall_whitelist
= false;
2736 /* Allow nothing but the ones listed */
2737 c
->syscall_whitelist
= true;
2739 /* Accept default syscalls if we are on a whitelist */
2740 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, false, "@default", false);
2748 _cleanup_free_
char *word
= NULL
;
2750 r
= extract_first_word(&p
, &word
, NULL
, 0);
2756 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2760 r
= syscall_filter_parse_one(unit
, filename
, line
, c
, invert
, word
, true);
2768 int config_parse_syscall_archs(
2770 const char *filename
,
2772 const char *section
,
2773 unsigned section_line
,
2784 if (isempty(rvalue
)) {
2785 *archs
= set_free(*archs
);
2789 r
= set_ensure_allocated(archs
, NULL
);
2793 for (p
= rvalue
;;) {
2794 _cleanup_free_
char *word
= NULL
;
2797 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2803 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2804 "Invalid syntax, ignoring: %s", rvalue
);
2808 r
= seccomp_arch_from_string(word
, &a
);
2810 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2811 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
2815 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2821 int config_parse_syscall_errno(
2823 const char *filename
,
2825 const char *section
,
2826 unsigned section_line
,
2833 ExecContext
*c
= data
;
2840 if (isempty(rvalue
)) {
2841 /* Empty assignment resets to KILL */
2842 c
->syscall_errno
= 0;
2846 e
= errno_from_name(rvalue
);
2848 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2852 c
->syscall_errno
= e
;
2856 int config_parse_address_families(
2858 const char *filename
,
2860 const char *section
,
2861 unsigned section_line
,
2868 ExecContext
*c
= data
;
2869 bool invert
= false;
2877 if (isempty(rvalue
)) {
2878 /* Empty assignment resets the list */
2879 c
->address_families
= set_free(c
->address_families
);
2880 c
->address_families_whitelist
= false;
2884 if (rvalue
[0] == '~') {
2889 if (!c
->address_families
) {
2890 c
->address_families
= set_new(NULL
);
2891 if (!c
->address_families
)
2894 c
->address_families_whitelist
= !invert
;
2897 for (p
= rvalue
;;) {
2898 _cleanup_free_
char *word
= NULL
;
2901 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2907 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2908 "Invalid syntax, ignoring: %s", rvalue
);
2912 af
= af_from_name(word
);
2914 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2915 "Failed to parse address family \"%s\", ignoring: %m", word
);
2919 /* If we previously wanted to forbid an address family and now
2920 * we want to allow it, then just remove it from the list.
2922 if (!invert
== c
->address_families_whitelist
) {
2923 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2927 set_remove(c
->address_families
, INT_TO_PTR(af
));
2931 int config_parse_restrict_namespaces(
2933 const char *filename
,
2935 const char *section
,
2936 unsigned section_line
,
2943 ExecContext
*c
= data
;
2944 bool invert
= false;
2947 if (isempty(rvalue
)) {
2948 /* Reset to the default. */
2949 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2953 if (rvalue
[0] == '~') {
2958 r
= parse_boolean(rvalue
);
2960 c
->restrict_namespaces
= 0;
2962 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2964 /* Not a boolean argument, in this case it's a list of namespace types. */
2966 r
= namespace_flag_from_string_many(rvalue
, &c
->restrict_namespaces
);
2968 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
2974 c
->restrict_namespaces
= (~c
->restrict_namespaces
) & NAMESPACE_FLAGS_ALL
;
2980 int config_parse_unit_slice(
2982 const char *filename
,
2984 const char *section
,
2985 unsigned section_line
,
2992 _cleanup_free_
char *k
= NULL
;
2993 Unit
*u
= userdata
, *slice
= NULL
;
3001 r
= unit_name_printf(u
, rvalue
, &k
);
3003 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
3007 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
3009 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
3013 r
= unit_set_slice(u
, slice
);
3015 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
3022 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
3024 int config_parse_cpu_weight(
3026 const char *filename
,
3028 const char *section
,
3029 unsigned section_line
,
3036 uint64_t *weight
= data
;
3043 r
= cg_weight_parse(rvalue
, weight
);
3045 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU weight '%s' invalid. Ignoring.", rvalue
);
3052 int config_parse_cpu_shares(
3054 const char *filename
,
3056 const char *section
,
3057 unsigned section_line
,
3064 uint64_t *shares
= data
;
3071 r
= cg_cpu_shares_parse(rvalue
, shares
);
3073 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
3080 int config_parse_cpu_quota(
3082 const char *filename
,
3084 const char *section
,
3085 unsigned section_line
,
3092 CGroupContext
*c
= data
;
3099 if (isempty(rvalue
)) {
3100 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3104 r
= parse_percent_unbounded(rvalue
);
3106 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU quota '%s' invalid. Ignoring.", rvalue
);
3110 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 100U;
3114 int config_parse_memory_limit(
3116 const char *filename
,
3118 const char *section
,
3119 unsigned section_line
,
3126 CGroupContext
*c
= data
;
3127 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3130 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3132 r
= parse_percent(rvalue
);
3134 r
= parse_size(rvalue
, 1024, &bytes
);
3136 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
3140 bytes
= physical_memory_scale(r
, 100U);
3142 if (bytes
<= 0 || bytes
>= UINT64_MAX
) {
3143 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range. Ignoring.", rvalue
);
3148 if (streq(lvalue
, "MemoryLow"))
3149 c
->memory_low
= bytes
;
3150 else if (streq(lvalue
, "MemoryHigh"))
3151 c
->memory_high
= bytes
;
3152 else if (streq(lvalue
, "MemoryMax"))
3153 c
->memory_max
= bytes
;
3154 else if (streq(lvalue
, "MemorySwapMax"))
3155 c
->memory_swap_max
= bytes
;
3156 else if (streq(lvalue
, "MemoryLimit"))
3157 c
->memory_limit
= bytes
;
3164 int config_parse_tasks_max(
3166 const char *filename
,
3168 const char *section
,
3169 unsigned section_line
,
3176 uint64_t *tasks_max
= data
, v
;
3180 if (isempty(rvalue
)) {
3181 *tasks_max
= u
->manager
->default_tasks_max
;
3185 if (streq(rvalue
, "infinity")) {
3186 *tasks_max
= CGROUP_LIMIT_MAX
;
3190 r
= parse_percent(rvalue
);
3192 r
= safe_atou64(rvalue
, &v
);
3194 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
3198 v
= system_tasks_max_scale(r
, 100U);
3200 if (v
<= 0 || v
>= UINT64_MAX
) {
3201 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue
);
3209 int config_parse_device_allow(
3211 const char *filename
,
3213 const char *section
,
3214 unsigned section_line
,
3221 _cleanup_free_
char *path
= NULL
, *t
= NULL
;
3222 CGroupContext
*c
= data
;
3223 CGroupDeviceAllow
*a
;
3224 const char *m
= NULL
;
3228 if (isempty(rvalue
)) {
3229 while (c
->device_allow
)
3230 cgroup_context_free_device_allow(c
, c
->device_allow
);
3235 r
= unit_full_printf(userdata
, rvalue
, &t
);
3237 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3238 "Failed to resolve specifiers in %s, ignoring: %m",
3242 n
= strcspn(t
, WHITESPACE
);
3244 path
= strndup(t
, n
);
3248 if (!is_deviceallow_pattern(path
)) {
3249 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3253 m
= t
+ n
+ strspn(t
+ n
, WHITESPACE
);
3257 if (!in_charset(m
, "rwm")) {
3258 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
3262 a
= new0(CGroupDeviceAllow
, 1);
3268 a
->r
= !!strchr(m
, 'r');
3269 a
->w
= !!strchr(m
, 'w');
3270 a
->m
= !!strchr(m
, 'm');
3272 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
3276 int config_parse_io_weight(
3278 const char *filename
,
3280 const char *section
,
3281 unsigned section_line
,
3288 uint64_t *weight
= data
;
3295 r
= cg_weight_parse(rvalue
, weight
);
3297 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", rvalue
);
3304 int config_parse_io_device_weight(
3306 const char *filename
,
3308 const char *section
,
3309 unsigned section_line
,
3316 _cleanup_free_
char *path
= NULL
;
3317 CGroupIODeviceWeight
*w
;
3318 CGroupContext
*c
= data
;
3328 if (isempty(rvalue
)) {
3329 while (c
->io_device_weights
)
3330 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3335 n
= strcspn(rvalue
, WHITESPACE
);
3336 weight
= rvalue
+ n
;
3337 weight
+= strspn(weight
, WHITESPACE
);
3339 if (isempty(weight
)) {
3340 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3344 path
= strndup(rvalue
, n
);
3348 if (!path_startswith(path
, "/dev")) {
3349 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3353 r
= cg_weight_parse(weight
, &u
);
3355 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid. Ignoring.", weight
);
3359 assert(u
!= CGROUP_WEIGHT_INVALID
);
3361 w
= new0(CGroupIODeviceWeight
, 1);
3370 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3374 int config_parse_io_limit(
3376 const char *filename
,
3378 const char *section
,
3379 unsigned section_line
,
3386 _cleanup_free_
char *path
= NULL
;
3387 CGroupIODeviceLimit
*l
= NULL
, *t
;
3388 CGroupContext
*c
= data
;
3389 CGroupIOLimitType type
;
3399 type
= cgroup_io_limit_type_from_string(lvalue
);
3402 if (isempty(rvalue
)) {
3403 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3404 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3408 n
= strcspn(rvalue
, WHITESPACE
);
3410 limit
+= strspn(limit
, WHITESPACE
);
3413 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3417 path
= strndup(rvalue
, n
);
3421 if (!path_startswith(path
, "/dev")) {
3422 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3426 if (streq("infinity", limit
)) {
3427 num
= CGROUP_LIMIT_MAX
;
3429 r
= parse_size(limit
, 1000, &num
);
3430 if (r
< 0 || num
<= 0) {
3431 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO Limit '%s' invalid. Ignoring.", rvalue
);
3436 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3437 if (path_equal(path
, t
->path
)) {
3444 CGroupIOLimitType ttype
;
3446 l
= new0(CGroupIODeviceLimit
, 1);
3452 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3453 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3455 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3458 l
->limits
[type
] = num
;
3463 int config_parse_blockio_weight(
3465 const char *filename
,
3467 const char *section
,
3468 unsigned section_line
,
3475 uint64_t *weight
= data
;
3482 r
= cg_blkio_weight_parse(rvalue
, weight
);
3484 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3491 int config_parse_blockio_device_weight(
3493 const char *filename
,
3495 const char *section
,
3496 unsigned section_line
,
3503 _cleanup_free_
char *path
= NULL
;
3504 CGroupBlockIODeviceWeight
*w
;
3505 CGroupContext
*c
= data
;
3515 if (isempty(rvalue
)) {
3516 while (c
->blockio_device_weights
)
3517 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3522 n
= strcspn(rvalue
, WHITESPACE
);
3523 weight
= rvalue
+ n
;
3524 weight
+= strspn(weight
, WHITESPACE
);
3526 if (isempty(weight
)) {
3527 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3531 path
= strndup(rvalue
, n
);
3535 if (!path_startswith(path
, "/dev")) {
3536 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3540 r
= cg_blkio_weight_parse(weight
, &u
);
3542 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3546 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3548 w
= new0(CGroupBlockIODeviceWeight
, 1);
3557 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3561 int config_parse_blockio_bandwidth(
3563 const char *filename
,
3565 const char *section
,
3566 unsigned section_line
,
3573 _cleanup_free_
char *path
= NULL
;
3574 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3575 CGroupContext
*c
= data
;
3576 const char *bandwidth
;
3586 read
= streq("BlockIOReadBandwidth", lvalue
);
3588 if (isempty(rvalue
)) {
3589 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3590 b
->rbps
= CGROUP_LIMIT_MAX
;
3591 b
->wbps
= CGROUP_LIMIT_MAX
;
3596 n
= strcspn(rvalue
, WHITESPACE
);
3597 bandwidth
= rvalue
+ n
;
3598 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3601 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3605 path
= strndup(rvalue
, n
);
3609 if (!path_startswith(path
, "/dev")) {
3610 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3614 r
= parse_size(bandwidth
, 1000, &bytes
);
3615 if (r
< 0 || bytes
<= 0) {
3616 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3620 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3621 if (path_equal(path
, t
->path
)) {
3628 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3634 b
->rbps
= CGROUP_LIMIT_MAX
;
3635 b
->wbps
= CGROUP_LIMIT_MAX
;
3637 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3648 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3650 int config_parse_job_mode_isolate(
3652 const char *filename
,
3654 const char *section
,
3655 unsigned section_line
,
3669 r
= parse_boolean(rvalue
);
3671 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3675 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3679 int config_parse_runtime_directory(
3681 const char *filename
,
3683 const char *section
,
3684 unsigned section_line
,
3701 if (isempty(rvalue
)) {
3702 /* Empty assignment resets the list */
3703 *rt
= strv_free(*rt
);
3707 for (p
= rvalue
;;) {
3708 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3710 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3716 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3717 "Invalid syntax, ignoring: %s", rvalue
);
3721 r
= unit_full_printf(u
, word
, &k
);
3723 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3724 "Failed to resolve specifiers in \"%s\", ignoring: %m", word
);
3728 if (!filename_is_valid(k
)) {
3729 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3730 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3734 r
= strv_push(rt
, k
);
3741 int config_parse_set_status(
3743 const char *filename
,
3745 const char *section
,
3746 unsigned section_line
,
3754 const char *word
, *state
;
3756 ExitStatusSet
*status_set
= data
;
3763 /* Empty assignment resets the list */
3764 if (isempty(rvalue
)) {
3765 exit_status_set_free(status_set
);
3769 FOREACH_WORD(word
, l
, rvalue
, state
) {
3770 _cleanup_free_
char *temp
;
3774 temp
= strndup(word
, l
);
3778 r
= safe_atoi(temp
, &val
);
3780 val
= signal_from_string_try_harder(temp
);
3783 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3786 set
= &status_set
->signal
;
3788 if (val
< 0 || val
> 255) {
3789 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3792 set
= &status_set
->status
;
3795 r
= set_ensure_allocated(set
, NULL
);
3799 r
= set_put(*set
, INT_TO_PTR(val
));
3801 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3805 if (!isempty(state
))
3806 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3811 int config_parse_namespace_path_strv(
3813 const char *filename
,
3815 const char *section
,
3816 unsigned section_line
,
3833 if (isempty(rvalue
)) {
3834 /* Empty assignment resets the list */
3835 *sv
= strv_free(*sv
);
3841 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
3843 bool ignore_enoent
= false, shall_prefix
= false;
3845 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3851 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
3855 if (!utf8_is_valid(word
)) {
3856 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3861 if (startswith(w
, "-")) {
3862 ignore_enoent
= true;
3865 if (startswith(w
, "+")) {
3866 shall_prefix
= true;
3870 r
= unit_full_printf(u
, w
, &resolved
);
3872 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers in %s: %m", word
);
3876 if (!path_is_absolute(resolved
)) {
3877 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", resolved
);
3881 path_kill_slashes(resolved
);
3883 joined
= strjoin(ignore_enoent
? "-" : "",
3884 shall_prefix
? "+" : "",
3887 r
= strv_push(sv
, joined
);
3897 int config_parse_bind_paths(
3899 const char *filename
,
3901 const char *section
,
3902 unsigned section_line
,
3909 ExecContext
*c
= data
;
3918 if (isempty(rvalue
)) {
3919 /* Empty assignment resets the list */
3920 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
3921 c
->bind_mounts
= NULL
;
3922 c
->n_bind_mounts
= 0;
3928 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
3929 char *s
= NULL
, *d
= NULL
;
3930 bool rbind
= true, ignore_enoent
= false;
3932 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
3938 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3944 ignore_enoent
= true;
3948 if (!utf8_is_valid(s
)) {
3949 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, s
);
3952 if (!path_is_absolute(s
)) {
3953 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute source path, ignoring: %s", s
);
3957 path_kill_slashes(s
);
3959 /* Optionally, the destination is specified. */
3960 if (p
&& p
[-1] == ':') {
3961 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
3965 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3969 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':': %s", rvalue
);
3973 if (!utf8_is_valid(destination
)) {
3974 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, destination
);
3977 if (!path_is_absolute(destination
)) {
3978 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute destination path, ignoring: %s", destination
);
3982 d
= path_kill_slashes(destination
);
3984 /* Optionally, there's also a short option string specified */
3985 if (p
&& p
[-1] == ':') {
3986 _cleanup_free_
char *options
= NULL
;
3988 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
3992 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
3996 if (isempty(options
) || streq(options
, "rbind"))
3998 else if (streq(options
, "norbind"))
4001 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
4008 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
4012 .read_only
= !!strstr(lvalue
, "ReadOnly"),
4014 .ignore_enoent
= ignore_enoent
,
4023 int config_parse_no_new_privileges(
4025 const char *filename
,
4027 const char *section
,
4028 unsigned section_line
,
4035 ExecContext
*c
= data
;
4043 k
= parse_boolean(rvalue
);
4045 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
4049 c
->no_new_privileges
= k
;
4054 int config_parse_protect_home(
4056 const char *filename
,
4058 const char *section
,
4059 unsigned section_line
,
4066 ExecContext
*c
= data
;
4074 /* Our enum shall be a superset of booleans, hence first try
4075 * to parse as boolean, and then as enum */
4077 k
= parse_boolean(rvalue
);
4079 c
->protect_home
= PROTECT_HOME_YES
;
4081 c
->protect_home
= PROTECT_HOME_NO
;
4085 h
= protect_home_from_string(rvalue
);
4087 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
4091 c
->protect_home
= h
;
4097 int config_parse_protect_system(
4099 const char *filename
,
4101 const char *section
,
4102 unsigned section_line
,
4109 ExecContext
*c
= data
;
4117 /* Our enum shall be a superset of booleans, hence first try
4118 * to parse as boolean, and then as enum */
4120 k
= parse_boolean(rvalue
);
4122 c
->protect_system
= PROTECT_SYSTEM_YES
;
4124 c
->protect_system
= PROTECT_SYSTEM_NO
;
4128 s
= protect_system_from_string(rvalue
);
4130 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
4134 c
->protect_system
= s
;
4140 #define FOLLOW_MAX 8
4142 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4153 /* This will update the filename pointer if the loaded file is
4154 * reached by a symlink. The old string will be freed. */
4157 char *target
, *name
;
4159 if (c
++ >= FOLLOW_MAX
)
4162 path_kill_slashes(*filename
);
4164 /* Add the file name we are currently looking at to
4165 * the names of this unit, but only if it is a valid
4167 name
= basename(*filename
);
4168 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4170 id
= set_get(names
, name
);
4176 r
= set_consume(names
, id
);
4182 /* Try to open the file name, but don't if its a symlink */
4183 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4190 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4191 r
= readlink_and_make_absolute(*filename
, &target
);
4199 f
= fdopen(fd
, "re");
4211 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4219 /* Let's try to add in all symlink names we found */
4220 while ((k
= set_steal_first(names
))) {
4222 /* First try to merge in the other name into our
4224 r
= unit_merge_by_name(*u
, k
);
4228 /* Hmm, we couldn't merge the other unit into
4229 * ours? Then let's try it the other way
4232 /* If the symlink name we are looking at is unit template, then
4233 we must search for instance of this template */
4234 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4235 _cleanup_free_
char *instance
= NULL
;
4237 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4241 other
= manager_get_unit((*u
)->manager
, instance
);
4243 other
= manager_get_unit((*u
)->manager
, k
);
4248 r
= unit_merge(other
, *u
);
4251 return merge_by_names(u
, names
, NULL
);
4259 unit_choose_id(*u
, id
);
4267 static int load_from_path(Unit
*u
, const char *path
) {
4268 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4269 _cleanup_fclose_
FILE *f
= NULL
;
4270 _cleanup_free_
char *filename
= NULL
;
4279 symlink_names
= set_new(&string_hash_ops
);
4283 if (path_is_absolute(path
)) {
4285 filename
= strdup(path
);
4289 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4291 filename
= mfree(filename
);
4299 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4301 /* Instead of opening the path right away, we manually
4302 * follow all symlinks and add their name to our unit
4303 * name set while doing so */
4304 filename
= path_make_absolute(path
, *p
);
4308 if (u
->manager
->unit_path_cache
&&
4309 !set_get(u
->manager
->unit_path_cache
, filename
))
4312 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4315 filename
= mfree(filename
);
4317 /* ENOENT means that the file is missing or is a dangling symlink.
4318 * ENOTDIR means that one of paths we expect to be is a directory
4319 * is not a directory, we should just ignore that.
4320 * EACCES means that the directory or file permissions are wrong.
4323 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4324 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4327 /* Empty the symlink names for the next run */
4328 set_clear_free(symlink_names
);
4333 /* Hmm, no suitable file found? */
4336 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4337 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4342 r
= merge_by_names(&merged
, symlink_names
, id
);
4347 u
->load_state
= UNIT_MERGED
;
4351 if (fstat(fileno(f
), &st
) < 0)
4354 if (null_or_empty(&st
)) {
4355 u
->load_state
= UNIT_MASKED
;
4356 u
->fragment_mtime
= 0;
4358 u
->load_state
= UNIT_LOADED
;
4359 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4361 /* Now, parse the file contents */
4362 r
= config_parse(u
->id
, filename
, f
,
4363 UNIT_VTABLE(u
)->sections
,
4364 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4365 false, true, false, u
);
4370 free(u
->fragment_path
);
4371 u
->fragment_path
= filename
;
4374 if (u
->source_path
) {
4375 if (stat(u
->source_path
, &st
) >= 0)
4376 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4378 u
->source_mtime
= 0;
4384 int unit_load_fragment(Unit
*u
) {
4390 assert(u
->load_state
== UNIT_STUB
);
4394 u
->load_state
= UNIT_LOADED
;
4398 /* First, try to find the unit under its id. We always look
4399 * for unit files in the default directories, to make it easy
4400 * to override things by placing things in /etc/systemd/system */
4401 r
= load_from_path(u
, u
->id
);
4405 /* Try to find an alias we can load this with */
4406 if (u
->load_state
== UNIT_STUB
) {
4407 SET_FOREACH(t
, u
->names
, i
) {
4412 r
= load_from_path(u
, t
);
4416 if (u
->load_state
!= UNIT_STUB
)
4421 /* And now, try looking for it under the suggested (originally linked) path */
4422 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4424 r
= load_from_path(u
, u
->fragment_path
);
4428 if (u
->load_state
== UNIT_STUB
)
4429 /* Hmm, this didn't work? Then let's get rid
4430 * of the fragment path stored for us, so that
4431 * we don't point to an invalid location. */
4432 u
->fragment_path
= mfree(u
->fragment_path
);
4435 /* Look for a template */
4436 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4437 _cleanup_free_
char *k
= NULL
;
4439 r
= unit_name_template(u
->id
, &k
);
4443 r
= load_from_path(u
, k
);
4447 if (u
->load_state
== UNIT_STUB
) {
4448 SET_FOREACH(t
, u
->names
, i
) {
4449 _cleanup_free_
char *z
= NULL
;
4454 r
= unit_name_template(t
, &z
);
4458 r
= load_from_path(u
, z
);
4462 if (u
->load_state
!= UNIT_STUB
)
4471 void unit_dump_config_items(FILE *f
) {
4472 static const struct {
4473 const ConfigParserCallback callback
;
4476 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
4477 { config_parse_warn_compat
, "NOTSUPPORTED" },
4479 { config_parse_int
, "INTEGER" },
4480 { config_parse_unsigned
, "UNSIGNED" },
4481 { config_parse_iec_size
, "SIZE" },
4482 { config_parse_iec_uint64
, "SIZE" },
4483 { config_parse_si_size
, "SIZE" },
4484 { config_parse_bool
, "BOOLEAN" },
4485 { config_parse_string
, "STRING" },
4486 { config_parse_path
, "PATH" },
4487 { config_parse_unit_path_printf
, "PATH" },
4488 { config_parse_strv
, "STRING [...]" },
4489 { config_parse_exec_nice
, "NICE" },
4490 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4491 { config_parse_exec_io_class
, "IOCLASS" },
4492 { config_parse_exec_io_priority
, "IOPRIORITY" },
4493 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4494 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4495 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4496 { config_parse_mode
, "MODE" },
4497 { config_parse_unit_env_file
, "FILE" },
4498 { config_parse_exec_output
, "OUTPUT" },
4499 { config_parse_exec_input
, "INPUT" },
4500 { config_parse_log_facility
, "FACILITY" },
4501 { config_parse_log_level
, "LEVEL" },
4502 { config_parse_exec_secure_bits
, "SECUREBITS" },
4503 { config_parse_capability_set
, "BOUNDINGSET" },
4504 { config_parse_limit
, "LIMIT" },
4505 { config_parse_unit_deps
, "UNIT [...]" },
4506 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4507 { config_parse_service_type
, "SERVICETYPE" },
4508 { config_parse_service_restart
, "SERVICERESTART" },
4509 #ifdef HAVE_SYSV_COMPAT
4510 { config_parse_sysv_priority
, "SYSVPRIORITY" },
4512 { config_parse_kill_mode
, "KILLMODE" },
4513 { config_parse_signal
, "SIGNAL" },
4514 { config_parse_socket_listen
, "SOCKET [...]" },
4515 { config_parse_socket_bind
, "SOCKETBIND" },
4516 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4517 { config_parse_sec
, "SECONDS" },
4518 { config_parse_nsec
, "NANOSECONDS" },
4519 { config_parse_namespace_path_strv
, "PATH [...]" },
4520 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4521 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4522 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4523 { config_parse_unit_string_printf
, "STRING" },
4524 { config_parse_trigger_unit
, "UNIT" },
4525 { config_parse_timer
, "TIMER" },
4526 { config_parse_path_spec
, "PATH" },
4527 { config_parse_notify_access
, "ACCESS" },
4528 { config_parse_ip_tos
, "TOS" },
4529 { config_parse_unit_condition_path
, "CONDITION" },
4530 { config_parse_unit_condition_string
, "CONDITION" },
4531 { config_parse_unit_condition_null
, "CONDITION" },
4532 { config_parse_unit_slice
, "SLICE" },
4533 { config_parse_documentation
, "URL" },
4534 { config_parse_service_timeout
, "SECONDS" },
4535 { config_parse_emergency_action
, "ACTION" },
4536 { config_parse_set_status
, "STATUS" },
4537 { config_parse_service_sockets
, "SOCKETS" },
4538 { config_parse_environ
, "ENVIRON" },
4540 { config_parse_syscall_filter
, "SYSCALLS" },
4541 { config_parse_syscall_archs
, "ARCHS" },
4542 { config_parse_syscall_errno
, "ERRNO" },
4543 { config_parse_address_families
, "FAMILIES" },
4544 { config_parse_restrict_namespaces
, "NAMESPACES" },
4546 { config_parse_cpu_shares
, "SHARES" },
4547 { config_parse_cpu_weight
, "WEIGHT" },
4548 { config_parse_memory_limit
, "LIMIT" },
4549 { config_parse_device_allow
, "DEVICE" },
4550 { config_parse_device_policy
, "POLICY" },
4551 { config_parse_io_limit
, "LIMIT" },
4552 { config_parse_io_weight
, "WEIGHT" },
4553 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4554 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4555 { config_parse_blockio_weight
, "WEIGHT" },
4556 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4557 { config_parse_long
, "LONG" },
4558 { config_parse_socket_service
, "SERVICE" },
4560 { config_parse_exec_selinux_context
, "LABEL" },
4562 { config_parse_job_mode
, "MODE" },
4563 { config_parse_job_mode_isolate
, "BOOLEAN" },
4564 { config_parse_personality
, "PERSONALITY" },
4567 const char *prev
= NULL
;
4572 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4573 const char *rvalue
= "OTHER", *lvalue
;
4577 const ConfigPerfItem
*p
;
4579 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4581 dot
= strchr(i
, '.');
4582 lvalue
= dot
? dot
+ 1 : i
;
4586 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
4590 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
4593 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
4594 if (p
->parse
== table
[j
].callback
) {
4595 rvalue
= table
[j
].rvalue
;
4599 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);