1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
7 Copyright 2012 Holger Hans Peter Freyther
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <linux/oom.h>
32 #include <sys/resource.h>
35 #include "alloc-util.h"
37 #include "bus-error.h"
38 #include "bus-internal.h"
42 #include "conf-parser.h"
43 #include "cpu-set-util.h"
45 #include "errno-list.h"
50 #include "load-fragment.h"
53 #include "parse-util.h"
54 #include "path-util.h"
55 #include "process-util.h"
57 #include "seccomp-util.h"
59 #include "securebits.h"
60 #include "signal-util.h"
61 #include "stat-util.h"
62 #include "string-util.h"
64 #include "unit-name.h"
65 #include "unit-printf.h"
70 int config_parse_warn_compat(
75 unsigned section_line
,
81 Disabled reason
= ltype
;
84 case DISABLED_CONFIGURATION
:
85 log_syntax(unit
, LOG_DEBUG
, filename
, line
, 0,
86 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
89 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
90 "Support for option %s= has been removed and it is ignored", lvalue
);
92 case DISABLED_EXPERIMENTAL
:
93 log_syntax(unit
, LOG_INFO
, filename
, line
, 0,
94 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
101 int config_parse_unit_deps(const char *unit
,
102 const char *filename
,
105 unsigned section_line
,
112 UnitDependency d
= ltype
;
122 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
125 r
= extract_first_word(&p
, &word
, NULL
, 0);
131 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Invalid syntax, ignoring: %s", rvalue
);
135 r
= unit_name_printf(u
, word
, &k
);
137 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
141 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
143 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
149 int config_parse_unit_string_printf(
151 const char *filename
,
154 unsigned section_line
,
161 _cleanup_free_
char *k
= NULL
;
170 r
= unit_full_printf(u
, rvalue
, &k
);
172 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
176 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
179 int config_parse_unit_strv_printf(
181 const char *filename
,
184 unsigned section_line
,
192 _cleanup_free_
char *k
= NULL
;
200 r
= unit_full_printf(u
, rvalue
, &k
);
202 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
206 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
209 int config_parse_unit_path_printf(
211 const char *filename
,
214 unsigned section_line
,
221 _cleanup_free_
char *k
= NULL
;
230 r
= unit_full_printf(u
, rvalue
, &k
);
232 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
236 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
239 int config_parse_unit_path_strv_printf(
241 const char *filename
,
244 unsigned section_line
,
252 const char *word
, *state
;
262 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
263 _cleanup_free_
char *k
= NULL
;
269 r
= unit_full_printf(u
, t
, &k
);
271 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", t
);
275 if (!utf8_is_valid(k
)) {
276 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
280 if (!path_is_absolute(k
)) {
281 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Symlink path %s is not absolute, ignoring: %m", k
);
285 path_kill_slashes(k
);
294 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, ignoring.");
299 int config_parse_socket_listen(const char *unit
,
300 const char *filename
,
303 unsigned section_line
,
310 _cleanup_free_ SocketPort
*p
= NULL
;
322 if (isempty(rvalue
)) {
323 /* An empty assignment removes all ports */
324 socket_free_ports(s
);
328 p
= new0(SocketPort
, 1);
332 if (ltype
!= SOCKET_SOCKET
) {
335 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
337 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
341 path_kill_slashes(p
->path
);
343 } else if (streq(lvalue
, "ListenNetlink")) {
344 _cleanup_free_
char *k
= NULL
;
346 p
->type
= SOCKET_SOCKET
;
347 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
349 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
353 r
= socket_address_parse_netlink(&p
->address
, k
);
355 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
360 _cleanup_free_
char *k
= NULL
;
362 p
->type
= SOCKET_SOCKET
;
363 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
365 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
369 r
= socket_address_parse_and_warn(&p
->address
, k
);
371 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse address value, ignoring: %s", rvalue
);
375 if (streq(lvalue
, "ListenStream"))
376 p
->address
.type
= SOCK_STREAM
;
377 else if (streq(lvalue
, "ListenDatagram"))
378 p
->address
.type
= SOCK_DGRAM
;
380 assert(streq(lvalue
, "ListenSequentialPacket"));
381 p
->address
.type
= SOCK_SEQPACKET
;
384 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
385 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Address family not supported, ignoring: %s", rvalue
);
391 p
->auxiliary_fds
= NULL
;
392 p
->n_auxiliary_fds
= 0;
396 LIST_FIND_TAIL(port
, s
->ports
, tail
);
397 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
399 LIST_PREPEND(port
, s
->ports
, p
);
405 int config_parse_socket_bind(const char *unit
,
406 const char *filename
,
409 unsigned section_line
,
417 SocketAddressBindIPv6Only b
;
426 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
430 r
= parse_boolean(rvalue
);
432 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
436 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
438 s
->bind_ipv6_only
= b
;
443 int config_parse_exec_nice(const char *unit
,
444 const char *filename
,
447 unsigned section_line
,
454 ExecContext
*c
= data
;
462 r
= safe_atoi(rvalue
, &priority
);
464 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse nice priority, ignoring: %s", rvalue
);
468 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
469 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Nice priority out of range, ignoring: %s", rvalue
);
479 int config_parse_exec_oom_score_adjust(const char* unit
,
480 const char *filename
,
483 unsigned section_line
,
490 ExecContext
*c
= data
;
498 r
= safe_atoi(rvalue
, &oa
);
500 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
504 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
505 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "OOM score adjust value out of range, ignoring: %s", 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
;
538 rvalue
+= strspn(rvalue
, WHITESPACE
);
540 if (isempty(rvalue
)) {
541 /* An empty assignment resets the list */
542 *e
= exec_command_free_list(*e
);
548 _cleanup_free_
char *path
= NULL
, *firstword
= NULL
;
549 bool separate_argv0
= false, ignore
= false;
550 _cleanup_free_ ExecCommand
*nce
= NULL
;
551 _cleanup_strv_free_
char **n
= NULL
;
552 size_t nlen
= 0, nbufsize
= 0;
558 r
= extract_first_word_and_warn(&p
, &firstword
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
563 for (i
= 0; i
< 2; i
++) {
564 /* We accept an absolute path as first argument, or
565 * alternatively an absolute prefixed with @ to allow
566 * overriding of argv[0]. */
567 if (*f
== '-' && !ignore
)
569 else if (*f
== '@' && !separate_argv0
)
570 separate_argv0
= true;
577 /* First word is either "-" or "@" with no command. */
578 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty path in command line, ignoring: \"%s\"", rvalue
);
581 if (!string_is_safe(f
)) {
582 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path contains special characters, ignoring: %s", rvalue
);
585 if (!path_is_absolute(f
)) {
586 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path is not absolute, ignoring: %s", rvalue
);
589 if (endswith(f
, "/")) {
590 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Executable path specifies a directory, ignoring: %s", rvalue
);
594 if (f
== firstword
) {
603 if (!separate_argv0
) {
604 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
613 path_kill_slashes(path
);
615 while (!isempty(p
)) {
616 _cleanup_free_
char *word
= NULL
;
618 /* Check explicitly for an unquoted semicolon as
619 * command separator token. */
620 if (p
[0] == ';' && (!p
[1] || strchr(WHITESPACE
, p
[1]))) {
622 p
+= strspn(p
, WHITESPACE
);
627 /* Check for \; explicitly, to not confuse it with \\;
628 * or "\;" or "\\;" etc. extract_first_word would
629 * return the same for all of those. */
630 if (p
[0] == '\\' && p
[1] == ';' && (!p
[2] || strchr(WHITESPACE
, p
[2]))) {
632 p
+= strspn(p
, WHITESPACE
);
633 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
643 r
= extract_first_word_and_warn(&p
, &word
, WHITESPACE
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
, unit
, filename
, line
, rvalue
);
649 if (!GREEDY_REALLOC(n
, nbufsize
, nlen
+ 2))
657 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue
);
661 nce
= new0(ExecCommand
, 1);
667 nce
->ignore
= ignore
;
669 exec_command_append_list(e
, nce
);
671 /* Do not _cleanup_free_ these. */
682 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
683 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
685 int config_parse_socket_bindtodevice(const char* unit
,
686 const char *filename
,
689 unsigned section_line
,
704 if (rvalue
[0] && !streq(rvalue
, "*")) {
711 free(s
->bind_to_device
);
712 s
->bind_to_device
= n
;
717 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
718 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
720 int config_parse_exec_io_class(const char *unit
,
721 const char *filename
,
724 unsigned section_line
,
731 ExecContext
*c
= data
;
739 x
= ioprio_class_from_string(rvalue
);
741 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
745 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
746 c
->ioprio_set
= true;
751 int config_parse_exec_io_priority(const char *unit
,
752 const char *filename
,
755 unsigned section_line
,
762 ExecContext
*c
= data
;
770 r
= safe_atoi(rvalue
, &i
);
771 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
772 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse IO priority, ignoring: %s", rvalue
);
776 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
777 c
->ioprio_set
= true;
782 int config_parse_exec_cpu_sched_policy(const char *unit
,
783 const char *filename
,
786 unsigned section_line
,
794 ExecContext
*c
= data
;
802 x
= sched_policy_from_string(rvalue
);
804 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
808 c
->cpu_sched_policy
= x
;
809 /* Moving to or from real-time policy? We need to adjust the priority */
810 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
811 c
->cpu_sched_set
= true;
816 int config_parse_exec_cpu_sched_prio(const char *unit
,
817 const char *filename
,
820 unsigned section_line
,
827 ExecContext
*c
= data
;
835 r
= safe_atoi(rvalue
, &i
);
837 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
841 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
842 min
= sched_get_priority_min(c
->cpu_sched_policy
);
843 max
= sched_get_priority_max(c
->cpu_sched_policy
);
845 if (i
< min
|| i
> max
) {
846 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
850 c
->cpu_sched_priority
= i
;
851 c
->cpu_sched_set
= true;
856 int config_parse_exec_cpu_affinity(const char *unit
,
857 const char *filename
,
860 unsigned section_line
,
867 ExecContext
*c
= data
;
868 _cleanup_cpu_free_ cpu_set_t
*cpuset
= NULL
;
876 ncpus
= parse_cpu_set_and_warn(rvalue
, &cpuset
, unit
, filename
, line
, lvalue
);
884 /* An empty assignment resets the CPU list */
890 c
->cpuset_ncpus
= ncpus
;
895 int config_parse_exec_capabilities(const char *unit
,
896 const char *filename
,
899 unsigned section_line
,
906 ExecContext
*c
= data
;
914 cap
= cap_from_text(rvalue
);
916 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
, "Failed to parse capabilities, ignoring: %s", rvalue
);
921 cap_free(c
->capabilities
);
922 c
->capabilities
= cap
;
927 int config_parse_exec_secure_bits(const char *unit
,
928 const char *filename
,
931 unsigned section_line
,
938 ExecContext
*c
= data
;
940 const char *word
, *state
;
947 if (isempty(rvalue
)) {
948 /* An empty assignment resets the field */
953 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
954 if (first_word(word
, "keep-caps"))
955 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
956 else if (first_word(word
, "keep-caps-locked"))
957 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
958 else if (first_word(word
, "no-setuid-fixup"))
959 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
960 else if (first_word(word
, "no-setuid-fixup-locked"))
961 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
962 else if (first_word(word
, "noroot"))
963 c
->secure_bits
|= 1<<SECURE_NOROOT
;
964 else if (first_word(word
, "noroot-locked"))
965 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
967 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse secure bits, ignoring: %s", rvalue
);
972 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid syntax, garbage at the end, ignoring.");
977 int config_parse_bounding_set(
979 const char *filename
,
982 unsigned section_line
,
989 uint64_t *capability_bounding_set_drop
= data
;
990 uint64_t capability_bounding_set
, sum
= 0;
999 if (rvalue
[0] == '~') {
1004 /* Note that we store this inverted internally, since the
1005 * kernel wants it like this. But we actually expose it
1006 * non-inverted everywhere to have a fully normalized
1011 _cleanup_free_
char *word
= NULL
;
1014 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1020 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse word, ignoring: %s", rvalue
);
1024 cap
= capability_from_name(word
);
1026 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse capability in bounding set, ignoring: %s", word
);
1030 sum
|= ((uint64_t) UINT64_C(1)) << (uint64_t) cap
;
1033 capability_bounding_set
= invert
? ~sum
: sum
;
1034 if (*capability_bounding_set_drop
!= 0 && capability_bounding_set
!= 0)
1035 *capability_bounding_set_drop
= ~(~*capability_bounding_set_drop
| capability_bounding_set
);
1037 *capability_bounding_set_drop
= ~capability_bounding_set
;
1042 int config_parse_limit(
1044 const char *filename
,
1046 const char *section
,
1047 unsigned section_line
,
1054 struct rlimit
**rl
= data
;
1065 if (streq(rvalue
, "infinity"))
1070 /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
1071 assert_cc(sizeof(rlim_t
) == sizeof(uint64_t));
1073 r
= safe_atou64(rvalue
, &u
);
1074 if (r
>= 0 && u
>= (uint64_t) RLIM_INFINITY
)
1077 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1085 *rl
= new(struct rlimit
, 1);
1090 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= v
;
1094 int config_parse_bytes_limit(
1096 const char *filename
,
1098 const char *section
,
1099 unsigned section_line
,
1106 struct rlimit
**rl
= data
;
1117 if (streq(rvalue
, "infinity"))
1118 bytes
= RLIM_INFINITY
;
1122 r
= parse_size(rvalue
, 1024, &u
);
1123 if (r
>= 0 && u
>= (uint64_t) RLIM_INFINITY
)
1126 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1134 *rl
= new(struct rlimit
, 1);
1139 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= bytes
;
1143 int config_parse_sec_limit(
1145 const char *filename
,
1147 const char *section
,
1148 unsigned section_line
,
1155 struct rlimit
**rl
= data
;
1166 if (streq(rvalue
, "infinity"))
1167 seconds
= RLIM_INFINITY
;
1171 r
= parse_sec(rvalue
, &t
);
1173 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1177 if (t
== USEC_INFINITY
)
1178 seconds
= RLIM_INFINITY
;
1180 seconds
= (rlim_t
) (DIV_ROUND_UP(t
, USEC_PER_SEC
));
1184 *rl
= new(struct rlimit
, 1);
1189 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= seconds
;
1194 int config_parse_usec_limit(
1196 const char *filename
,
1198 const char *section
,
1199 unsigned section_line
,
1206 struct rlimit
**rl
= data
;
1217 if (streq(rvalue
, "infinity"))
1218 useconds
= RLIM_INFINITY
;
1222 r
= parse_time(rvalue
, &t
, 1);
1224 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse resource value, ignoring: %s", rvalue
);
1228 if (t
== USEC_INFINITY
)
1229 useconds
= RLIM_INFINITY
;
1231 useconds
= (rlim_t
) t
;
1235 *rl
= new(struct rlimit
, 1);
1240 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= useconds
;
1244 #ifdef HAVE_SYSV_COMPAT
1245 int config_parse_sysv_priority(const char *unit
,
1246 const char *filename
,
1248 const char *section
,
1249 unsigned section_line
,
1256 int *priority
= data
;
1264 r
= safe_atoi(rvalue
, &i
);
1265 if (r
< 0 || i
< 0) {
1266 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1270 *priority
= (int) i
;
1275 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode
, exec_utmp_mode
, ExecUtmpMode
, "Failed to parse utmp mode");
1276 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1278 int config_parse_exec_mount_flags(const char *unit
,
1279 const char *filename
,
1281 const char *section
,
1282 unsigned section_line
,
1289 ExecContext
*c
= data
;
1290 const char *word
, *state
;
1292 unsigned long flags
= 0;
1299 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1300 _cleanup_free_
char *t
;
1302 t
= strndup(word
, l
);
1306 if (streq(t
, "shared"))
1308 else if (streq(t
, "slave"))
1310 else if (streq(t
, "private"))
1313 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1317 if (!isempty(state
))
1318 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
1320 c
->mount_flags
= flags
;
1324 int config_parse_exec_selinux_context(
1326 const char *filename
,
1328 const char *section
,
1329 unsigned section_line
,
1336 ExecContext
*c
= data
;
1347 if (isempty(rvalue
)) {
1348 c
->selinux_context
= mfree(c
->selinux_context
);
1349 c
->selinux_context_ignore
= false;
1353 if (rvalue
[0] == '-') {
1359 r
= unit_name_printf(u
, rvalue
, &k
);
1361 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1365 free(c
->selinux_context
);
1366 c
->selinux_context
= k
;
1367 c
->selinux_context_ignore
= ignore
;
1372 int config_parse_exec_apparmor_profile(
1374 const char *filename
,
1376 const char *section
,
1377 unsigned section_line
,
1384 ExecContext
*c
= data
;
1395 if (isempty(rvalue
)) {
1396 c
->apparmor_profile
= mfree(c
->apparmor_profile
);
1397 c
->apparmor_profile_ignore
= false;
1401 if (rvalue
[0] == '-') {
1407 r
= unit_name_printf(u
, rvalue
, &k
);
1409 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1413 free(c
->apparmor_profile
);
1414 c
->apparmor_profile
= k
;
1415 c
->apparmor_profile_ignore
= ignore
;
1420 int config_parse_exec_smack_process_label(
1422 const char *filename
,
1424 const char *section
,
1425 unsigned section_line
,
1432 ExecContext
*c
= data
;
1443 if (isempty(rvalue
)) {
1444 c
->smack_process_label
= mfree(c
->smack_process_label
);
1445 c
->smack_process_label_ignore
= false;
1449 if (rvalue
[0] == '-') {
1455 r
= unit_name_printf(u
, rvalue
, &k
);
1457 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1461 free(c
->smack_process_label
);
1462 c
->smack_process_label
= k
;
1463 c
->smack_process_label_ignore
= ignore
;
1468 int config_parse_timer(const char *unit
,
1469 const char *filename
,
1471 const char *section
,
1472 unsigned section_line
,
1483 CalendarSpec
*c
= NULL
;
1490 if (isempty(rvalue
)) {
1491 /* Empty assignment resets list */
1492 timer_free_values(t
);
1496 b
= timer_base_from_string(lvalue
);
1498 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer base, ignoring: %s", lvalue
);
1502 if (b
== TIMER_CALENDAR
) {
1503 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1504 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse calendar specification, ignoring: %s", rvalue
);
1508 if (parse_sec(rvalue
, &u
) < 0) {
1509 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse timer value, ignoring: %s", rvalue
);
1514 v
= new0(TimerValue
, 1);
1516 calendar_spec_free(c
);
1522 v
->calendar_spec
= c
;
1524 LIST_PREPEND(value
, t
->values
, v
);
1529 int config_parse_trigger_unit(
1531 const char *filename
,
1533 const char *section
,
1534 unsigned section_line
,
1541 _cleanup_free_
char *p
= NULL
;
1551 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1552 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Multiple units to trigger specified, ignoring: %s", rvalue
);
1556 r
= unit_name_printf(u
, rvalue
, &p
);
1558 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1562 type
= unit_name_to_type(p
);
1564 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit type not valid, ignoring: %s", rvalue
);
1568 if (type
== u
->type
) {
1569 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trigger cannot be of same type, ignoring: %s", rvalue
);
1573 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
, NULL
, true);
1575 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add trigger on %s, ignoring: %m", p
);
1582 int config_parse_path_spec(const char *unit
,
1583 const char *filename
,
1585 const char *section
,
1586 unsigned section_line
,
1596 _cleanup_free_
char *k
= NULL
;
1604 if (isempty(rvalue
)) {
1605 /* Empty assignment clears list */
1610 b
= path_type_from_string(lvalue
);
1612 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse path type, ignoring: %s", lvalue
);
1616 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1618 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
1622 if (!path_is_absolute(k
)) {
1623 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path is not absolute, ignoring: %s", k
);
1627 s
= new0(PathSpec
, 1);
1632 s
->path
= path_kill_slashes(k
);
1637 LIST_PREPEND(spec
, p
->specs
, s
);
1642 int config_parse_socket_service(
1644 const char *filename
,
1646 const char *section
,
1647 unsigned section_line
,
1654 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1655 _cleanup_free_
char *p
= NULL
;
1665 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1667 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1671 if (!endswith(p
, ".service")) {
1672 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1676 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1678 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1682 unit_ref_set(&s
->service
, x
);
1687 int config_parse_fdname(
1689 const char *filename
,
1691 const char *section
,
1692 unsigned section_line
,
1699 _cleanup_free_
char *p
= NULL
;
1708 if (isempty(rvalue
)) {
1709 s
->fdname
= mfree(s
->fdname
);
1713 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1715 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1719 if (!fdname_is_valid(p
)) {
1720 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid file descriptor name, ignoring: %s", p
);
1731 int config_parse_service_sockets(
1733 const char *filename
,
1735 const char *section
,
1736 unsigned section_line
,
1754 _cleanup_free_
char *word
= NULL
, *k
= NULL
;
1756 r
= extract_first_word(&p
, &word
, NULL
, 0);
1762 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage in sockets, ignoring: %s", rvalue
);
1766 r
= unit_name_printf(UNIT(s
), word
, &k
);
1768 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
1772 if (!endswith(k
, ".socket")) {
1773 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type socket, ignoring: %s", k
);
1777 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
, NULL
, true);
1779 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1781 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
, NULL
, true);
1783 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add dependency on %s, ignoring: %m", k
);
1789 int config_parse_bus_name(
1791 const char *filename
,
1793 const char *section
,
1794 unsigned section_line
,
1801 _cleanup_free_
char *k
= NULL
;
1810 r
= unit_full_printf(u
, rvalue
, &k
);
1812 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue
);
1816 if (!service_name_is_valid(k
)) {
1817 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid bus name %s, ignoring.", k
);
1821 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
1824 int config_parse_service_timeout(const char *unit
,
1825 const char *filename
,
1827 const char *section
,
1828 unsigned section_line
,
1835 Service
*s
= userdata
;
1843 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1844 rvalue
, data
, userdata
);
1848 if (streq(lvalue
, "TimeoutSec")) {
1849 s
->start_timeout_defined
= true;
1850 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1851 } else if (streq(lvalue
, "TimeoutStartSec"))
1852 s
->start_timeout_defined
= true;
1857 int config_parse_busname_service(
1859 const char *filename
,
1861 const char *section
,
1862 unsigned section_line
,
1869 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1873 _cleanup_free_
char *p
= NULL
;
1880 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1882 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
1886 if (!endswith(p
, ".service")) {
1887 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unit must be of type service, ignoring: %s", rvalue
);
1891 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1893 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1897 unit_ref_set(&n
->service
, x
);
1902 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1904 int config_parse_bus_policy(
1906 const char *filename
,
1908 const char *section
,
1909 unsigned section_line
,
1916 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1917 _cleanup_free_
char *id_str
= NULL
;
1918 BusName
*busname
= data
;
1926 p
= new0(BusNamePolicy
, 1);
1930 if (streq(lvalue
, "AllowUser"))
1931 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1932 else if (streq(lvalue
, "AllowGroup"))
1933 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1935 assert_not_reached("Unknown lvalue");
1937 id_str
= strdup(rvalue
);
1941 access_str
= strpbrk(id_str
, WHITESPACE
);
1943 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy value '%s'", rvalue
);
1949 access_str
+= strspn(access_str
, WHITESPACE
);
1951 p
->access
= bus_policy_access_from_string(access_str
);
1952 if (p
->access
< 0) {
1953 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid busname policy access type '%s'", access_str
);
1960 LIST_PREPEND(policy
, busname
->policy
, p
);
1966 int config_parse_bus_endpoint_policy(
1968 const char *filename
,
1970 const char *section
,
1971 unsigned section_line
,
1978 _cleanup_free_
char *name
= NULL
;
1979 BusPolicyAccess access
;
1980 ExecContext
*c
= data
;
1989 name
= strdup(rvalue
);
1993 access_str
= strpbrk(name
, WHITESPACE
);
1995 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy value '%s'", rvalue
);
2001 access_str
+= strspn(access_str
, WHITESPACE
);
2003 access
= bus_policy_access_from_string(access_str
);
2004 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
2005 access
>= _BUS_POLICY_ACCESS_MAX
) {
2006 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid endpoint policy access type '%s'", access_str
);
2010 if (!c
->bus_endpoint
) {
2011 r
= bus_endpoint_new(&c
->bus_endpoint
);
2013 return log_error_errno(r
, "Failed to create bus endpoint object: %m");
2016 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
2019 int config_parse_working_directory(
2021 const char *filename
,
2023 const char *section
,
2024 unsigned section_line
,
2031 ExecContext
*c
= data
;
2042 if (rvalue
[0] == '-') {
2048 if (streq(rvalue
, "~")) {
2049 c
->working_directory_home
= true;
2050 c
->working_directory
= mfree(c
->working_directory
);
2052 _cleanup_free_
char *k
= NULL
;
2054 r
= unit_full_printf(u
, rvalue
, &k
);
2056 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue
);
2060 path_kill_slashes(k
);
2062 if (!utf8_is_valid(k
)) {
2063 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2067 if (!path_is_absolute(k
)) {
2068 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue
);
2072 free(c
->working_directory
);
2073 c
->working_directory
= k
;
2076 c
->working_directory_home
= false;
2079 c
->working_directory_missing_ok
= missing_ok
;
2083 int config_parse_unit_env_file(const char *unit
,
2084 const char *filename
,
2086 const char *section
,
2087 unsigned section_line
,
2096 _cleanup_free_
char *n
= NULL
;
2104 if (isempty(rvalue
)) {
2105 /* Empty assignment frees the list */
2106 *env
= strv_free(*env
);
2110 r
= unit_full_printf(u
, rvalue
, &n
);
2112 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2116 if (!path_is_absolute(n
[0] == '-' ? n
+ 1 : n
)) {
2117 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path '%s' is not absolute, ignoring.", n
);
2121 r
= strv_extend(env
, n
);
2128 int config_parse_environ(const char *unit
,
2129 const char *filename
,
2131 const char *section
,
2132 unsigned section_line
,
2141 const char *word
, *state
;
2143 _cleanup_free_
char *k
= NULL
;
2151 if (isempty(rvalue
)) {
2152 /* Empty assignment resets the list */
2153 *env
= strv_free(*env
);
2158 r
= unit_full_printf(u
, rvalue
, &k
);
2160 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2171 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
2172 _cleanup_free_
char *n
= NULL
;
2175 r
= cunescape_length(word
, l
, 0, &n
);
2177 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Couldn't unescape assignment, ignoring: %s", rvalue
);
2181 if (!env_assignment_is_valid(n
)) {
2182 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid environment assignment, ignoring: %s", rvalue
);
2186 x
= strv_env_set(*env
, n
);
2193 if (!isempty(state
))
2194 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2199 int config_parse_ip_tos(const char *unit
,
2200 const char *filename
,
2202 const char *section
,
2203 unsigned section_line
,
2210 int *ip_tos
= data
, x
;
2217 x
= ip_tos_from_string(rvalue
);
2219 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2227 int config_parse_unit_condition_path(
2229 const char *filename
,
2231 const char *section
,
2232 unsigned section_line
,
2239 _cleanup_free_
char *p
= NULL
;
2240 Condition
**list
= data
, *c
;
2241 ConditionType t
= ltype
;
2242 bool trigger
, negate
;
2251 if (isempty(rvalue
)) {
2252 /* Empty assignment resets the list */
2253 *list
= condition_free_list(*list
);
2257 trigger
= rvalue
[0] == '|';
2261 negate
= rvalue
[0] == '!';
2265 r
= unit_full_printf(u
, rvalue
, &p
);
2267 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2271 if (!path_is_absolute(p
)) {
2272 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Path in condition not absolute, ignoring: %s", p
);
2276 c
= condition_new(t
, p
, trigger
, negate
);
2280 LIST_PREPEND(conditions
, *list
, c
);
2284 int config_parse_unit_condition_string(
2286 const char *filename
,
2288 const char *section
,
2289 unsigned section_line
,
2296 _cleanup_free_
char *s
= NULL
;
2297 Condition
**list
= data
, *c
;
2298 ConditionType t
= ltype
;
2299 bool trigger
, negate
;
2308 if (isempty(rvalue
)) {
2309 /* Empty assignment resets the list */
2310 *list
= condition_free_list(*list
);
2314 trigger
= rvalue
[0] == '|';
2318 negate
= rvalue
[0] == '!';
2322 r
= unit_full_printf(u
, rvalue
, &s
);
2324 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2328 c
= condition_new(t
, s
, trigger
, negate
);
2332 LIST_PREPEND(conditions
, *list
, c
);
2336 int config_parse_unit_condition_null(
2338 const char *filename
,
2340 const char *section
,
2341 unsigned section_line
,
2348 Condition
**list
= data
, *c
;
2349 bool trigger
, negate
;
2357 if (isempty(rvalue
)) {
2358 /* Empty assignment resets the list */
2359 *list
= condition_free_list(*list
);
2363 trigger
= rvalue
[0] == '|';
2367 negate
= rvalue
[0] == '!';
2371 b
= parse_boolean(rvalue
);
2373 log_syntax(unit
, LOG_ERR
, filename
, line
, b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2380 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2384 LIST_PREPEND(conditions
, *list
, c
);
2388 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2389 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2391 int config_parse_unit_requires_mounts_for(
2393 const char *filename
,
2395 const char *section
,
2396 unsigned section_line
,
2404 const char *word
, *state
;
2412 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2414 _cleanup_free_
char *n
;
2416 n
= strndup(word
, l
);
2420 if (!utf8_is_valid(n
)) {
2421 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, rvalue
);
2425 r
= unit_require_mounts_for(u
, n
);
2427 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to add required mount for, ignoring: %s", rvalue
);
2431 if (!isempty(state
))
2432 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2437 int config_parse_documentation(const char *unit
,
2438 const char *filename
,
2440 const char *section
,
2441 unsigned section_line
,
2457 if (isempty(rvalue
)) {
2458 /* Empty assignment resets the list */
2459 u
->documentation
= strv_free(u
->documentation
);
2463 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2464 rvalue
, data
, userdata
);
2468 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2470 if (documentation_url_is_valid(*a
))
2473 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid URL, ignoring: %s", *a
);
2484 int config_parse_syscall_filter(
2486 const char *filename
,
2488 const char *section
,
2489 unsigned section_line
,
2496 static const char default_syscalls
[] =
2503 ExecContext
*c
= data
;
2505 bool invert
= false;
2506 const char *word
, *state
;
2515 if (isempty(rvalue
)) {
2516 /* Empty assignment resets the list */
2517 c
->syscall_filter
= set_free(c
->syscall_filter
);
2518 c
->syscall_whitelist
= false;
2522 if (rvalue
[0] == '~') {
2527 if (!c
->syscall_filter
) {
2528 c
->syscall_filter
= set_new(NULL
);
2529 if (!c
->syscall_filter
)
2533 /* Allow everything but the ones listed */
2534 c
->syscall_whitelist
= false;
2538 /* Allow nothing but the ones listed */
2539 c
->syscall_whitelist
= true;
2541 /* Accept default syscalls if we are on a whitelist */
2542 NULSTR_FOREACH(i
, default_syscalls
) {
2545 id
= seccomp_syscall_resolve_name(i
);
2549 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2558 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2559 _cleanup_free_
char *t
= NULL
;
2562 t
= strndup(word
, l
);
2566 id
= seccomp_syscall_resolve_name(t
);
2568 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call, ignoring: %s", t
);
2572 /* If we previously wanted to forbid a syscall and now
2573 * we want to allow it, then remove it from the list
2575 if (!invert
== c
->syscall_whitelist
) {
2576 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2582 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2584 if (!isempty(state
))
2585 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2587 /* Turn on NNP, but only if it wasn't configured explicitly
2588 * before, and only if we are in user mode. */
2589 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== MANAGER_USER
)
2590 c
->no_new_privileges
= true;
2595 int config_parse_syscall_archs(
2597 const char *filename
,
2599 const char *section
,
2600 unsigned section_line
,
2608 const char *word
, *state
;
2612 if (isempty(rvalue
)) {
2613 *archs
= set_free(*archs
);
2617 r
= set_ensure_allocated(archs
, NULL
);
2621 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2622 _cleanup_free_
char *t
= NULL
;
2625 t
= strndup(word
, l
);
2629 r
= seccomp_arch_from_string(t
, &a
);
2631 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse system call architecture, ignoring: %s", t
);
2635 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2641 if (!isempty(state
))
2642 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2647 int config_parse_syscall_errno(
2649 const char *filename
,
2651 const char *section
,
2652 unsigned section_line
,
2659 ExecContext
*c
= data
;
2666 if (isempty(rvalue
)) {
2667 /* Empty assignment resets to KILL */
2668 c
->syscall_errno
= 0;
2672 e
= errno_from_name(rvalue
);
2674 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse error number, ignoring: %s", rvalue
);
2678 c
->syscall_errno
= e
;
2682 int config_parse_address_families(
2684 const char *filename
,
2686 const char *section
,
2687 unsigned section_line
,
2694 ExecContext
*c
= data
;
2695 bool invert
= false;
2696 const char *word
, *state
;
2704 if (isempty(rvalue
)) {
2705 /* Empty assignment resets the list */
2706 c
->address_families
= set_free(c
->address_families
);
2707 c
->address_families_whitelist
= false;
2711 if (rvalue
[0] == '~') {
2716 if (!c
->address_families
) {
2717 c
->address_families
= set_new(NULL
);
2718 if (!c
->address_families
)
2721 c
->address_families_whitelist
= !invert
;
2724 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2725 _cleanup_free_
char *t
= NULL
;
2728 t
= strndup(word
, l
);
2732 af
= af_from_name(t
);
2734 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse address family, ignoring: %s", t
);
2738 /* If we previously wanted to forbid an address family and now
2739 * we want to allow it, then remove it from the list
2741 if (!invert
== c
->address_families_whitelist
) {
2742 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2748 set_remove(c
->address_families
, INT_TO_PTR(af
));
2750 if (!isempty(state
))
2751 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
2757 int config_parse_unit_slice(
2759 const char *filename
,
2761 const char *section
,
2762 unsigned section_line
,
2769 _cleanup_free_
char *k
= NULL
;
2770 Unit
*u
= userdata
, *slice
= NULL
;
2778 r
= unit_name_printf(u
, rvalue
, &k
);
2780 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2784 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2786 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to load slice unit %s. Ignoring.", k
);
2790 r
= unit_set_slice(u
, slice
);
2792 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to assign slice %s to unit %s. Ignoring.", slice
->id
, u
->id
);
2799 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2801 int config_parse_cpu_shares(
2803 const char *filename
,
2805 const char *section
,
2806 unsigned section_line
,
2813 uint64_t *shares
= data
;
2820 r
= cg_cpu_shares_parse(rvalue
, shares
);
2822 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "CPU shares '%s' invalid. Ignoring.", rvalue
);
2829 int config_parse_cpu_quota(
2831 const char *filename
,
2833 const char *section
,
2834 unsigned section_line
,
2841 CGroupContext
*c
= data
;
2848 if (isempty(rvalue
)) {
2849 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2853 if (!endswith(rvalue
, "%")) {
2854 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2858 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2859 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "CPU quota '%s' invalid. Ignoring.", rvalue
);
2863 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2868 int config_parse_memory_limit(
2870 const char *filename
,
2872 const char *section
,
2873 unsigned section_line
,
2880 CGroupContext
*c
= data
;
2884 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2885 c
->memory_limit
= (uint64_t) -1;
2889 r
= parse_size(rvalue
, 1024, &bytes
);
2890 if (r
< 0 || bytes
< 1) {
2891 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Memory limit '%s' invalid. Ignoring.", rvalue
);
2895 c
->memory_limit
= bytes
;
2899 int config_parse_tasks_max(
2901 const char *filename
,
2903 const char *section
,
2904 unsigned section_line
,
2911 CGroupContext
*c
= data
;
2915 if (isempty(rvalue
) || streq(rvalue
, "infinity")) {
2916 c
->tasks_max
= (uint64_t) -1;
2920 r
= safe_atou64(rvalue
, &u
);
2921 if (r
< 0 || u
< 1) {
2922 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Maximum tasks value '%s' invalid. Ignoring.", rvalue
);
2930 int config_parse_device_allow(
2932 const char *filename
,
2934 const char *section
,
2935 unsigned section_line
,
2942 _cleanup_free_
char *path
= NULL
;
2943 CGroupContext
*c
= data
;
2944 CGroupDeviceAllow
*a
;
2948 if (isempty(rvalue
)) {
2949 while (c
->device_allow
)
2950 cgroup_context_free_device_allow(c
, c
->device_allow
);
2955 n
= strcspn(rvalue
, WHITESPACE
);
2956 path
= strndup(rvalue
, n
);
2960 if (!startswith(path
, "/dev/") &&
2961 !startswith(path
, "block-") &&
2962 !startswith(path
, "char-")) {
2963 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
2967 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2971 if (!in_charset(m
, "rwm")) {
2972 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device rights '%s'. Ignoring.", m
);
2976 a
= new0(CGroupDeviceAllow
, 1);
2982 a
->r
= !!strchr(m
, 'r');
2983 a
->w
= !!strchr(m
, 'w');
2984 a
->m
= !!strchr(m
, 'm');
2986 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2990 int config_parse_blockio_weight(
2992 const char *filename
,
2994 const char *section
,
2995 unsigned section_line
,
3002 uint64_t *weight
= data
;
3009 r
= cg_blkio_weight_parse(rvalue
, weight
);
3011 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", rvalue
);
3018 int config_parse_blockio_device_weight(
3020 const char *filename
,
3022 const char *section
,
3023 unsigned section_line
,
3030 _cleanup_free_
char *path
= NULL
;
3031 CGroupBlockIODeviceWeight
*w
;
3032 CGroupContext
*c
= data
;
3042 if (isempty(rvalue
)) {
3043 while (c
->blockio_device_weights
)
3044 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
3049 n
= strcspn(rvalue
, WHITESPACE
);
3050 weight
= rvalue
+ n
;
3051 weight
+= strspn(weight
, WHITESPACE
);
3053 if (isempty(weight
)) {
3054 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected block device and device weight. Ignoring.");
3058 path
= strndup(rvalue
, n
);
3062 if (!path_startswith(path
, "/dev")) {
3063 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3067 r
= cg_blkio_weight_parse(weight
, &u
);
3069 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO weight '%s' invalid. Ignoring.", weight
);
3073 assert(u
!= CGROUP_BLKIO_WEIGHT_INVALID
);
3075 w
= new0(CGroupBlockIODeviceWeight
, 1);
3084 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
3088 int config_parse_blockio_bandwidth(
3090 const char *filename
,
3092 const char *section
,
3093 unsigned section_line
,
3100 _cleanup_free_
char *path
= NULL
;
3101 CGroupBlockIODeviceBandwidth
*b
;
3102 CGroupContext
*c
= data
;
3103 const char *bandwidth
;
3113 read
= streq("BlockIOReadBandwidth", lvalue
);
3115 if (isempty(rvalue
)) {
3116 CGroupBlockIODeviceBandwidth
*next
;
3118 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
3119 if (b
->read
== read
)
3120 cgroup_context_free_blockio_device_bandwidth(c
, b
);
3125 n
= strcspn(rvalue
, WHITESPACE
);
3126 bandwidth
= rvalue
+ n
;
3127 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
3130 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3134 path
= strndup(rvalue
, n
);
3138 if (!path_startswith(path
, "/dev")) {
3139 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid device node path '%s'. Ignoring.", path
);
3143 r
= parse_size(bandwidth
, 1000, &bytes
);
3144 if (r
< 0 || bytes
<= 0) {
3145 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
3149 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
3155 b
->bandwidth
= bytes
;
3158 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
3163 int config_parse_netclass(
3165 const char *filename
,
3167 const char *section
,
3168 unsigned section_line
,
3175 CGroupContext
*c
= data
;
3183 if (streq(rvalue
, "auto")) {
3184 c
->netclass_type
= CGROUP_NETCLASS_TYPE_AUTO
;
3188 r
= safe_atou32(rvalue
, &v
);
3190 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Netclass '%s' invalid. Ignoring.", rvalue
);
3194 if (v
> CGROUP_NETCLASS_FIXED_MAX
)
3195 log_syntax(unit
, LOG_ERR
, filename
, line
, 0,
3196 "Fixed netclass %" PRIu32
" out of allowed range (0-%d). Applying anyway.", v
, (uint32_t) CGROUP_NETCLASS_FIXED_MAX
);
3199 c
->netclass_type
= CGROUP_NETCLASS_TYPE_FIXED
;
3204 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
3206 int config_parse_job_mode_isolate(
3208 const char *filename
,
3210 const char *section
,
3211 unsigned section_line
,
3225 r
= parse_boolean(rvalue
);
3227 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse boolean, ignoring: %s", rvalue
);
3231 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3235 int config_parse_runtime_directory(
3237 const char *filename
,
3239 const char *section
,
3240 unsigned section_line
,
3249 const char *word
, *state
;
3258 if (isempty(rvalue
)) {
3259 /* Empty assignment resets the list */
3260 *rt
= strv_free(*rt
);
3264 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3265 _cleanup_free_
char *t
= NULL
, *n
= NULL
;
3267 t
= strndup(word
, l
);
3271 r
= unit_name_printf(u
, t
, &n
);
3273 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to resolve specifiers, ignoring: %m");
3277 if (!filename_is_valid(n
)) {
3278 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3282 r
= strv_push(rt
, n
);
3288 if (!isempty(state
))
3289 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3294 int config_parse_set_status(
3296 const char *filename
,
3298 const char *section
,
3299 unsigned section_line
,
3307 const char *word
, *state
;
3309 ExitStatusSet
*status_set
= data
;
3316 /* Empty assignment resets the list */
3317 if (isempty(rvalue
)) {
3318 exit_status_set_free(status_set
);
3322 FOREACH_WORD(word
, l
, rvalue
, state
) {
3323 _cleanup_free_
char *temp
;
3327 temp
= strndup(word
, l
);
3331 r
= safe_atoi(temp
, &val
);
3333 val
= signal_from_string_try_harder(temp
);
3336 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse value, ignoring: %s", word
);
3339 set
= &status_set
->signal
;
3341 if (val
< 0 || val
> 255) {
3342 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Value %d is outside range 0-255, ignoring", val
);
3345 set
= &status_set
->status
;
3348 r
= set_ensure_allocated(set
, NULL
);
3352 r
= set_put(*set
, INT_TO_PTR(val
));
3354 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Unable to store: %s", word
);
3358 if (!isempty(state
))
3359 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Trailing garbage, ignoring.");
3364 int config_parse_namespace_path_strv(
3366 const char *filename
,
3368 const char *section
,
3369 unsigned section_line
,
3386 if (isempty(rvalue
)) {
3387 /* Empty assignment resets the list */
3388 *sv
= strv_free(*sv
);
3392 prev
= cur
= rvalue
;
3394 _cleanup_free_
char *word
= NULL
;
3397 r
= extract_first_word(&cur
, &word
, NULL
, EXTRACT_QUOTES
);
3403 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Trailing garbage, ignoring: %s", prev
);
3407 if (!utf8_is_valid(word
)) {
3408 log_syntax_invalid_utf8(unit
, LOG_ERR
, filename
, line
, word
);
3413 offset
= word
[0] == '-';
3414 if (!path_is_absolute(word
+ offset
)) {
3415 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Not an absolute path, ignoring: %s", word
);
3420 path_kill_slashes(word
+ offset
);
3422 r
= strv_push(sv
, word
);
3433 int config_parse_no_new_privileges(
3435 const char *filename
,
3437 const char *section
,
3438 unsigned section_line
,
3445 ExecContext
*c
= data
;
3453 k
= parse_boolean(rvalue
);
3455 log_syntax(unit
, LOG_ERR
, filename
, line
, k
, "Failed to parse boolean value, ignoring: %s", rvalue
);
3459 c
->no_new_privileges
= !!k
;
3460 c
->no_new_privileges_set
= true;
3465 int config_parse_protect_home(
3467 const char *filename
,
3469 const char *section
,
3470 unsigned section_line
,
3477 ExecContext
*c
= data
;
3485 /* Our enum shall be a superset of booleans, hence first try
3486 * to parse as as boolean, and then as enum */
3488 k
= parse_boolean(rvalue
);
3490 c
->protect_home
= PROTECT_HOME_YES
;
3492 c
->protect_home
= PROTECT_HOME_NO
;
3496 h
= protect_home_from_string(rvalue
);
3498 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect home value, ignoring: %s", rvalue
);
3502 c
->protect_home
= h
;
3508 int config_parse_protect_system(
3510 const char *filename
,
3512 const char *section
,
3513 unsigned section_line
,
3520 ExecContext
*c
= data
;
3528 /* Our enum shall be a superset of booleans, hence first try
3529 * to parse as as boolean, and then as enum */
3531 k
= parse_boolean(rvalue
);
3533 c
->protect_system
= PROTECT_SYSTEM_YES
;
3535 c
->protect_system
= PROTECT_SYSTEM_NO
;
3539 s
= protect_system_from_string(rvalue
);
3541 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse protect system value, ignoring: %s", rvalue
);
3545 c
->protect_system
= s
;
3551 #define FOLLOW_MAX 8
3553 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3564 /* This will update the filename pointer if the loaded file is
3565 * reached by a symlink. The old string will be freed. */
3568 char *target
, *name
;
3570 if (c
++ >= FOLLOW_MAX
)
3573 path_kill_slashes(*filename
);
3575 /* Add the file name we are currently looking at to
3576 * the names of this unit, but only if it is a valid
3578 name
= basename(*filename
);
3580 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
3582 id
= set_get(names
, name
);
3588 r
= set_consume(names
, id
);
3594 /* Try to open the file name, but don't if its a symlink */
3595 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3602 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3603 r
= readlink_and_make_absolute(*filename
, &target
);
3611 f
= fdopen(fd
, "re");
3622 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3630 /* Let's try to add in all symlink names we found */
3631 while ((k
= set_steal_first(names
))) {
3633 /* First try to merge in the other name into our
3635 r
= unit_merge_by_name(*u
, k
);
3639 /* Hmm, we couldn't merge the other unit into
3640 * ours? Then let's try it the other way
3643 other
= manager_get_unit((*u
)->manager
, k
);
3647 r
= unit_merge(other
, *u
);
3650 return merge_by_names(u
, names
, NULL
);
3658 unit_choose_id(*u
, id
);
3666 static int load_from_path(Unit
*u
, const char *path
) {
3668 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3669 _cleanup_fclose_
FILE *f
= NULL
;
3670 _cleanup_free_
char *filename
= NULL
;
3678 symlink_names
= set_new(&string_hash_ops
);
3682 if (path_is_absolute(path
)) {
3684 filename
= strdup(path
);
3688 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3690 filename
= mfree(filename
);
3698 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3700 /* Instead of opening the path right away, we manually
3701 * follow all symlinks and add their name to our unit
3702 * name set while doing so */
3703 filename
= path_make_absolute(path
, *p
);
3707 if (u
->manager
->unit_path_cache
&&
3708 !set_get(u
->manager
->unit_path_cache
, filename
))
3711 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3714 filename
= mfree(filename
);
3718 /* Empty the symlink names for the next run */
3719 set_clear_free(symlink_names
);
3728 /* Hmm, no suitable file found? */
3732 r
= merge_by_names(&merged
, symlink_names
, id
);
3737 u
->load_state
= UNIT_MERGED
;
3741 if (fstat(fileno(f
), &st
) < 0)
3744 if (null_or_empty(&st
))
3745 u
->load_state
= UNIT_MASKED
;
3747 u
->load_state
= UNIT_LOADED
;
3749 /* Now, parse the file contents */
3750 r
= config_parse(u
->id
, filename
, f
,
3751 UNIT_VTABLE(u
)->sections
,
3752 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3753 false, true, false, u
);
3758 free(u
->fragment_path
);
3759 u
->fragment_path
= filename
;
3762 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3764 if (u
->source_path
) {
3765 if (stat(u
->source_path
, &st
) >= 0)
3766 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3768 u
->source_mtime
= 0;
3774 int unit_load_fragment(Unit
*u
) {
3780 assert(u
->load_state
== UNIT_STUB
);
3784 u
->load_state
= UNIT_LOADED
;
3788 /* First, try to find the unit under its id. We always look
3789 * for unit files in the default directories, to make it easy
3790 * to override things by placing things in /etc/systemd/system */
3791 r
= load_from_path(u
, u
->id
);
3795 /* Try to find an alias we can load this with */
3796 if (u
->load_state
== UNIT_STUB
) {
3797 SET_FOREACH(t
, u
->names
, i
) {
3802 r
= load_from_path(u
, t
);
3806 if (u
->load_state
!= UNIT_STUB
)
3811 /* And now, try looking for it under the suggested (originally linked) path */
3812 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3814 r
= load_from_path(u
, u
->fragment_path
);
3818 if (u
->load_state
== UNIT_STUB
)
3819 /* Hmm, this didn't work? Then let's get rid
3820 * of the fragment path stored for us, so that
3821 * we don't point to an invalid location. */
3822 u
->fragment_path
= mfree(u
->fragment_path
);
3825 /* Look for a template */
3826 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3827 _cleanup_free_
char *k
= NULL
;
3829 r
= unit_name_template(u
->id
, &k
);
3833 r
= load_from_path(u
, k
);
3837 if (u
->load_state
== UNIT_STUB
) {
3838 SET_FOREACH(t
, u
->names
, i
) {
3839 _cleanup_free_
char *z
= NULL
;
3844 r
= unit_name_template(t
, &z
);
3848 r
= load_from_path(u
, z
);
3852 if (u
->load_state
!= UNIT_STUB
)
3861 void unit_dump_config_items(FILE *f
) {
3862 static const struct {
3863 const ConfigParserCallback callback
;
3866 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3867 { config_parse_warn_compat
, "NOTSUPPORTED" },
3869 { config_parse_int
, "INTEGER" },
3870 { config_parse_unsigned
, "UNSIGNED" },
3871 { config_parse_iec_size
, "SIZE" },
3872 { config_parse_iec_uint64
, "SIZE" },
3873 { config_parse_si_size
, "SIZE" },
3874 { config_parse_bool
, "BOOLEAN" },
3875 { config_parse_string
, "STRING" },
3876 { config_parse_path
, "PATH" },
3877 { config_parse_unit_path_printf
, "PATH" },
3878 { config_parse_strv
, "STRING [...]" },
3879 { config_parse_exec_nice
, "NICE" },
3880 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3881 { config_parse_exec_io_class
, "IOCLASS" },
3882 { config_parse_exec_io_priority
, "IOPRIORITY" },
3883 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3884 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3885 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3886 { config_parse_mode
, "MODE" },
3887 { config_parse_unit_env_file
, "FILE" },
3888 { config_parse_output
, "OUTPUT" },
3889 { config_parse_input
, "INPUT" },
3890 { config_parse_log_facility
, "FACILITY" },
3891 { config_parse_log_level
, "LEVEL" },
3892 { config_parse_exec_capabilities
, "CAPABILITIES" },
3893 { config_parse_exec_secure_bits
, "SECUREBITS" },
3894 { config_parse_bounding_set
, "BOUNDINGSET" },
3895 { config_parse_limit
, "LIMIT" },
3896 { config_parse_unit_deps
, "UNIT [...]" },
3897 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3898 { config_parse_service_type
, "SERVICETYPE" },
3899 { config_parse_service_restart
, "SERVICERESTART" },
3900 #ifdef HAVE_SYSV_COMPAT
3901 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3903 { config_parse_kill_mode
, "KILLMODE" },
3904 { config_parse_signal
, "SIGNAL" },
3905 { config_parse_socket_listen
, "SOCKET [...]" },
3906 { config_parse_socket_bind
, "SOCKETBIND" },
3907 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3908 { config_parse_sec
, "SECONDS" },
3909 { config_parse_nsec
, "NANOSECONDS" },
3910 { config_parse_namespace_path_strv
, "PATH [...]" },
3911 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3912 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3913 { config_parse_unit_string_printf
, "STRING" },
3914 { config_parse_trigger_unit
, "UNIT" },
3915 { config_parse_timer
, "TIMER" },
3916 { config_parse_path_spec
, "PATH" },
3917 { config_parse_notify_access
, "ACCESS" },
3918 { config_parse_ip_tos
, "TOS" },
3919 { config_parse_unit_condition_path
, "CONDITION" },
3920 { config_parse_unit_condition_string
, "CONDITION" },
3921 { config_parse_unit_condition_null
, "CONDITION" },
3922 { config_parse_unit_slice
, "SLICE" },
3923 { config_parse_documentation
, "URL" },
3924 { config_parse_service_timeout
, "SECONDS" },
3925 { config_parse_failure_action
, "ACTION" },
3926 { config_parse_set_status
, "STATUS" },
3927 { config_parse_service_sockets
, "SOCKETS" },
3928 { config_parse_environ
, "ENVIRON" },
3930 { config_parse_syscall_filter
, "SYSCALLS" },
3931 { config_parse_syscall_archs
, "ARCHS" },
3932 { config_parse_syscall_errno
, "ERRNO" },
3933 { config_parse_address_families
, "FAMILIES" },
3935 { config_parse_cpu_shares
, "SHARES" },
3936 { config_parse_memory_limit
, "LIMIT" },
3937 { config_parse_device_allow
, "DEVICE" },
3938 { config_parse_device_policy
, "POLICY" },
3939 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3940 { config_parse_blockio_weight
, "WEIGHT" },
3941 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3942 { config_parse_long
, "LONG" },
3943 { config_parse_socket_service
, "SERVICE" },
3945 { config_parse_exec_selinux_context
, "LABEL" },
3947 { config_parse_job_mode
, "MODE" },
3948 { config_parse_job_mode_isolate
, "BOOLEAN" },
3949 { config_parse_personality
, "PERSONALITY" },
3952 const char *prev
= NULL
;
3957 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3958 const char *rvalue
= "OTHER", *lvalue
;
3962 const ConfigPerfItem
*p
;
3964 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3966 dot
= strchr(i
, '.');
3967 lvalue
= dot
? dot
+ 1 : i
;
3971 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3975 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3978 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3979 if (p
->parse
== table
[j
].callback
) {
3980 rvalue
= table
[j
].rvalue
;
3984 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);