1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2012 Holger Hans Peter Freyther
15 #include <sys/resource.h>
19 #include "alloc-util.h"
20 #include "all-units.h"
21 #include "bus-error.h"
22 #include "bus-internal.h"
25 #include "capability-util.h"
27 #include "conf-parser.h"
28 #include "cpu-set-util.h"
30 #include "errno-list.h"
34 #include "hexdecoct.h"
37 #include "journal-util.h"
38 #include "load-fragment.h"
41 #include "mount-util.h"
42 #include "parse-util.h"
43 #include "path-util.h"
44 #include "process-util.h"
46 #include "seccomp-util.h"
48 #include "securebits.h"
49 #include "securebits-util.h"
50 #include "signal-util.h"
51 #include "socket-protocol-list.h"
52 #include "stat-util.h"
53 #include "string-util.h"
55 #include "unit-name.h"
56 #include "unit-printf.h"
57 #include "user-util.h"
60 static int supported_socket_protocol_from_string(const char *s
) {
66 r
= socket_protocol_from_name(s
);
69 if (!IN_SET(r
, IPPROTO_UDPLITE
, IPPROTO_SCTP
))
70 return -EPROTONOSUPPORT
;
75 DEFINE_CONFIG_PARSE(config_parse_socket_protocol
, supported_socket_protocol_from_string
, "Failed to parse socket protocol");
76 DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits
, secure_bits_from_string
, "Failed to parse secure bits");
77 DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode
, collect_mode
, CollectMode
, "Failed to parse garbage collection mode");
78 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
79 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode
, exec_keyring_mode
, ExecKeyringMode
, "Failed to parse keyring mode");
80 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
81 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
82 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
83 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
84 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home
, protect_home
, ProtectHome
, "Failed to parse protect home value");
85 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system
, protect_system
, ProtectSystem
, "Failed to parse protect system value");
86 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode
, exec_preserve_mode
, ExecPreserveMode
, "Failed to parse runtime directory preserve mode");
87 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
88 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
89 DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind
, socket_address_bind_ipv6_only_or_bool
, SocketAddressBindIPv6Only
, "Failed to parse bind IPv6 only value");
90 DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos
, ip_tos
, int, -1, "Failed to parse IP TOS value");
91 DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight
, cg_blkio_weight_parse
, uint64_t, "Invalid block IO weight");
92 DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight
, cg_weight_parse
, uint64_t, "Invalid weight");
93 DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares
, cg_cpu_shares_parse
, uint64_t, "Invalid CPU shares");
94 DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags
, mount_propagation_flags_from_string
, unsigned long, "Failed to parse mount flag");
96 int config_parse_unit_deps(
101 unsigned section_line
,
108 UnitDependency d
= ltype
;
118 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
121 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_RETAIN_ESCAPE
);
127 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
131 r
= unit_name_printf(u
, word
, &k
);
133 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", word
);
137 r
= unit_add_dependency_by_name(u
, d
, k
, true, UNIT_DEPENDENCY_FILE
);
139 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
145 int config_parse_obsolete_unit_deps(
147 const char *filename
,
150 unsigned section_line
,
157 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
158 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue
, unit_dependency_to_string(ltype
));
160 return config_parse_unit_deps(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, rvalue
, data
, userdata
);
163 int config_parse_unit_string_printf(
165 const char *filename
,
168 unsigned section_line
,
175 _cleanup_free_
char *k
= NULL
;
184 r
= unit_full_printf(u
, rvalue
, &k
);
186 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue
);
190 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
193 int config_parse_unit_strv_printf(
195 const char *filename
,
198 unsigned section_line
,
206 _cleanup_free_
char *k
= NULL
;
214 r
= unit_full_printf(u
, rvalue
, &k
);
216 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue
);
220 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
223 int config_parse_unit_path_printf(
225 const char *filename
,
228 unsigned section_line
,
235 _cleanup_free_
char *k
= NULL
;
245 /* Let's not bother with anything that is too long */
246 if (strlen(rvalue
) >= PATH_MAX
) {
247 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
248 "%s value too long%s.",
249 lvalue
, fatal
? "" : ", ignoring");
250 return fatal
? -ENAMETOOLONG
: 0;
253 r
= unit_full_printf(u
, rvalue
, &k
);
255 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
256 "Failed to resolve unit specifiers in '%s'%s: %m",
257 rvalue
, fatal
? "" : ", ignoring");
258 return fatal
? -ENOEXEC
: 0;
261 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
264 int config_parse_unit_path_strv_printf(
266 const char *filename
,
269 unsigned section_line
,
286 if (isempty(rvalue
)) {
292 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
294 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
300 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
301 "Invalid syntax, ignoring: %s", rvalue
);
305 r
= unit_full_printf(u
, word
, &k
);
307 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
308 "Failed to resolve unit specifiers in '%s', ignoring: %m", word
);
312 r
= path_simplify_and_warn(k
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
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
) {
357 _cleanup_free_
char *k
= NULL
;
359 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
361 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue
);
365 r
= path_simplify_and_warn(k
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
369 free_and_replace(p
->path
, k
);
372 } else if (streq(lvalue
, "ListenNetlink")) {
373 _cleanup_free_
char *k
= NULL
;
375 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
377 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue
);
381 r
= socket_address_parse_netlink(&p
->address
, k
);
383 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value in '%s', ignoring: %m", k
);
387 p
->type
= SOCKET_SOCKET
;
390 _cleanup_free_
char *k
= NULL
;
392 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
394 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue
);
398 r
= socket_address_parse_and_warn(&p
->address
, k
);
400 if (r
!= -EAFNOSUPPORT
)
401 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value in '%s', ignoring: %m", k
);
405 if (streq(lvalue
, "ListenStream"))
406 p
->address
.type
= SOCK_STREAM
;
407 else if (streq(lvalue
, "ListenDatagram"))
408 p
->address
.type
= SOCK_DGRAM
;
410 assert(streq(lvalue
, "ListenSequentialPacket"));
411 p
->address
.type
= SOCK_SEQPACKET
;
414 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
415 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
419 p
->type
= SOCKET_SOCKET
;
423 p
->auxiliary_fds
= NULL
;
424 p
->n_auxiliary_fds
= 0;
427 LIST_FIND_TAIL(port
, s
->ports
, tail
);
428 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
435 int config_parse_exec_nice(
437 const char *filename
,
440 unsigned section_line
,
447 ExecContext
*c
= data
;
455 if (isempty(rvalue
)) {
460 r
= parse_nice(rvalue
, &priority
);
463 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Nice priority out of range, ignoring: %s", rvalue
);
465 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority '%s', ignoring: %m", rvalue
);
475 int config_parse_exec_oom_score_adjust(
477 const char *filename
,
480 unsigned section_line
,
487 ExecContext
*c
= data
;
495 if (isempty(rvalue
)) {
496 c
->oom_score_adjust_set
= false;
500 r
= parse_oom_score_adjust(rvalue
, &oa
);
503 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "OOM score adjust value out of range, ignoring: %s", rvalue
);
505 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value '%s', ignoring: %m", rvalue
);
509 c
->oom_score_adjust
= oa
;
510 c
->oom_score_adjust_set
= true;
515 int config_parse_exec(
517 const char *filename
,
520 unsigned section_line
,
527 ExecCommand
**e
= data
;
539 rvalue
+= strspn(rvalue
, WHITESPACE
);
541 if (isempty(rvalue
)) {
542 /* An empty assignment resets the list */
543 *e
= exec_command_free_list(*e
);
549 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
550 ExecCommandFlags flags
= 0;
551 bool ignore
= false, separate_argv0
= false;
552 _cleanup_free_ ExecCommand
*nce
= NULL
;
553 _cleanup_strv_free_
char **n
= NULL
;
554 size_t nlen
= 0, nbufsize
= 0;
559 r
= extract_first_word_and_warn(&p
, &firstword
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
565 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
566 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
567 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
568 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
569 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
570 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
571 * other sandboxing, with some special exceptions for changing UID.
573 * The idea is that '!!' may be used to write services that can take benefit of systemd's
574 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
575 * privilege dropping within the daemon if the kernel does not offer that. */
577 if (*f
== '-' && !(flags
& EXEC_COMMAND_IGNORE_FAILURE
)) {
578 flags
|= EXEC_COMMAND_IGNORE_FAILURE
;
580 } else if (*f
== '@' && !separate_argv0
)
581 separate_argv0
= true;
582 else if (*f
== '+' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
583 flags
|= EXEC_COMMAND_FULLY_PRIVILEGED
;
584 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_NO_SETUID
|EXEC_COMMAND_AMBIENT_MAGIC
)))
585 flags
|= EXEC_COMMAND_NO_SETUID
;
586 else if (*f
== '!' && !(flags
& (EXEC_COMMAND_FULLY_PRIVILEGED
|EXEC_COMMAND_AMBIENT_MAGIC
))) {
587 flags
&= ~EXEC_COMMAND_NO_SETUID
;
588 flags
|= EXEC_COMMAND_AMBIENT_MAGIC
;
594 r
= unit_full_printf(u
, f
, &path
);
596 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
597 "Failed to resolve unit specifiers in '%s'%s: %m",
598 f
, ignore
? ", ignoring" : "");
599 return ignore
? 0 : -ENOEXEC
;
603 /* First word is either "-" or "@" with no command. */
604 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
605 "Empty path in command line%s: '%s'",
606 ignore
? ", ignoring" : "", rvalue
);
607 return ignore
? 0 : -ENOEXEC
;
609 if (!string_is_safe(path
)) {
610 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
611 "Executable name contains special characters%s: %s",
612 ignore
? ", ignoring" : "", path
);
613 return ignore
? 0 : -ENOEXEC
;
615 if (endswith(path
, "/")) {
616 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
617 "Executable path specifies a directory%s: %s",
618 ignore
? ", ignoring" : "", path
);
619 return ignore
? 0 : -ENOEXEC
;
622 if (!path_is_absolute(path
)) {
626 if (!filename_is_valid(path
)) {
627 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
628 "Neither a valid executable name nor an absolute path%s: %s",
629 ignore
? ", ignoring" : "", path
);
630 return ignore
? 0 : -ENOEXEC
;
633 /* Resolve a single-component name to a full path */
634 NULSTR_FOREACH(prefix
, DEFAULT_PATH_NULSTR
) {
635 _cleanup_free_
char *fullpath
= NULL
;
637 fullpath
= strjoin(prefix
, "/", path
);
641 if (access(fullpath
, F_OK
) >= 0) {
642 free_and_replace(path
, fullpath
);
649 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
650 "Executable \"%s\" not found in path \"%s\"%s",
651 path
, DEFAULT_PATH
, ignore
? ", ignoring" : "");
652 return ignore
? 0 : -ENOEXEC
;
656 if (!separate_argv0
) {
659 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
669 path_simplify(path
, false);
671 while (!isempty(p
)) {
672 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
674 /* Check explicitly for an unquoted semicolon as
675 * command separator token. */
676 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
678 p
+= strspn(p
, WHITESPACE
);
683 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
684 * extract_first_word() would return the same for all of those. */
685 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
689 p
+= strspn(p
, WHITESPACE
);
691 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
702 r
= extract_first_word_and_warn(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
706 return ignore
? 0 : -ENOEXEC
;
708 r
= unit_full_printf(u
, word
, &resolved
);
710 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
711 "Failed to resolve unit specifiers in %s%s: %m",
712 word
, ignore
? ", ignoring" : "");
713 return ignore
? 0 : -ENOEXEC
;
716 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
719 n
[nlen
++] = TAKE_PTR(resolved
);
724 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
725 "Empty executable name or zeroeth argument%s: %s",
726 ignore
? ", ignoring" : "", rvalue
);
727 return ignore
? 0 : -ENOEXEC
;
730 nce
= new0(ExecCommand
, 1);
734 nce
->argv
= TAKE_PTR(n
);
735 nce
->path
= TAKE_PTR(path
);
738 exec_command_append_list(e
, nce
);
740 /* Do not _cleanup_free_ these. */
749 int config_parse_socket_bindtodevice(
751 const char *filename
,
754 unsigned section_line
,
768 if (isempty(rvalue
) || streq(rvalue
, "*")) {
769 s
->bind_to_device
= mfree(s
->bind_to_device
);
773 if (!ifname_valid(rvalue
)) {
774 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid interface name, ignoring: %s", rvalue
);
778 if (free_and_strdup(&s
->bind_to_device
, rvalue
) < 0)
784 int config_parse_exec_input(
786 const char *filename
,
789 unsigned section_line
,
796 ExecContext
*c
= data
;
807 n
= startswith(rvalue
, "fd:");
809 _cleanup_free_
char *resolved
= NULL
;
811 r
= unit_full_printf(u
, n
, &resolved
);
813 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s': %m", n
);
815 if (isempty(resolved
))
816 resolved
= mfree(resolved
);
817 else if (!fdname_is_valid(resolved
)) {
818 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name: %s", resolved
);
822 free_and_replace(c
->stdio_fdname
[STDIN_FILENO
], resolved
);
824 ei
= EXEC_INPUT_NAMED_FD
;
826 } else if ((n
= startswith(rvalue
, "file:"))) {
827 _cleanup_free_
char *resolved
= NULL
;
829 r
= unit_full_printf(u
, n
, &resolved
);
831 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s': %m", n
);
833 r
= path_simplify_and_warn(resolved
, PATH_CHECK_ABSOLUTE
| PATH_CHECK_FATAL
, unit
, filename
, line
, lvalue
);
837 free_and_replace(c
->stdio_file
[STDIN_FILENO
], resolved
);
839 ei
= EXEC_INPUT_FILE
;
842 ei
= exec_input_from_string(rvalue
);
844 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse input specifier, ignoring: %s", rvalue
);
853 int config_parse_exec_input_text(
855 const char *filename
,
858 unsigned section_line
,
865 _cleanup_free_
char *unescaped
= NULL
, *resolved
= NULL
;
866 ExecContext
*c
= data
;
877 if (isempty(rvalue
)) {
878 /* Reset if the empty string is assigned */
879 c
->stdin_data
= mfree(c
->stdin_data
);
880 c
->stdin_data_size
= 0;
884 r
= cunescape(rvalue
, 0, &unescaped
);
886 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to decode C escaped text '%s': %m", rvalue
);
888 r
= unit_full_printf(u
, unescaped
, &resolved
);
890 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s': %m", unescaped
);
892 sz
= strlen(resolved
);
893 if (c
->stdin_data_size
+ sz
+ 1 < c
->stdin_data_size
|| /* check for overflow */
894 c
->stdin_data_size
+ sz
+ 1 > EXEC_STDIN_DATA_MAX
) {
895 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c
->stdin_data_size
+ sz
, (size_t) EXEC_STDIN_DATA_MAX
);
899 p
= realloc(c
->stdin_data
, c
->stdin_data_size
+ sz
+ 1);
903 *((char*) mempcpy((char*) p
+ c
->stdin_data_size
, resolved
, sz
)) = '\n';
906 c
->stdin_data_size
+= sz
+ 1;
911 int config_parse_exec_input_data(
913 const char *filename
,
916 unsigned section_line
,
923 _cleanup_free_
void *p
= NULL
;
924 ExecContext
*c
= data
;
934 if (isempty(rvalue
)) {
935 /* Reset if the empty string is assigned */
936 c
->stdin_data
= mfree(c
->stdin_data
);
937 c
->stdin_data_size
= 0;
941 r
= unbase64mem(rvalue
, (size_t) -1, &p
, &sz
);
943 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to decode base64 data, ignoring: %s", rvalue
);
947 if (c
->stdin_data_size
+ sz
< c
->stdin_data_size
|| /* check for overflow */
948 c
->stdin_data_size
+ sz
> EXEC_STDIN_DATA_MAX
) {
949 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c
->stdin_data_size
+ sz
, (size_t) EXEC_STDIN_DATA_MAX
);
953 q
= realloc(c
->stdin_data
, c
->stdin_data_size
+ sz
);
957 memcpy((uint8_t*) q
+ c
->stdin_data_size
, p
, sz
);
960 c
->stdin_data_size
+= sz
;
965 int config_parse_exec_output(
967 const char *filename
,
970 unsigned section_line
,
977 _cleanup_free_
char *resolved
= NULL
;
979 ExecContext
*c
= data
;
990 n
= startswith(rvalue
, "fd:");
992 r
= unit_full_printf(u
, n
, &resolved
);
994 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", n
);
996 if (isempty(resolved
))
997 resolved
= mfree(resolved
);
998 else if (!fdname_is_valid(resolved
)) {
999 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name: %s", resolved
);
1003 eo
= EXEC_OUTPUT_NAMED_FD
;
1005 } else if ((n
= startswith(rvalue
, "file:"))) {
1007 r
= unit_full_printf(u
, n
, &resolved
);
1009 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", n
);
1011 r
= path_simplify_and_warn(resolved
, PATH_CHECK_ABSOLUTE
| PATH_CHECK_FATAL
, unit
, filename
, line
, lvalue
);
1015 eo
= EXEC_OUTPUT_FILE
;
1017 } else if ((n
= startswith(rvalue
, "append:"))) {
1019 r
= unit_full_printf(u
, n
, &resolved
);
1021 return log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", n
);
1023 r
= path_simplify_and_warn(resolved
, PATH_CHECK_ABSOLUTE
| PATH_CHECK_FATAL
, unit
, filename
, line
, lvalue
);
1027 eo
= EXEC_OUTPUT_FILE_APPEND
;
1029 eo
= exec_output_from_string(rvalue
);
1031 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse output specifier, ignoring: %s", rvalue
);
1036 if (streq(lvalue
, "StandardOutput")) {
1037 if (eo
== EXEC_OUTPUT_NAMED_FD
)
1038 free_and_replace(c
->stdio_fdname
[STDOUT_FILENO
], resolved
);
1040 free_and_replace(c
->stdio_file
[STDOUT_FILENO
], resolved
);
1045 assert(streq(lvalue
, "StandardError"));
1047 if (eo
== EXEC_OUTPUT_NAMED_FD
)
1048 free_and_replace(c
->stdio_fdname
[STDERR_FILENO
], resolved
);
1050 free_and_replace(c
->stdio_file
[STDERR_FILENO
], resolved
);
1058 int config_parse_exec_io_class(const char *unit
,
1059 const char *filename
,
1061 const char *section
,
1062 unsigned section_line
,
1069 ExecContext
*c
= data
;
1077 if (isempty(rvalue
)) {
1078 c
->ioprio_set
= false;
1079 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE
, 0);
1083 x
= ioprio_class_from_string(rvalue
);
1085 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
1089 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
1090 c
->ioprio_set
= true;
1095 int config_parse_exec_io_priority(const char *unit
,
1096 const char *filename
,
1098 const char *section
,
1099 unsigned section_line
,
1106 ExecContext
*c
= data
;
1114 if (isempty(rvalue
)) {
1115 c
->ioprio_set
= false;
1116 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE
, 0);
1120 r
= ioprio_parse_priority(rvalue
, &i
);
1122 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
1126 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
1127 c
->ioprio_set
= true;
1132 int config_parse_exec_cpu_sched_policy(const char *unit
,
1133 const char *filename
,
1135 const char *section
,
1136 unsigned section_line
,
1143 ExecContext
*c
= data
;
1151 if (isempty(rvalue
)) {
1152 c
->cpu_sched_set
= false;
1153 c
->cpu_sched_policy
= SCHED_OTHER
;
1154 c
->cpu_sched_priority
= 0;
1158 x
= sched_policy_from_string(rvalue
);
1160 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
1164 c
->cpu_sched_policy
= x
;
1165 /* Moving to or from real-time policy? We need to adjust the priority */
1166 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
1167 c
->cpu_sched_set
= true;
1172 int config_parse_exec_cpu_sched_prio(const char *unit
,
1173 const char *filename
,
1175 const char *section
,
1176 unsigned section_line
,
1183 ExecContext
*c
= data
;
1191 r
= safe_atoi(rvalue
, &i
);
1193 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling priority, ignoring: %s", rvalue
);
1197 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1198 min
= sched_get_priority_min(c
->cpu_sched_policy
);
1199 max
= sched_get_priority_max(c
->cpu_sched_policy
);
1201 if (i
< min
|| i
> max
) {
1202 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
1206 c
->cpu_sched_priority
= i
;
1207 c
->cpu_sched_set
= true;
1212 int config_parse_exec_cpu_affinity(const char *unit
,
1213 const char *filename
,
1215 const char *section
,
1216 unsigned section_line
,
1223 ExecContext
*c
= data
;
1224 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
1232 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
1237 /* An empty assignment resets the CPU list */
1238 c
->cpuset
= cpu_set_mfree(c
->cpuset
);
1239 c
->cpuset_ncpus
= 0;
1244 c
->cpuset
= TAKE_PTR(cpuset
);
1245 c
->cpuset_ncpus
= (unsigned) ncpus
;
1249 if (c
->cpuset_ncpus
< (unsigned) ncpus
) {
1250 CPU_OR_S(CPU_ALLOC_SIZE(c
->cpuset_ncpus
), cpuset
, c
->cpuset
, cpuset
);
1251 CPU_FREE(c
->cpuset
);
1252 c
->cpuset
= TAKE_PTR(cpuset
);
1253 c
->cpuset_ncpus
= (unsigned) ncpus
;
1257 CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus
), c
->cpuset
, c
->cpuset
, cpuset
);
1262 int config_parse_capability_set(
1264 const char *filename
,
1266 const char *section
,
1267 unsigned section_line
,
1274 uint64_t *capability_set
= data
;
1275 uint64_t sum
= 0, initial
= 0;
1276 bool invert
= false;
1284 if (rvalue
[0] == '~') {
1289 if (streq(lvalue
, "CapabilityBoundingSet"))
1290 initial
= CAP_ALL
; /* initialized to all bits on */
1291 /* else "AmbientCapabilities" initialized to all bits off */
1293 r
= capability_set_from_string(rvalue
, &sum
);
1295 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= specifier '%s', ignoring: %m", lvalue
, rvalue
);
1299 if (sum
== 0 || *capability_set
== initial
)
1300 /* "", "~" or uninitialized data -> replace */
1301 *capability_set
= invert
? ~sum
: sum
;
1303 /* previous data -> merge */
1305 *capability_set
&= ~sum
;
1307 *capability_set
|= sum
;
1313 int config_parse_exec_selinux_context(
1315 const char *filename
,
1317 const char *section
,
1318 unsigned section_line
,
1325 ExecContext
*c
= data
;
1336 if (isempty(rvalue
)) {
1337 c
->selinux_context
= mfree(c
->selinux_context
);
1338 c
->selinux_context_ignore
= false;
1342 if (rvalue
[0] == '-') {
1348 r
= unit_full_printf(u
, rvalue
, &k
);
1350 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1351 "Failed to resolve unit specifiers in '%s'%s: %m",
1352 rvalue
, ignore
? ", ignoring" : "");
1353 return ignore
? 0 : -ENOEXEC
;
1356 free_and_replace(c
->selinux_context
, k
);
1357 c
->selinux_context_ignore
= ignore
;
1362 int config_parse_exec_apparmor_profile(
1364 const char *filename
,
1366 const char *section
,
1367 unsigned section_line
,
1374 ExecContext
*c
= data
;
1385 if (isempty(rvalue
)) {
1386 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1387 c
->apparmor_profile_ignore
= false;
1391 if (rvalue
[0] == '-') {
1397 r
= unit_full_printf(u
, rvalue
, &k
);
1399 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1400 "Failed to resolve unit specifiers in '%s'%s: %m",
1401 rvalue
, ignore
? ", ignoring" : "");
1402 return ignore
? 0 : -ENOEXEC
;
1405 free_and_replace(c
->apparmor_profile
, k
);
1406 c
->apparmor_profile_ignore
= ignore
;
1411 int config_parse_exec_smack_process_label(
1413 const char *filename
,
1415 const char *section
,
1416 unsigned section_line
,
1423 ExecContext
*c
= data
;
1434 if (isempty(rvalue
)) {
1435 c
->smack_process_label
= mfree(c
->smack_process_label
);
1436 c
->smack_process_label_ignore
= false;
1440 if (rvalue
[0] == '-') {
1446 r
= unit_full_printf(u
, rvalue
, &k
);
1448 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
1449 "Failed to resolve unit specifiers in '%s'%s: %m",
1450 rvalue
, ignore
? ", ignoring" : "");
1451 return ignore
? 0 : -ENOEXEC
;
1454 free_and_replace(c
->smack_process_label
, k
);
1455 c
->smack_process_label_ignore
= ignore
;
1460 int config_parse_timer(const char *unit
,
1461 const char *filename
,
1463 const char *section
,
1464 unsigned section_line
,
1475 _cleanup_(calendar_spec_freep
) CalendarSpec
*c
= NULL
;
1477 _cleanup_free_
char *k
= NULL
;
1485 if (isempty(rvalue
)) {
1486 /* Empty assignment resets list */
1487 timer_free_values(t
);
1491 b
= timer_base_from_string(lvalue
);
1493 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1497 r
= unit_full_printf(u
, rvalue
, &k
);
1499 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue
);
1503 if (b
== TIMER_CALENDAR
) {
1504 if (calendar_spec_from_string(k
, &c
) < 0) {
1505 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", k
);
1509 if (parse_sec(k
, &usec
) < 0) {
1510 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", k
);
1514 v
= new0(TimerValue
, 1);
1520 v
->calendar_spec
= TAKE_PTR(c
);
1522 LIST_PREPEND(value
, t
->values
, v
);
1527 int config_parse_trigger_unit(
1529 const char *filename
,
1531 const char *section
,
1532 unsigned section_line
,
1539 _cleanup_free_
char *p
= NULL
;
1549 if (!hashmap_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1550 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1554 r
= unit_name_printf(u
, rvalue
, &p
);
1556 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1560 type
= unit_name_to_type(p
);
1562 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1565 if (unit_has_name(u
, p
)) {
1566 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Units cannot trigger themselves, ignoring: %s", rvalue
);
1570 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, true, UNIT_DEPENDENCY_FILE
);
1572 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1579 int config_parse_path_spec(const char *unit
,
1580 const char *filename
,
1582 const char *section
,
1583 unsigned section_line
,
1593 _cleanup_free_
char *k
= NULL
;
1601 if (isempty(rvalue
)) {
1602 /* Empty assignment clears list */
1607 b
= path_type_from_string(lvalue
);
1609 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1613 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1615 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1619 r
= path_simplify_and_warn(k
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
1623 s
= new0(PathSpec
, 1);
1628 s
->path
= TAKE_PTR(k
);
1632 LIST_PREPEND(spec
, p
->specs
, s
);
1637 int config_parse_socket_service(
1639 const char *filename
,
1641 const char *section
,
1642 unsigned section_line
,
1649 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1650 _cleanup_free_
char *p
= NULL
;
1660 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1662 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
1666 if (!endswith(p
, ".service")) {
1667 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service: %s", rvalue
);
1671 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1673 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s: %s", rvalue
, bus_error_message(&error
, r
));
1677 unit_ref_set(&s
->service
, UNIT(s
), x
);
1682 int config_parse_fdname(
1684 const char *filename
,
1686 const char *section
,
1687 unsigned section_line
,
1694 _cleanup_free_
char *p
= NULL
;
1703 if (isempty(rvalue
)) {
1704 s
->fdname
= mfree(s
->fdname
);
1708 r
= unit_full_printf(UNIT(s
), rvalue
, &p
);
1710 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue
);
1714 if (!fdname_is_valid(p
)) {
1715 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1719 return free_and_replace(s
->fdname
, p
);
1722 int config_parse_service_sockets(
1724 const char *filename
,
1726 const char *section
,
1727 unsigned section_line
,
1745 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1747 r
= extract_first_word(&p
, &word
, NULL
, 0);
1753 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1757 r
= unit_name_printf(UNIT(s
), word
, &k
);
1759 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", word
);
1763 if (!endswith(k
, ".socket")) {
1764 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1768 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, true, UNIT_DEPENDENCY_FILE
);
1770 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1772 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, true, UNIT_DEPENDENCY_FILE
);
1774 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1780 int config_parse_bus_name(
1782 const char *filename
,
1784 const char *section
,
1785 unsigned section_line
,
1792 _cleanup_free_
char *k
= NULL
;
1801 r
= unit_full_printf(u
, rvalue
, &k
);
1803 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
1807 if (!service_name_is_valid(k
)) {
1808 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name, ignoring: %s", k
);
1812 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1815 int config_parse_service_timeout(
1817 const char *filename
,
1819 const char *section
,
1820 unsigned section_line
,
1827 Service
*s
= userdata
;
1836 /* This is called for two cases: TimeoutSec= and TimeoutStartSec=. */
1838 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1839 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1840 * all other timeouts. */
1841 r
= parse_sec_fix_0(rvalue
, &usec
);
1843 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1847 s
->start_timeout_defined
= true;
1848 s
->timeout_start_usec
= usec
;
1850 if (streq(lvalue
, "TimeoutSec"))
1851 s
->timeout_stop_usec
= usec
;
1856 int config_parse_sec_fix_0(
1858 const char *filename
,
1860 const char *section
,
1861 unsigned section_line
,
1868 usec_t
*usec
= data
;
1876 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1877 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1880 r
= parse_sec_fix_0(rvalue
, usec
);
1882 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s= parameter, ignoring: %s", lvalue
, rvalue
);
1889 int config_parse_user_group(
1891 const char *filename
,
1893 const char *section
,
1894 unsigned section_line
,
1901 _cleanup_free_
char *k
= NULL
;
1911 if (isempty(rvalue
)) {
1912 *user
= mfree(*user
);
1916 r
= unit_full_printf(u
, rvalue
, &k
);
1918 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", rvalue
);
1922 if (!valid_user_group_name_or_id(k
)) {
1923 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1927 return free_and_replace(*user
, k
);
1930 int config_parse_user_group_strv(
1932 const char *filename
,
1934 const char *section
,
1935 unsigned section_line
,
1942 char ***users
= data
;
1944 const char *p
= rvalue
;
1952 if (isempty(rvalue
)) {
1953 *users
= strv_free(*users
);
1958 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1960 r
= extract_first_word(&p
, &word
, NULL
, 0);
1966 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax: %s", rvalue
);
1970 r
= unit_full_printf(u
, word
, &k
);
1972 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", word
);
1976 if (!valid_user_group_name_or_id(k
)) {
1977 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid user/group name or numeric ID: %s", k
);
1981 r
= strv_push(users
, k
);
1991 int config_parse_working_directory(
1993 const char *filename
,
1995 const char *section
,
1996 unsigned section_line
,
2003 ExecContext
*c
= data
;
2014 if (isempty(rvalue
)) {
2015 c
->working_directory_home
= false;
2016 c
->working_directory
= mfree(c
->working_directory
);
2020 if (rvalue
[0] == '-') {
2026 if (streq(rvalue
, "~")) {
2027 c
->working_directory_home
= true;
2028 c
->working_directory
= mfree(c
->working_directory
);
2030 _cleanup_free_
char *k
= NULL
;
2032 r
= unit_full_printf(u
, rvalue
, &k
);
2034 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2035 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2036 rvalue
, missing_ok
? ", ignoring" : "");
2037 return missing_ok
? 0 : -ENOEXEC
;
2040 r
= path_simplify_and_warn(k
, PATH_CHECK_ABSOLUTE
| (missing_ok
? 0 : PATH_CHECK_FATAL
), unit
, filename
, line
, lvalue
);
2042 return missing_ok
? 0 : -ENOEXEC
;
2044 c
->working_directory_home
= false;
2045 free_and_replace(c
->working_directory
, k
);
2048 c
->working_directory_missing_ok
= missing_ok
;
2052 int config_parse_unit_env_file(const char *unit
,
2053 const char *filename
,
2055 const char *section
,
2056 unsigned section_line
,
2065 _cleanup_free_
char *n
= NULL
;
2073 if (isempty(rvalue
)) {
2074 /* Empty assignment frees the list */
2075 *env
= strv_free(*env
);
2079 r
= unit_full_printf(u
, rvalue
, &n
);
2081 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue
);
2085 r
= path_simplify_and_warn(n
[0] == '-' ? n
+ 1 : n
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
2089 r
= strv_push(env
, n
);
2098 int config_parse_environ(
2100 const char *filename
,
2102 const char *section
,
2103 unsigned section_line
,
2120 if (isempty(rvalue
)) {
2121 /* Empty assignment resets the list */
2122 *env
= strv_free(*env
);
2126 for (p
= rvalue
;; ) {
2127 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2129 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2135 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2136 "Invalid syntax, ignoring: %s", rvalue
);
2141 r
= unit_full_printf(u
, word
, &k
);
2143 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2144 "Failed to resolve unit specifiers in %s, ignoring: %m", word
);
2150 if (!env_assignment_is_valid(k
)) {
2151 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2152 "Invalid environment assignment, ignoring: %s", k
);
2156 r
= strv_env_replace(env
, k
);
2164 int config_parse_pass_environ(
2166 const char *filename
,
2168 const char *section
,
2169 unsigned section_line
,
2176 _cleanup_strv_free_
char **n
= NULL
;
2177 size_t nlen
= 0, nbufsize
= 0;
2178 char*** passenv
= data
;
2179 const char *p
= rvalue
;
2188 if (isempty(rvalue
)) {
2189 /* Empty assignment resets the list */
2190 *passenv
= strv_free(*passenv
);
2195 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2197 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2203 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2204 "Trailing garbage in %s, ignoring: %s", lvalue
, rvalue
);
2209 r
= unit_full_printf(u
, word
, &k
);
2211 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2212 "Failed to resolve specifiers in %s, ignoring: %m", word
);
2218 if (!env_name_is_valid(k
)) {
2219 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2220 "Invalid environment name for %s, ignoring: %s", lvalue
, k
);
2224 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2227 n
[nlen
++] = TAKE_PTR(k
);
2232 r
= strv_extend_strv(passenv
, n
, true);
2240 int config_parse_unset_environ(
2242 const char *filename
,
2244 const char *section
,
2245 unsigned section_line
,
2252 _cleanup_strv_free_
char **n
= NULL
;
2253 size_t nlen
= 0, nbufsize
= 0;
2254 char*** unsetenv
= data
;
2255 const char *p
= rvalue
;
2264 if (isempty(rvalue
)) {
2265 /* Empty assignment resets the list */
2266 *unsetenv
= strv_free(*unsetenv
);
2271 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2273 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2279 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2280 "Trailing garbage in %s, ignoring: %s", lvalue
, rvalue
);
2285 r
= unit_full_printf(u
, word
, &k
);
2287 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2288 "Failed to resolve unit specifiers in %s, ignoring: %m", word
);
2294 if (!env_assignment_is_valid(k
) && !env_name_is_valid(k
)) {
2295 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
2296 "Invalid environment name or assignment %s, ignoring: %s", lvalue
, k
);
2300 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
2303 n
[nlen
++] = TAKE_PTR(k
);
2308 r
= strv_extend_strv(unsetenv
, n
, true);
2316 int config_parse_log_extra_fields(
2318 const char *filename
,
2320 const char *section
,
2321 unsigned section_line
,
2328 ExecContext
*c
= data
;
2330 const char *p
= rvalue
;
2338 if (isempty(rvalue
)) {
2339 exec_context_free_log_extra_fields(c
);
2344 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
2348 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_CUNESCAPE
|EXTRACT_QUOTES
);
2354 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2358 r
= unit_full_printf(u
, word
, &k
);
2360 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", word
);
2364 eq
= strchr(k
, '=');
2366 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field lacks '=' character, ignoring: %s", k
);
2370 if (!journal_field_valid(k
, eq
-k
, false)) {
2371 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Log field name is invalid, ignoring: %s", k
);
2375 t
= reallocarray(c
->log_extra_fields
, c
->n_log_extra_fields
+1, sizeof(struct iovec
));
2379 c
->log_extra_fields
= t
;
2380 c
->log_extra_fields
[c
->n_log_extra_fields
++] = IOVEC_MAKE_STRING(k
);
2386 int config_parse_unit_condition_path(
2388 const char *filename
,
2390 const char *section
,
2391 unsigned section_line
,
2398 _cleanup_free_
char *p
= NULL
;
2399 Condition
**list
= data
, *c
;
2400 ConditionType t
= ltype
;
2401 bool trigger
, negate
;
2410 if (isempty(rvalue
)) {
2411 /* Empty assignment resets the list */
2412 *list
= condition_free_list(*list
);
2416 trigger
= rvalue
[0] == '|';
2420 negate
= rvalue
[0] == '!';
2424 r
= unit_full_printf(u
, rvalue
, &p
);
2426 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
2430 r
= path_simplify_and_warn(p
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
2434 c
= condition_new(t
, p
, trigger
, negate
);
2438 LIST_PREPEND(conditions
, *list
, c
);
2442 int config_parse_unit_condition_string(
2444 const char *filename
,
2446 const char *section
,
2447 unsigned section_line
,
2454 _cleanup_free_
char *s
= NULL
;
2455 Condition
**list
= data
, *c
;
2456 ConditionType t
= ltype
;
2457 bool trigger
, negate
;
2466 if (isempty(rvalue
)) {
2467 /* Empty assignment resets the list */
2468 *list
= condition_free_list(*list
);
2472 trigger
= rvalue
[0] == '|';
2476 negate
= rvalue
[0] == '!';
2480 r
= unit_full_printf(u
, rvalue
, &s
);
2482 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue
);
2486 c
= condition_new(t
, s
, trigger
, negate
);
2490 LIST_PREPEND(conditions
, *list
, c
);
2494 int config_parse_unit_condition_null(
2496 const char *filename
,
2498 const char *section
,
2499 unsigned section_line
,
2506 Condition
**list
= data
, *c
;
2507 bool trigger
, negate
;
2515 if (isempty(rvalue
)) {
2516 /* Empty assignment resets the list */
2517 *list
= condition_free_list(*list
);
2521 trigger
= rvalue
[0] == '|';
2525 negate
= rvalue
[0] == '!';
2529 b
= parse_boolean(rvalue
);
2531 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2538 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2542 LIST_PREPEND(conditions
, *list
, c
);
2546 int config_parse_unit_requires_mounts_for(
2548 const char *filename
,
2550 const char *section
,
2551 unsigned section_line
,
2558 const char *p
= rvalue
;
2568 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
;
2570 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2576 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2577 "Invalid syntax, ignoring: %s", rvalue
);
2581 r
= unit_full_printf(u
, word
, &resolved
);
2583 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", word
);
2587 r
= path_simplify_and_warn(resolved
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
2591 r
= unit_require_mounts_for(u
, resolved
, UNIT_DEPENDENCY_FILE
);
2593 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount '%s', ignoring: %m", resolved
);
2599 int config_parse_documentation(const char *unit
,
2600 const char *filename
,
2602 const char *section
,
2603 unsigned section_line
,
2619 if (isempty(rvalue
)) {
2620 /* Empty assignment resets the list */
2621 u
->documentation
= strv_free(u
->documentation
);
2625 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2626 rvalue
, data
, userdata
);
2630 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2632 if (documentation_url_is_valid(*a
))
2635 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2646 int config_parse_syscall_filter(
2648 const char *filename
,
2650 const char *section
,
2651 unsigned section_line
,
2658 ExecContext
*c
= data
;
2660 bool invert
= false;
2669 if (isempty(rvalue
)) {
2670 /* Empty assignment resets the list */
2671 c
->syscall_filter
= hashmap_free(c
->syscall_filter
);
2672 c
->syscall_whitelist
= false;
2676 if (rvalue
[0] == '~') {
2681 if (!c
->syscall_filter
) {
2682 c
->syscall_filter
= hashmap_new(NULL
);
2683 if (!c
->syscall_filter
)
2687 /* Allow everything but the ones listed */
2688 c
->syscall_whitelist
= false;
2690 /* Allow nothing but the ones listed */
2691 c
->syscall_whitelist
= true;
2693 /* Accept default syscalls if we are on a whitelist */
2694 r
= seccomp_parse_syscall_filter("@default", -1, c
->syscall_filter
, SECCOMP_PARSE_WHITELIST
);
2702 _cleanup_free_
char *word
= NULL
, *name
= NULL
;
2705 r
= extract_first_word(&p
, &word
, NULL
, 0);
2711 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
2715 r
= parse_syscall_and_errno(word
, &name
, &num
);
2717 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
, "Failed to parse syscall:errno, ignoring: %s", word
);
2721 r
= seccomp_parse_syscall_filter_full(name
, num
, c
->syscall_filter
,
2722 SECCOMP_PARSE_LOG
|SECCOMP_PARSE_PERMISSIVE
|(invert
? SECCOMP_PARSE_INVERT
: 0)|(c
->syscall_whitelist
? SECCOMP_PARSE_WHITELIST
: 0),
2723 unit
, filename
, line
);
2729 int config_parse_syscall_archs(
2731 const char *filename
,
2733 const char *section
,
2734 unsigned section_line
,
2741 const char *p
= rvalue
;
2745 if (isempty(rvalue
)) {
2746 *archs
= set_free(*archs
);
2750 r
= set_ensure_allocated(archs
, NULL
);
2755 _cleanup_free_
char *word
= NULL
;
2758 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2764 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2765 "Invalid syntax, ignoring: %s", rvalue
);
2769 r
= seccomp_arch_from_string(word
, &a
);
2771 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
2772 "Failed to parse system call architecture \"%s\", ignoring: %m", word
);
2776 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2782 int config_parse_syscall_errno(
2784 const char *filename
,
2786 const char *section
,
2787 unsigned section_line
,
2794 ExecContext
*c
= data
;
2801 if (isempty(rvalue
)) {
2802 /* Empty assignment resets to KILL */
2803 c
->syscall_errno
= 0;
2807 e
= parse_errno(rvalue
);
2809 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2813 c
->syscall_errno
= e
;
2817 int config_parse_address_families(
2819 const char *filename
,
2821 const char *section
,
2822 unsigned section_line
,
2829 ExecContext
*c
= data
;
2830 bool invert
= false;
2838 if (isempty(rvalue
)) {
2839 /* Empty assignment resets the list */
2840 c
->address_families
= set_free(c
->address_families
);
2841 c
->address_families_whitelist
= false;
2845 if (rvalue
[0] == '~') {
2850 if (!c
->address_families
) {
2851 c
->address_families
= set_new(NULL
);
2852 if (!c
->address_families
)
2855 c
->address_families_whitelist
= !invert
;
2858 for (p
= rvalue
;;) {
2859 _cleanup_free_
char *word
= NULL
;
2862 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
2868 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
2869 "Invalid syntax, ignoring: %s", rvalue
);
2873 af
= af_from_name(word
);
2875 log_syntax(unit
, LOG_ERR
, filename
, line
, af
,
2876 "Failed to parse address family, ignoring: %s", word
);
2880 /* If we previously wanted to forbid an address family and now
2881 * we want to allow it, then just remove it from the list.
2883 if (!invert
== c
->address_families_whitelist
) {
2884 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2888 set_remove(c
->address_families
, INT_TO_PTR(af
));
2892 int config_parse_restrict_namespaces(
2894 const char *filename
,
2896 const char *section
,
2897 unsigned section_line
,
2904 ExecContext
*c
= data
;
2905 unsigned long flags
;
2906 bool invert
= false;
2909 if (isempty(rvalue
)) {
2910 /* Reset to the default. */
2911 c
->restrict_namespaces
= NAMESPACE_FLAGS_INITIAL
;
2915 /* Boolean parameter ignores the previous settings */
2916 r
= parse_boolean(rvalue
);
2918 c
->restrict_namespaces
= 0;
2920 } else if (r
== 0) {
2921 c
->restrict_namespaces
= NAMESPACE_FLAGS_ALL
;
2925 if (rvalue
[0] == '~') {
2930 /* Not a boolean argument, in this case it's a list of namespace types. */
2931 r
= namespace_flags_from_string(rvalue
, &flags
);
2933 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse namespace type string, ignoring: %s", rvalue
);
2937 if (c
->restrict_namespaces
== NAMESPACE_FLAGS_INITIAL
)
2938 /* Initial assignment. Just set the value. */
2939 c
->restrict_namespaces
= invert
? (~flags
) & NAMESPACE_FLAGS_ALL
: flags
;
2941 /* Merge the value with the previous one. */
2942 SET_FLAG(c
->restrict_namespaces
, flags
, !invert
);
2948 int config_parse_unit_slice(
2950 const char *filename
,
2952 const char *section
,
2953 unsigned section_line
,
2960 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2961 _cleanup_free_
char *k
= NULL
;
2962 Unit
*u
= userdata
, *slice
= NULL
;
2970 r
= unit_name_printf(u
, rvalue
, &k
);
2972 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue
);
2976 r
= manager_load_unit(u
->manager
, k
, NULL
, &error
, &slice
);
2978 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s, ignoring: %s", k
, bus_error_message(&error
, r
));
2982 r
= unit_set_slice(u
, slice
);
2984 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s, ignoring: %m", slice
->id
, u
->id
);
2991 int config_parse_cpu_quota(
2993 const char *filename
,
2995 const char *section
,
2996 unsigned section_line
,
3003 CGroupContext
*c
= data
;
3010 if (isempty(rvalue
)) {
3011 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
3015 r
= parse_permille_unbounded(rvalue
);
3017 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid CPU quota '%s', ignoring.", rvalue
);
3021 c
->cpu_quota_per_sec_usec
= ((usec_t
) r
* USEC_PER_SEC
) / 1000U;
3025 int config_parse_memory_limit(
3027 const char *filename
,
3029 const char *section
,
3030 unsigned section_line
,
3037 CGroupContext
*c
= data
;
3038 uint64_t bytes
= CGROUP_LIMIT_MAX
;
3041 if (!isempty(rvalue
) && !streq(rvalue
, "infinity")) {
3043 r
= parse_permille(rvalue
);
3045 r
= parse_size(rvalue
, 1024, &bytes
);
3047 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid memory limit '%s', ignoring: %m", rvalue
);
3051 bytes
= physical_memory_scale(r
, 1000U);
3053 if (bytes
>= UINT64_MAX
||
3054 (bytes
<= 0 && !streq(lvalue
, "MemorySwapMax"))) {
3055 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Memory limit '%s' out of range, ignoring.", rvalue
);
3060 if (streq(lvalue
, "MemoryMin"))
3061 c
->memory_min
= bytes
;
3062 else if (streq(lvalue
, "MemoryLow"))
3063 c
->memory_low
= bytes
;
3064 else if (streq(lvalue
, "MemoryHigh"))
3065 c
->memory_high
= bytes
;
3066 else if (streq(lvalue
, "MemoryMax"))
3067 c
->memory_max
= bytes
;
3068 else if (streq(lvalue
, "MemorySwapMax"))
3069 c
->memory_swap_max
= bytes
;
3070 else if (streq(lvalue
, "MemoryLimit"))
3071 c
->memory_limit
= bytes
;
3078 int config_parse_tasks_max(
3080 const char *filename
,
3082 const char *section
,
3083 unsigned section_line
,
3090 uint64_t *tasks_max
= data
, v
;
3094 if (isempty(rvalue
)) {
3095 *tasks_max
= u
? u
->manager
->default_tasks_max
: UINT64_MAX
;
3099 if (streq(rvalue
, "infinity")) {
3100 *tasks_max
= CGROUP_LIMIT_MAX
;
3104 r
= parse_permille(rvalue
);
3106 r
= safe_atou64(rvalue
, &v
);
3108 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid maximum tasks value '%s', ignoring: %m", rvalue
);
3112 v
= system_tasks_max_scale(r
, 1000U);
3114 if (v
<= 0 || v
>= UINT64_MAX
) {
3115 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue
);
3123 int config_parse_delegate(
3125 const char *filename
,
3127 const char *section
,
3128 unsigned section_line
,
3135 CGroupContext
*c
= data
;
3139 t
= unit_name_to_type(unit
);
3140 assert(t
!= _UNIT_TYPE_INVALID
);
3142 if (!unit_vtable
[t
]->can_delegate
) {
3143 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Delegate= setting not supported for this unit type, ignoring.");
3147 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3148 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3149 * mask to delegate. */
3151 if (isempty(rvalue
)) {
3152 /* An empty string resets controllers and set Delegate=yes. */
3154 c
->delegate_controllers
= 0;
3158 r
= parse_boolean(rvalue
);
3160 const char *p
= rvalue
;
3161 CGroupMask mask
= 0;
3164 _cleanup_free_
char *word
= NULL
;
3165 CGroupController cc
;
3167 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3173 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
3177 cc
= cgroup_controller_from_string(word
);
3179 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid controller name '%s', ignoring", word
);
3183 mask
|= CGROUP_CONTROLLER_TO_MASK(cc
);
3187 c
->delegate_controllers
|= mask
;
3191 c
->delegate_controllers
= _CGROUP_MASK_ALL
;
3193 c
->delegate
= false;
3194 c
->delegate_controllers
= 0;
3200 int config_parse_device_allow(
3202 const char *filename
,
3204 const char *section
,
3205 unsigned section_line
,
3212 _cleanup_free_
char *path
= NULL
, *resolved
= NULL
;
3213 CGroupContext
*c
= data
;
3214 const char *p
= rvalue
;
3217 if (isempty(rvalue
)) {
3218 while (c
->device_allow
)
3219 cgroup_context_free_device_allow(c
, c
->device_allow
);
3224 r
= extract_first_word(&p
, &path
, NULL
, EXTRACT_QUOTES
);
3228 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3229 "Invalid syntax, ignoring: %s", rvalue
);
3233 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
3234 "Failed to extract device path and rights from '%s', ignoring.", rvalue
);
3238 r
= unit_full_printf(userdata
, path
, &resolved
);
3240 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3241 "Failed to resolve unit specifiers in '%s', ignoring: %m", path
);
3245 if (!STARTSWITH_SET(resolved
, "block-", "char-")) {
3247 r
= path_simplify_and_warn(resolved
, 0, unit
, filename
, line
, lvalue
);
3251 if (!valid_device_node_path(resolved
)) {
3252 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s', ignoring.", resolved
);
3257 if (!isempty(p
) && !in_charset(p
, "rwm")) {
3258 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s', ignoring.", p
);
3262 return cgroup_add_device_allow(c
, resolved
, p
);
3265 int config_parse_io_device_weight(
3267 const char *filename
,
3269 const char *section
,
3270 unsigned section_line
,
3277 _cleanup_free_
char *path
= NULL
, *resolved
= NULL
;
3278 CGroupIODeviceWeight
*w
;
3279 CGroupContext
*c
= data
;
3280 const char *p
= rvalue
;
3288 if (isempty(rvalue
)) {
3289 while (c
->io_device_weights
)
3290 cgroup_context_free_io_device_weight(c
, c
->io_device_weights
);
3295 r
= extract_first_word(&p
, &path
, NULL
, EXTRACT_QUOTES
);
3299 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3300 "Invalid syntax, ignoring: %s", rvalue
);
3303 if (r
== 0 || isempty(p
)) {
3304 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
3305 "Failed to extract device path and weight from '%s', ignoring.", rvalue
);
3309 r
= unit_full_printf(userdata
, path
, &resolved
);
3311 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3312 "Failed to resolve unit specifiers in '%s', ignoring: %m", path
);
3316 r
= path_simplify_and_warn(resolved
, 0, unit
, filename
, line
, lvalue
);
3320 r
= cg_weight_parse(p
, &u
);
3322 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "IO weight '%s' invalid, ignoring: %m", p
);
3326 assert(u
!= CGROUP_WEIGHT_INVALID
);
3328 w
= new0(CGroupIODeviceWeight
, 1);
3332 w
->path
= TAKE_PTR(resolved
);
3335 LIST_PREPEND(device_weights
, c
->io_device_weights
, w
);
3339 int config_parse_io_device_latency(
3341 const char *filename
,
3343 const char *section
,
3344 unsigned section_line
,
3351 _cleanup_free_
char *path
= NULL
, *resolved
= NULL
;
3352 CGroupIODeviceLatency
*l
;
3353 CGroupContext
*c
= data
;
3354 const char *p
= rvalue
;
3362 if (isempty(rvalue
)) {
3363 while (c
->io_device_latencies
)
3364 cgroup_context_free_io_device_latency(c
, c
->io_device_latencies
);
3369 r
= extract_first_word(&p
, &path
, NULL
, EXTRACT_QUOTES
);
3373 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3374 "Invalid syntax, ignoring: %s", rvalue
);
3377 if (r
== 0 || isempty(p
)) {
3378 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
3379 "Failed to extract device path and latency from '%s', ignoring.", rvalue
);
3383 r
= unit_full_printf(userdata
, path
, &resolved
);
3385 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3386 "Failed to resolve unit specifiers in '%s', ignoring: %m", path
);
3390 r
= path_simplify_and_warn(resolved
, 0, unit
, filename
, line
, lvalue
);
3394 if (parse_sec(p
, &usec
) < 0) {
3395 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", p
);
3399 l
= new0(CGroupIODeviceLatency
, 1);
3403 l
->path
= TAKE_PTR(resolved
);
3404 l
->target_usec
= usec
;
3406 LIST_PREPEND(device_latencies
, c
->io_device_latencies
, l
);
3410 int config_parse_io_limit(
3412 const char *filename
,
3414 const char *section
,
3415 unsigned section_line
,
3422 _cleanup_free_
char *path
= NULL
, *resolved
= NULL
;
3423 CGroupIODeviceLimit
*l
= NULL
, *t
;
3424 CGroupContext
*c
= data
;
3425 CGroupIOLimitType type
;
3426 const char *p
= rvalue
;
3434 type
= cgroup_io_limit_type_from_string(lvalue
);
3437 if (isempty(rvalue
)) {
3438 LIST_FOREACH(device_limits
, l
, c
->io_device_limits
)
3439 l
->limits
[type
] = cgroup_io_limit_defaults
[type
];
3443 r
= extract_first_word(&p
, &path
, NULL
, EXTRACT_QUOTES
);
3447 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3448 "Invalid syntax, ignoring: %s", rvalue
);
3451 if (r
== 0 || isempty(p
)) {
3452 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
3453 "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue
);
3457 r
= unit_full_printf(userdata
, path
, &resolved
);
3459 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3460 "Failed to resolve unit specifiers in '%s', ignoring: %m", path
);
3464 r
= path_simplify_and_warn(resolved
, 0, unit
, filename
, line
, lvalue
);
3468 if (streq("infinity", p
))
3469 num
= CGROUP_LIMIT_MAX
;
3471 r
= parse_size(p
, 1000, &num
);
3472 if (r
< 0 || num
<= 0) {
3473 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid IO limit '%s', ignoring.", p
);
3478 LIST_FOREACH(device_limits
, t
, c
->io_device_limits
) {
3479 if (path_equal(resolved
, t
->path
)) {
3486 CGroupIOLimitType ttype
;
3488 l
= new0(CGroupIODeviceLimit
, 1);
3492 l
->path
= TAKE_PTR(resolved
);
3493 for (ttype
= 0; ttype
< _CGROUP_IO_LIMIT_TYPE_MAX
; ttype
++)
3494 l
->limits
[ttype
] = cgroup_io_limit_defaults
[ttype
];
3496 LIST_PREPEND(device_limits
, c
->io_device_limits
, l
);
3499 l
->limits
[type
] = num
;
3504 int config_parse_blockio_device_weight(
3506 const char *filename
,
3508 const char *section
,
3509 unsigned section_line
,
3516 _cleanup_free_
char *path
= NULL
, *resolved
= NULL
;
3517 CGroupBlockIODeviceWeight
*w
;
3518 CGroupContext
*c
= data
;
3519 const char *p
= rvalue
;
3527 if (isempty(rvalue
)) {
3528 while (c
->blockio_device_weights
)
3529 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3534 r
= extract_first_word(&p
, &path
, NULL
, EXTRACT_QUOTES
);
3538 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3539 "Invalid syntax, ignoring: %s", rvalue
);
3542 if (r
== 0 || isempty(p
)) {
3543 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
3544 "Failed to extract device node and weight from '%s', ignoring.", rvalue
);
3548 r
= unit_full_printf(userdata
, path
, &resolved
);
3550 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3551 "Failed to resolve unit specifiers in '%s', ignoring: %m", path
);
3555 r
= path_simplify_and_warn(resolved
, 0, unit
, filename
, line
, lvalue
);
3559 r
= cg_blkio_weight_parse(p
, &u
);
3561 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid block IO weight '%s', ignoring: %m", p
);
3565 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3567 w
= new0(CGroupBlockIODeviceWeight
, 1);
3571 w
->path
= TAKE_PTR(resolved
);
3574 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3578 int config_parse_blockio_bandwidth(
3580 const char *filename
,
3582 const char *section
,
3583 unsigned section_line
,
3590 _cleanup_free_
char *path
= NULL
, *resolved
= NULL
;
3591 CGroupBlockIODeviceBandwidth
*b
= NULL
, *t
;
3592 CGroupContext
*c
= data
;
3593 const char *p
= rvalue
;
3602 read
= streq("BlockIOReadBandwidth", lvalue
);
3604 if (isempty(rvalue
)) {
3605 LIST_FOREACH(device_bandwidths
, b
, c
->blockio_device_bandwidths
) {
3606 b
->rbps
= CGROUP_LIMIT_MAX
;
3607 b
->wbps
= CGROUP_LIMIT_MAX
;
3612 r
= extract_first_word(&p
, &path
, NULL
, EXTRACT_QUOTES
);
3616 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3617 "Invalid syntax, ignoring: %s", rvalue
);
3620 if (r
== 0 || isempty(p
)) {
3621 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
3622 "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue
);
3626 r
= unit_full_printf(userdata
, path
, &resolved
);
3628 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3629 "Failed to resolve unit specifiers in '%s', ignoring: %m", path
);
3633 r
= path_simplify_and_warn(resolved
, 0, unit
, filename
, line
, lvalue
);
3637 r
= parse_size(p
, 1000, &bytes
);
3638 if (r
< 0 || bytes
<= 0) {
3639 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid Block IO Bandwidth '%s', ignoring.", p
);
3643 LIST_FOREACH(device_bandwidths
, t
, c
->blockio_device_bandwidths
) {
3644 if (path_equal(resolved
, t
->path
)) {
3651 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3655 b
->path
= TAKE_PTR(resolved
);
3656 b
->rbps
= CGROUP_LIMIT_MAX
;
3657 b
->wbps
= CGROUP_LIMIT_MAX
;
3659 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3670 int config_parse_job_mode_isolate(
3672 const char *filename
,
3674 const char *section
,
3675 unsigned section_line
,
3689 r
= parse_boolean(rvalue
);
3691 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3695 log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue
);
3697 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3701 int config_parse_exec_directories(
3703 const char *filename
,
3705 const char *section
,
3706 unsigned section_line
,
3723 if (isempty(rvalue
)) {
3724 /* Empty assignment resets the list */
3725 *rt
= strv_free(*rt
);
3729 for (p
= rvalue
;;) {
3730 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
3732 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3736 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
3737 "Invalid syntax, ignoring: %s", rvalue
);
3743 r
= unit_full_printf(u
, word
, &k
);
3745 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
3746 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word
);
3750 r
= path_simplify_and_warn(k
, PATH_CHECK_RELATIVE
, unit
, filename
, line
, lvalue
);
3754 if (path_startswith(k
, "private")) {
3755 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3756 "%s= path can't be 'private', ingoring assignment: %s", lvalue
, word
);
3760 r
= strv_push(rt
, k
);
3767 int config_parse_set_status(
3769 const char *filename
,
3771 const char *section
,
3772 unsigned section_line
,
3780 const char *word
, *state
;
3782 ExitStatusSet
*status_set
= data
;
3789 /* Empty assignment resets the list */
3790 if (isempty(rvalue
)) {
3791 exit_status_set_free(status_set
);
3795 FOREACH_WORD(word
, l
, rvalue
, state
) {
3796 _cleanup_free_
char *temp
;
3800 temp
= strndup(word
, l
);
3804 r
= safe_atoi(temp
, &val
);
3806 val
= signal_from_string(temp
);
3809 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3812 set
= &status_set
->signal
;
3814 if (val
< 0 || val
> 255) {
3815 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3818 set
= &status_set
->status
;
3821 r
= set_ensure_allocated(set
, NULL
);
3825 r
= set_put(*set
, INT_TO_PTR(val
));
3829 if (!isempty(state
))
3830 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3835 int config_parse_namespace_path_strv(
3837 const char *filename
,
3839 const char *section
,
3840 unsigned section_line
,
3849 const char *p
= rvalue
;
3857 if (isempty(rvalue
)) {
3858 /* Empty assignment resets the list */
3859 *sv
= strv_free(*sv
);
3864 _cleanup_free_
char *word
= NULL
, *resolved
= NULL
, *joined
= NULL
;
3866 bool ignore_enoent
= false, shall_prefix
= false;
3868 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3874 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
3879 if (startswith(w
, "-")) {
3880 ignore_enoent
= true;
3883 if (startswith(w
, "+")) {
3884 shall_prefix
= true;
3888 r
= unit_full_printf(u
, w
, &resolved
);
3890 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s: %m", w
);
3894 r
= path_simplify_and_warn(resolved
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
3898 joined
= strjoin(ignore_enoent
? "-" : "",
3899 shall_prefix
? "+" : "",
3902 r
= strv_push(sv
, joined
);
3912 int config_parse_temporary_filesystems(
3914 const char *filename
,
3916 const char *section
,
3917 unsigned section_line
,
3925 ExecContext
*c
= data
;
3926 const char *p
= rvalue
;
3934 if (isempty(rvalue
)) {
3935 /* Empty assignment resets the list */
3936 temporary_filesystem_free_many(c
->temporary_filesystems
, c
->n_temporary_filesystems
);
3937 c
->temporary_filesystems
= NULL
;
3938 c
->n_temporary_filesystems
= 0;
3943 _cleanup_free_
char *word
= NULL
, *path
= NULL
, *resolved
= NULL
;
3946 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
3952 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", rvalue
);
3957 r
= extract_first_word(&w
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
3961 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to extract first word, ignoring: %s", word
);
3965 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring: %s", word
);
3969 r
= unit_full_printf(u
, path
, &resolved
);
3971 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in %s, ignoring: %m", path
);
3975 r
= path_simplify_and_warn(resolved
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
3979 r
= temporary_filesystem_add(&c
->temporary_filesystems
, &c
->n_temporary_filesystems
, resolved
, w
);
3985 int config_parse_bind_paths(
3987 const char *filename
,
3989 const char *section
,
3990 unsigned section_line
,
3997 ExecContext
*c
= data
;
4007 if (isempty(rvalue
)) {
4008 /* Empty assignment resets the list */
4009 bind_mount_free_many(c
->bind_mounts
, c
->n_bind_mounts
);
4010 c
->bind_mounts
= NULL
;
4011 c
->n_bind_mounts
= 0;
4017 _cleanup_free_
char *source
= NULL
, *destination
= NULL
;
4018 _cleanup_free_
char *sresolved
= NULL
, *dresolved
= NULL
;
4019 char *s
= NULL
, *d
= NULL
;
4020 bool rbind
= true, ignore_enoent
= false;
4022 r
= extract_first_word(&p
, &source
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4028 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s, ignoring: %s", lvalue
, rvalue
);
4032 r
= unit_full_printf(u
, source
, &sresolved
);
4034 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4035 "Failed to resolved unit specifiers in \"%s\", ignoring: %m", source
);
4041 ignore_enoent
= true;
4045 r
= path_simplify_and_warn(s
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
4049 /* Optionally, the destination is specified. */
4050 if (p
&& p
[-1] == ':') {
4051 r
= extract_first_word(&p
, &destination
, ":" WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_DONT_COALESCE_SEPARATORS
);
4055 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s, ignoring: %s", lvalue
, rvalue
);
4059 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Missing argument after ':', ignoring: %s", s
);
4063 r
= unit_full_printf(u
, destination
, &dresolved
);
4065 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4066 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination
);
4070 r
= path_simplify_and_warn(dresolved
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
4076 /* Optionally, there's also a short option string specified */
4077 if (p
&& p
[-1] == ':') {
4078 _cleanup_free_
char *options
= NULL
;
4080 r
= extract_first_word(&p
, &options
, NULL
, EXTRACT_QUOTES
);
4084 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse %s: %s", lvalue
, rvalue
);
4088 if (isempty(options
) || streq(options
, "rbind"))
4090 else if (streq(options
, "norbind"))
4093 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid option string, ignoring setting: %s", options
);
4100 r
= bind_mount_add(&c
->bind_mounts
, &c
->n_bind_mounts
,
4104 .read_only
= !!strstr(lvalue
, "ReadOnly"),
4106 .ignore_enoent
= ignore_enoent
,
4115 int config_parse_job_timeout_sec(
4117 const char *filename
,
4119 const char *section
,
4120 unsigned section_line
,
4136 r
= parse_sec_fix_0(rvalue
, &usec
);
4138 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue
);
4142 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
4143 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
4146 if (!u
->job_running_timeout_set
)
4147 u
->job_running_timeout
= usec
;
4149 u
->job_timeout
= usec
;
4154 int config_parse_job_running_timeout_sec(
4156 const char *filename
,
4158 const char *section
,
4159 unsigned section_line
,
4175 r
= parse_sec_fix_0(rvalue
, &usec
);
4177 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue
);
4181 u
->job_running_timeout
= usec
;
4182 u
->job_running_timeout_set
= true;
4187 int config_parse_emergency_action(
4189 const char *filename
,
4191 const char *section
,
4192 unsigned section_line
,
4200 EmergencyAction
*x
= data
;
4209 m
= ((Unit
*) userdata
)->manager
;
4213 r
= parse_emergency_action(rvalue
, MANAGER_IS_SYSTEM(m
), x
);
4215 if (r
== -EOPNOTSUPP
&& MANAGER_IS_USER(m
)) {
4216 /* Compat mode: remove for systemd 241. */
4218 log_syntax(unit
, LOG_INFO
, filename
, line
, r
,
4219 "%s= in user mode specified as \"%s\", using \"exit-force\" instead.",
4221 *x
= EMERGENCY_ACTION_EXIT_FORCE
;
4225 if (r
== -EOPNOTSUPP
)
4226 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4227 "%s= specified as %s mode action, ignoring: %s",
4228 lvalue
, MANAGER_IS_SYSTEM(m
) ? "user" : "system", rvalue
);
4230 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
4231 "Failed to parse %s=, ignoring: %s", lvalue
, rvalue
);
4238 int config_parse_pid_file(
4240 const char *filename
,
4242 const char *section
,
4243 unsigned section_line
,
4250 _cleanup_free_
char *k
= NULL
, *n
= NULL
;
4261 r
= unit_full_printf(u
, rvalue
, &k
);
4263 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue
);
4267 /* If this is a relative path make it absolute by prefixing the /run */
4268 n
= path_make_absolute(k
, u
->manager
->prefix
[EXEC_DIRECTORY_RUNTIME
]);
4272 /* Check that the result is a sensible path */
4273 r
= path_simplify_and_warn(n
, PATH_CHECK_ABSOLUTE
, unit
, filename
, line
, lvalue
);
4277 e
= path_startswith(n
, "/var/run/");
4281 z
= strjoin("/run/", e
);
4285 log_syntax(unit
, LOG_NOTICE
, filename
, line
, 0, "PIDFile= references path below legacy directory /var/run/, updating %s → %s; please update the unit file accordingly.", n
, z
);
4287 free_and_replace(*s
, z
);
4289 free_and_replace(*s
, n
);
4294 int config_parse_exit_status(
4296 const char *filename
,
4298 const char *section
,
4299 unsigned section_line
,
4306 int *exit_status
= data
, r
;
4312 assert(exit_status
);
4314 if (isempty(rvalue
)) {
4319 r
= safe_atou8(rvalue
, &u
);
4321 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse exit status '%s', ignoring: %m", rvalue
);
4329 #define FOLLOW_MAX 8
4331 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
4342 /* This will update the filename pointer if the loaded file is
4343 * reached by a symlink. The old string will be freed. */
4346 char *target
, *name
;
4348 if (c
++ >= FOLLOW_MAX
)
4351 path_simplify(*filename
, false);
4353 /* Add the file name we are currently looking at to
4354 * the names of this unit, but only if it is a valid
4356 name
= basename(*filename
);
4357 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
4359 id
= set_get(names
, name
);
4365 r
= set_consume(names
, id
);
4371 /* Try to open the file name, but don't if its a symlink */
4372 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
4379 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4380 r
= readlink_and_make_absolute(*filename
, &target
);
4384 free_and_replace(*filename
, target
);
4387 f
= fdopen(fd
, "re");
4399 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
4407 /* Let's try to add in all symlink names we found */
4408 while ((k
= set_steal_first(names
))) {
4410 /* First try to merge in the other name into our
4412 r
= unit_merge_by_name(*u
, k
);
4416 /* Hmm, we couldn't merge the other unit into
4417 * ours? Then let's try it the other way
4420 /* If the symlink name we are looking at is unit template, then
4421 we must search for instance of this template */
4422 if (unit_name_is_valid(k
, UNIT_NAME_TEMPLATE
) && (*u
)->instance
) {
4423 _cleanup_free_
char *instance
= NULL
;
4425 r
= unit_name_replace_instance(k
, (*u
)->instance
, &instance
);
4429 other
= manager_get_unit((*u
)->manager
, instance
);
4431 other
= manager_get_unit((*u
)->manager
, k
);
4436 r
= unit_merge(other
, *u
);
4439 return merge_by_names(u
, names
, NULL
);
4447 unit_choose_id(*u
, id
);
4455 static int load_from_path(Unit
*u
, const char *path
) {
4456 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
4457 _cleanup_fclose_
FILE *f
= NULL
;
4458 _cleanup_free_
char *filename
= NULL
;
4467 symlink_names
= set_new(&string_hash_ops
);
4471 if (path_is_absolute(path
)) {
4473 filename
= strdup(path
);
4477 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4479 filename
= mfree(filename
);
4487 STRV_FOREACH(p
, u
->manager
->lookup_paths
.search_path
) {
4489 /* Instead of opening the path right away, we manually
4490 * follow all symlinks and add their name to our unit
4491 * name set while doing so */
4492 filename
= path_make_absolute(path
, *p
);
4496 if (u
->manager
->unit_path_cache
&&
4497 !set_get(u
->manager
->unit_path_cache
, filename
))
4500 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
4503 filename
= mfree(filename
);
4505 /* ENOENT means that the file is missing or is a dangling symlink.
4506 * ENOTDIR means that one of paths we expect to be is a directory
4507 * is not a directory, we should just ignore that.
4508 * EACCES means that the directory or file permissions are wrong.
4511 log_debug_errno(r
, "Cannot access \"%s\": %m", filename
);
4512 else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
))
4515 /* Empty the symlink names for the next run */
4516 set_clear_free(symlink_names
);
4521 /* Hmm, no suitable file found? */
4524 if (!unit_type_may_alias(u
->type
) && set_size(symlink_names
) > 1) {
4525 log_unit_warning(u
, "Unit type of %s does not support alias names, refusing loading via symlink.", u
->id
);
4530 r
= merge_by_names(&merged
, symlink_names
, id
);
4535 u
->load_state
= UNIT_MERGED
;
4539 if (fstat(fileno(f
), &st
) < 0)
4542 if (null_or_empty(&st
)) {
4543 u
->load_state
= UNIT_MASKED
;
4544 u
->fragment_mtime
= 0;
4546 u
->load_state
= UNIT_LOADED
;
4547 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
4549 /* Now, parse the file contents */
4550 r
= config_parse(u
->id
, filename
, f
,
4551 UNIT_VTABLE(u
)->sections
,
4552 config_item_perf_lookup
, load_fragment_gperf_lookup
,
4553 CONFIG_PARSE_ALLOW_INCLUDE
, u
);
4558 free_and_replace(u
->fragment_path
, filename
);
4560 if (u
->source_path
) {
4561 if (stat(u
->source_path
, &st
) >= 0)
4562 u
->source_mtime
= timespec_load(&st
.st_mtim
);
4564 u
->source_mtime
= 0;
4570 int unit_load_fragment(Unit
*u
) {
4576 assert(u
->load_state
== UNIT_STUB
);
4580 u
->load_state
= UNIT_LOADED
;
4584 /* First, try to find the unit under its id. We always look
4585 * for unit files in the default directories, to make it easy
4586 * to override things by placing things in /etc/systemd/system */
4587 r
= load_from_path(u
, u
->id
);
4591 /* Try to find an alias we can load this with */
4592 if (u
->load_state
== UNIT_STUB
) {
4593 SET_FOREACH(t
, u
->names
, i
) {
4598 r
= load_from_path(u
, t
);
4602 if (u
->load_state
!= UNIT_STUB
)
4607 /* And now, try looking for it under the suggested (originally linked) path */
4608 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
4610 r
= load_from_path(u
, u
->fragment_path
);
4614 if (u
->load_state
== UNIT_STUB
)
4615 /* Hmm, this didn't work? Then let's get rid
4616 * of the fragment path stored for us, so that
4617 * we don't point to an invalid location. */
4618 u
->fragment_path
= mfree(u
->fragment_path
);
4621 /* Look for a template */
4622 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
4623 _cleanup_free_
char *k
= NULL
;
4625 r
= unit_name_template(u
->id
, &k
);
4629 r
= load_from_path(u
, k
);
4632 log_unit_notice(u
, "Unit configuration has fatal error, unit will not be started.");
4636 if (u
->load_state
== UNIT_STUB
) {
4637 SET_FOREACH(t
, u
->names
, i
) {
4638 _cleanup_free_
char *z
= NULL
;
4643 r
= unit_name_template(t
, &z
);
4647 r
= load_from_path(u
, z
);
4651 if (u
->load_state
!= UNIT_STUB
)
4660 void unit_dump_config_items(FILE *f
) {
4661 static const struct {
4662 const ConfigParserCallback callback
;
4665 { config_parse_warn_compat
, "NOTSUPPORTED" },
4666 { config_parse_int
, "INTEGER" },
4667 { config_parse_unsigned
, "UNSIGNED" },
4668 { config_parse_iec_size
, "SIZE" },
4669 { config_parse_iec_uint64
, "SIZE" },
4670 { config_parse_si_size
, "SIZE" },
4671 { config_parse_bool
, "BOOLEAN" },
4672 { config_parse_string
, "STRING" },
4673 { config_parse_path
, "PATH" },
4674 { config_parse_unit_path_printf
, "PATH" },
4675 { config_parse_strv
, "STRING [...]" },
4676 { config_parse_exec_nice
, "NICE" },
4677 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
4678 { config_parse_exec_io_class
, "IOCLASS" },
4679 { config_parse_exec_io_priority
, "IOPRIORITY" },
4680 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
4681 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
4682 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
4683 { config_parse_mode
, "MODE" },
4684 { config_parse_unit_env_file
, "FILE" },
4685 { config_parse_exec_output
, "OUTPUT" },
4686 { config_parse_exec_input
, "INPUT" },
4687 { config_parse_log_facility
, "FACILITY" },
4688 { config_parse_log_level
, "LEVEL" },
4689 { config_parse_exec_secure_bits
, "SECUREBITS" },
4690 { config_parse_capability_set
, "BOUNDINGSET" },
4691 { config_parse_rlimit
, "LIMIT" },
4692 { config_parse_unit_deps
, "UNIT [...]" },
4693 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
4694 { config_parse_service_type
, "SERVICETYPE" },
4695 { config_parse_service_restart
, "SERVICERESTART" },
4696 { config_parse_kill_mode
, "KILLMODE" },
4697 { config_parse_signal
, "SIGNAL" },
4698 { config_parse_socket_listen
, "SOCKET [...]" },
4699 { config_parse_socket_bind
, "SOCKETBIND" },
4700 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
4701 { config_parse_sec
, "SECONDS" },
4702 { config_parse_nsec
, "NANOSECONDS" },
4703 { config_parse_namespace_path_strv
, "PATH [...]" },
4704 { config_parse_bind_paths
, "PATH[:PATH[:OPTIONS]] [...]" },
4705 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
4706 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
4707 { config_parse_unit_string_printf
, "STRING" },
4708 { config_parse_trigger_unit
, "UNIT" },
4709 { config_parse_timer
, "TIMER" },
4710 { config_parse_path_spec
, "PATH" },
4711 { config_parse_notify_access
, "ACCESS" },
4712 { config_parse_ip_tos
, "TOS" },
4713 { config_parse_unit_condition_path
, "CONDITION" },
4714 { config_parse_unit_condition_string
, "CONDITION" },
4715 { config_parse_unit_condition_null
, "CONDITION" },
4716 { config_parse_unit_slice
, "SLICE" },
4717 { config_parse_documentation
, "URL" },
4718 { config_parse_service_timeout
, "SECONDS" },
4719 { config_parse_emergency_action
, "ACTION" },
4720 { config_parse_set_status
, "STATUS" },
4721 { config_parse_service_sockets
, "SOCKETS" },
4722 { config_parse_environ
, "ENVIRON" },
4724 { config_parse_syscall_filter
, "SYSCALLS" },
4725 { config_parse_syscall_archs
, "ARCHS" },
4726 { config_parse_syscall_errno
, "ERRNO" },
4727 { config_parse_address_families
, "FAMILIES" },
4728 { config_parse_restrict_namespaces
, "NAMESPACES" },
4730 { config_parse_cpu_shares
, "SHARES" },
4731 { config_parse_cg_weight
, "WEIGHT" },
4732 { config_parse_memory_limit
, "LIMIT" },
4733 { config_parse_device_allow
, "DEVICE" },
4734 { config_parse_device_policy
, "POLICY" },
4735 { config_parse_io_limit
, "LIMIT" },
4736 { config_parse_io_device_weight
, "DEVICEWEIGHT" },
4737 { config_parse_io_device_latency
, "DEVICELATENCY" },
4738 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
4739 { config_parse_blockio_weight
, "WEIGHT" },
4740 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
4741 { config_parse_long
, "LONG" },
4742 { config_parse_socket_service
, "SERVICE" },
4744 { config_parse_exec_selinux_context
, "LABEL" },
4746 { config_parse_job_mode
, "MODE" },
4747 { config_parse_job_mode_isolate
, "BOOLEAN" },
4748 { config_parse_personality
, "PERSONALITY" },
4751 const char *prev
= NULL
;
4756 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
4757 const char *rvalue
= "OTHER", *lvalue
;
4758 const ConfigPerfItem
*p
;
4763 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
4765 /* Hide legacy settings */
4766 if (p
->parse
== config_parse_warn_compat
&&
4767 p
->ltype
== DISABLED_LEGACY
)
4770 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
4771 if (p
->parse
== table
[j
].callback
) {
4772 rvalue
= table
[j
].rvalue
;
4776 dot
= strchr(i
, '.');
4777 lvalue
= dot
? dot
+ 1 : i
;
4781 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
4785 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
4788 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);