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/>.
23 #include <linux/oom.h>
30 #include <sys/prctl.h>
31 #include <sys/mount.h>
35 #include <sys/resource.h>
36 #include <sys/types.h>
43 #include "sd-messages.h"
46 #include "conf-parser.h"
47 #include "load-fragment.h"
50 #include "securebits.h"
52 #include "unit-name.h"
53 #include "unit-printf.h"
55 #include "path-util.h"
59 #include "bus-error.h"
60 #include "errno-list.h"
64 #include "seccomp-util.h"
67 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
68 int config_parse_warn_compat(
73 unsigned section_line
,
79 Disabled reason
= ltype
;
82 case DISABLED_CONFIGURATION
:
83 log_syntax(unit
, LOG_DEBUG
, filename
, line
, EINVAL
,
84 "Support for option %s= has been disabled at compile time and it is ignored", lvalue
);
86 case DISABLED_EXPERIMENTAL
:
87 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
88 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
96 int config_parse_unit_deps(const char *unit
,
100 unsigned section_line
,
107 UnitDependency d
= ltype
;
109 const char *word
, *state
;
116 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
117 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
120 t
= strndup(word
, l
);
124 r
= unit_name_printf(u
, t
, &k
);
126 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
127 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
131 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
133 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
134 "Failed to add dependency on %s, ignoring: %s", k
, strerror(-r
));
137 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
142 int config_parse_unit_string_printf(const char *unit
,
143 const char *filename
,
146 unsigned section_line
,
154 _cleanup_free_
char *k
= NULL
;
162 r
= unit_full_printf(u
, rvalue
, &k
);
164 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
165 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
167 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
168 k
? k
: rvalue
, data
, userdata
);
171 int config_parse_unit_strv_printf(const char *unit
,
172 const char *filename
,
175 unsigned section_line
,
183 _cleanup_free_
char *k
= NULL
;
191 r
= unit_full_printf(u
, rvalue
, &k
);
193 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
194 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
196 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
197 k
? k
: rvalue
, data
, userdata
);
200 int config_parse_unit_path_printf(const char *unit
,
201 const char *filename
,
204 unsigned section_line
,
211 _cleanup_free_
char *k
= NULL
;
220 r
= unit_full_printf(u
, rvalue
, &k
);
222 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
226 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
229 int config_parse_unit_path_strv_printf(
231 const char *filename
,
234 unsigned section_line
,
242 const char *word
, *state
;
252 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
253 _cleanup_free_
char *k
= NULL
;
259 r
= unit_full_printf(u
, t
, &k
);
261 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", t
, strerror(-r
));
265 if (!utf8_is_valid(k
)) {
266 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
270 if (!path_is_absolute(k
)) {
271 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Symlink path %s is not absolute, ignoring: %s", k
, strerror(-r
));
275 path_kill_slashes(k
);
284 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
289 int config_parse_socket_listen(const char *unit
,
290 const char *filename
,
293 unsigned section_line
,
300 _cleanup_free_ SocketPort
*p
= NULL
;
312 if (isempty(rvalue
)) {
313 /* An empty assignment removes all ports */
314 socket_free_ports(s
);
318 p
= new0(SocketPort
, 1);
322 if (ltype
!= SOCKET_SOCKET
) {
325 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
327 p
->path
= strdup(rvalue
);
331 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
332 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
335 path_kill_slashes(p
->path
);
337 } else if (streq(lvalue
, "ListenNetlink")) {
338 _cleanup_free_
char *k
= NULL
;
340 p
->type
= SOCKET_SOCKET
;
341 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
343 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
344 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
346 r
= socket_address_parse_netlink(&p
->address
, k
?: rvalue
);
348 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
349 "Failed to parse address value, ignoring: %s", rvalue
);
354 _cleanup_free_
char *k
= NULL
;
356 p
->type
= SOCKET_SOCKET
;
357 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
359 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
360 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
362 r
= socket_address_parse(&p
->address
, k
? k
: rvalue
);
364 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
365 "Failed to parse address value, ignoring: %s", rvalue
);
369 if (streq(lvalue
, "ListenStream"))
370 p
->address
.type
= SOCK_STREAM
;
371 else if (streq(lvalue
, "ListenDatagram"))
372 p
->address
.type
= SOCK_DGRAM
;
374 assert(streq(lvalue
, "ListenSequentialPacket"));
375 p
->address
.type
= SOCK_SEQPACKET
;
378 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
379 log_syntax(unit
, LOG_ERR
, filename
, line
, ENOTSUP
,
380 "Address family not supported, ignoring: %s", rvalue
);
389 LIST_FIND_TAIL(port
, s
->ports
, tail
);
390 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
392 LIST_PREPEND(port
, s
->ports
, p
);
398 int config_parse_socket_bind(const char *unit
,
399 const char *filename
,
402 unsigned section_line
,
410 SocketAddressBindIPv6Only b
;
419 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
423 r
= parse_boolean(rvalue
);
425 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
426 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
430 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
432 s
->bind_ipv6_only
= b
;
437 int config_parse_exec_nice(const char *unit
,
438 const char *filename
,
441 unsigned section_line
,
448 ExecContext
*c
= data
;
456 r
= safe_atoi(rvalue
, &priority
);
458 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
459 "Failed to parse nice priority, ignoring: %s. ", rvalue
);
463 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
464 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
465 "Nice priority out of range, ignoring: %s", rvalue
);
475 int config_parse_exec_oom_score_adjust(const char* unit
,
476 const char *filename
,
479 unsigned section_line
,
486 ExecContext
*c
= data
;
494 r
= safe_atoi(rvalue
, &oa
);
496 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
497 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue
);
501 if (oa
< OOM_SCORE_ADJ_MIN
|| oa
> OOM_SCORE_ADJ_MAX
) {
502 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
503 "OOM score adjust value out of range, ignoring: %s", rvalue
);
507 c
->oom_score_adjust
= oa
;
508 c
->oom_score_adjust_set
= true;
513 int config_parse_exec(const char *unit
,
514 const char *filename
,
517 unsigned section_line
,
524 ExecCommand
**e
= data
, *nce
;
536 if (isempty(rvalue
)) {
537 /* An empty assignment resets the list */
538 exec_command_free_list(*e
);
543 /* We accept an absolute path as first argument, or
544 * alternatively an absolute prefixed with @ to allow
545 * overriding of argv[0]. */
548 const char *word
, *state
;
550 bool honour_argv0
= false, ignore
= false;
556 rvalue
+= strspn(rvalue
, WHITESPACE
);
561 for (i
= 0; i
< 2; i
++) {
562 if (rvalue
[0] == '-' && !ignore
) {
567 if (rvalue
[0] == '@' && !honour_argv0
) {
573 if (*rvalue
!= '/') {
574 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
575 "Executable path is not absolute, ignoring: %s", rvalue
);
580 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
581 if (strneq(word
, ";", MAX(l
, 1U)))
586 if (!isempty(state
)) {
587 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
588 "Trailing garbage, ignoring.");
593 n
= new(char*, k
+ !honour_argv0
);
598 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
599 if (strneq(word
, ";", MAX(l
, 1U)))
601 else if (strneq(word
, "\\;", MAX(l
, 1U)))
604 if (honour_argv0
&& word
== rvalue
) {
607 path
= strndup(word
, l
);
613 if (!utf8_is_valid(path
)) {
614 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
622 c
= n
[k
++] = cunescape_length(word
, l
);
628 if (!utf8_is_valid(c
)) {
629 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
639 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
640 "Invalid command line, ignoring: %s", rvalue
);
653 assert(path_is_absolute(path
));
655 nce
= new0(ExecCommand
, 1);
663 nce
->ignore
= ignore
;
665 path_kill_slashes(nce
->path
);
667 exec_command_append_list(e
, nce
);
683 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
684 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
686 int config_parse_socket_bindtodevice(const char* unit
,
687 const char *filename
,
690 unsigned section_line
,
705 if (rvalue
[0] && !streq(rvalue
, "*")) {
712 free(s
->bind_to_device
);
713 s
->bind_to_device
= n
;
718 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
719 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
721 int config_parse_exec_io_class(const char *unit
,
722 const char *filename
,
725 unsigned section_line
,
732 ExecContext
*c
= data
;
740 x
= ioprio_class_from_string(rvalue
);
742 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
743 "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
747 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
748 c
->ioprio_set
= true;
753 int config_parse_exec_io_priority(const char *unit
,
754 const char *filename
,
757 unsigned section_line
,
764 ExecContext
*c
= data
;
772 r
= safe_atoi(rvalue
, &i
);
773 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
774 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
775 "Failed to parse IO priority, ignoring: %s", rvalue
);
779 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
780 c
->ioprio_set
= true;
785 int config_parse_exec_cpu_sched_policy(const char *unit
,
786 const char *filename
,
789 unsigned section_line
,
797 ExecContext
*c
= data
;
805 x
= sched_policy_from_string(rvalue
);
807 log_syntax(unit
, LOG_ERR
, filename
, line
, -x
,
808 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
812 c
->cpu_sched_policy
= x
;
813 /* Moving to or from real-time policy? We need to adjust the priority */
814 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
815 c
->cpu_sched_set
= true;
820 int config_parse_exec_cpu_sched_prio(const char *unit
,
821 const char *filename
,
824 unsigned section_line
,
831 ExecContext
*c
= data
;
839 r
= safe_atoi(rvalue
, &i
);
841 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
842 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
846 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
847 min
= sched_get_priority_min(c
->cpu_sched_policy
);
848 max
= sched_get_priority_max(c
->cpu_sched_policy
);
850 if (i
< min
|| i
> max
) {
851 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
852 "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
856 c
->cpu_sched_priority
= i
;
857 c
->cpu_sched_set
= true;
862 int config_parse_exec_cpu_affinity(const char *unit
,
863 const char *filename
,
866 unsigned section_line
,
873 ExecContext
*c
= data
;
874 const char *word
, *state
;
882 if (isempty(rvalue
)) {
883 /* An empty assignment resets the CPU list */
890 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
891 _cleanup_free_
char *t
= NULL
;
895 t
= strndup(word
, l
);
899 r
= safe_atou(t
, &cpu
);
902 c
->cpuset
= cpu_set_malloc(&c
->cpuset_ncpus
);
907 if (r
< 0 || cpu
>= c
->cpuset_ncpus
) {
908 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
909 "Failed to parse CPU affinity '%s', ignoring: %s", t
, rvalue
);
913 CPU_SET_S(cpu
, CPU_ALLOC_SIZE(c
->cpuset_ncpus
), c
->cpuset
);
916 log_syntax(unit
, LOG_WARNING
, filename
, line
, EINVAL
,
917 "Trailing garbage, ignoring.");
922 int config_parse_exec_capabilities(const char *unit
,
923 const char *filename
,
926 unsigned section_line
,
933 ExecContext
*c
= data
;
941 cap
= cap_from_text(rvalue
);
943 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
,
944 "Failed to parse capabilities, ignoring: %s", rvalue
);
949 cap_free(c
->capabilities
);
950 c
->capabilities
= cap
;
955 int config_parse_exec_secure_bits(const char *unit
,
956 const char *filename
,
959 unsigned section_line
,
966 ExecContext
*c
= data
;
968 const char *word
, *state
;
975 if (isempty(rvalue
)) {
976 /* An empty assignment resets the field */
981 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
982 if (first_word(word
, "keep-caps"))
983 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
984 else if (first_word(word
, "keep-caps-locked"))
985 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
986 else if (first_word(word
, "no-setuid-fixup"))
987 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
988 else if (first_word(word
, "no-setuid-fixup-locked"))
989 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
990 else if (first_word(word
, "noroot"))
991 c
->secure_bits
|= 1<<SECURE_NOROOT
;
992 else if (first_word(word
, "noroot-locked"))
993 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
995 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
996 "Failed to parse secure bits, ignoring: %s", rvalue
);
1000 if (!isempty(state
))
1001 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1002 "Invalid syntax, garbage at the end, ignoring.");
1007 int config_parse_bounding_set(const char *unit
,
1008 const char *filename
,
1010 const char *section
,
1011 unsigned section_line
,
1018 uint64_t *capability_bounding_set_drop
= data
;
1019 const char *word
, *state
;
1021 bool invert
= false;
1029 if (rvalue
[0] == '~') {
1034 /* Note that we store this inverted internally, since the
1035 * kernel wants it like this. But we actually expose it
1036 * non-inverted everywhere to have a fully normalized
1039 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1040 _cleanup_free_
char *t
= NULL
;
1044 t
= strndup(word
, l
);
1048 r
= cap_from_name(t
, &cap
);
1050 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
,
1051 "Failed to parse capability in bounding set, ignoring: %s", t
);
1055 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1057 if (!isempty(state
))
1058 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1059 "Trailing garbage, ignoring.");
1062 *capability_bounding_set_drop
|= sum
;
1064 *capability_bounding_set_drop
|= ~sum
;
1069 int config_parse_limit(const char *unit
,
1070 const char *filename
,
1072 const char *section
,
1073 unsigned section_line
,
1080 struct rlimit
**rl
= data
;
1081 unsigned long long u
;
1090 if (streq(rvalue
, "infinity"))
1091 u
= (unsigned long long) RLIM_INFINITY
;
1095 r
= safe_atollu(rvalue
, &u
);
1097 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1098 "Failed to parse resource value, ignoring: %s", rvalue
);
1104 *rl
= new(struct rlimit
, 1);
1109 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1113 #ifdef HAVE_SYSV_COMPAT
1114 int config_parse_sysv_priority(const char *unit
,
1115 const char *filename
,
1117 const char *section
,
1118 unsigned section_line
,
1125 int *priority
= data
;
1133 r
= safe_atoi(rvalue
, &i
);
1134 if (r
< 0 || i
< 0) {
1135 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1136 "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1140 *priority
= (int) i
;
1145 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1147 int config_parse_kill_signal(const char *unit
,
1148 const char *filename
,
1150 const char *section
,
1151 unsigned section_line
,
1166 r
= signal_from_string_try_harder(rvalue
);
1168 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1169 "Failed to parse kill signal, ignoring: %s", rvalue
);
1177 int config_parse_exec_mount_flags(const char *unit
,
1178 const char *filename
,
1180 const char *section
,
1181 unsigned section_line
,
1188 ExecContext
*c
= data
;
1189 const char *word
, *state
;
1191 unsigned long flags
= 0;
1198 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1199 _cleanup_free_
char *t
;
1201 t
= strndup(word
, l
);
1205 if (streq(t
, "shared"))
1207 else if (streq(t
, "slave"))
1209 else if (streq(word
, "private"))
1212 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1213 "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1217 if (!isempty(state
))
1218 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1219 "Trailing garbage, ignoring.");
1221 c
->mount_flags
= flags
;
1225 int config_parse_exec_selinux_context(
1227 const char *filename
,
1229 const char *section
,
1230 unsigned section_line
,
1237 ExecContext
*c
= data
;
1248 if (isempty(rvalue
)) {
1249 free(c
->selinux_context
);
1250 c
->selinux_context
= NULL
;
1251 c
->selinux_context_ignore
= false;
1255 if (rvalue
[0] == '-') {
1261 r
= unit_name_printf(u
, rvalue
, &k
);
1263 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1264 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1268 free(c
->selinux_context
);
1269 c
->selinux_context
= k
;
1270 c
->selinux_context_ignore
= ignore
;
1275 int config_parse_exec_apparmor_profile(
1277 const char *filename
,
1279 const char *section
,
1280 unsigned section_line
,
1287 ExecContext
*c
= data
;
1298 if (isempty(rvalue
)) {
1299 free(c
->apparmor_profile
);
1300 c
->apparmor_profile
= NULL
;
1301 c
->apparmor_profile_ignore
= false;
1305 if (rvalue
[0] == '-') {
1311 r
= unit_name_printf(u
, rvalue
, &k
);
1313 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1314 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1318 free(c
->apparmor_profile
);
1319 c
->apparmor_profile
= k
;
1320 c
->apparmor_profile_ignore
= ignore
;
1325 int config_parse_exec_smack_process_label(
1327 const char *filename
,
1329 const char *section
,
1330 unsigned section_line
,
1337 ExecContext
*c
= data
;
1348 if (isempty(rvalue
)) {
1349 free(c
->smack_process_label
);
1350 c
->smack_process_label
= NULL
;
1351 c
->smack_process_label_ignore
= false;
1355 if (rvalue
[0] == '-') {
1361 r
= unit_name_printf(u
, rvalue
, &k
);
1363 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1364 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1368 free(c
->smack_process_label
);
1369 c
->smack_process_label
= k
;
1370 c
->smack_process_label_ignore
= ignore
;
1375 int config_parse_timer(const char *unit
,
1376 const char *filename
,
1378 const char *section
,
1379 unsigned section_line
,
1390 CalendarSpec
*c
= NULL
;
1397 if (isempty(rvalue
)) {
1398 /* Empty assignment resets list */
1399 timer_free_values(t
);
1403 b
= timer_base_from_string(lvalue
);
1405 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
,
1406 "Failed to parse timer base, ignoring: %s", lvalue
);
1410 if (b
== TIMER_CALENDAR
) {
1411 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1412 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1413 "Failed to parse calendar specification, ignoring: %s",
1418 if (parse_sec(rvalue
, &u
) < 0) {
1419 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1420 "Failed to parse timer value, ignoring: %s",
1426 v
= new0(TimerValue
, 1);
1428 calendar_spec_free(c
);
1434 v
->calendar_spec
= c
;
1436 LIST_PREPEND(value
, t
->values
, v
);
1441 int config_parse_trigger_unit(
1443 const char *filename
,
1445 const char *section
,
1446 unsigned section_line
,
1453 _cleanup_free_
char *p
= NULL
;
1463 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1464 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1465 "Multiple units to trigger specified, ignoring: %s", rvalue
);
1469 r
= unit_name_printf(u
, rvalue
, &p
);
1471 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1472 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1474 type
= unit_name_to_type(p
?: rvalue
);
1476 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1477 "Unit type not valid, ignoring: %s", rvalue
);
1481 if (type
== u
->type
) {
1482 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1483 "Trigger cannot be of same type, ignoring: %s", rvalue
);
1487 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
?: rvalue
, NULL
, true);
1489 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1490 "Failed to add trigger on %s, ignoring: %s", p
?: rvalue
, strerror(-r
));
1497 int config_parse_path_spec(const char *unit
,
1498 const char *filename
,
1500 const char *section
,
1501 unsigned section_line
,
1511 _cleanup_free_
char *k
= NULL
;
1519 if (isempty(rvalue
)) {
1520 /* Empty assignment clears list */
1525 b
= path_type_from_string(lvalue
);
1527 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1528 "Failed to parse path type, ignoring: %s", lvalue
);
1532 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1538 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1539 "Failed to resolve unit specifiers on %s. Ignoring.",
1543 if (!path_is_absolute(k
)) {
1544 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1545 "Path is not absolute, ignoring: %s", k
);
1549 s
= new0(PathSpec
, 1);
1554 s
->path
= path_kill_slashes(k
);
1559 LIST_PREPEND(spec
, p
->specs
, s
);
1564 int config_parse_socket_service(const char *unit
,
1565 const char *filename
,
1567 const char *section
,
1568 unsigned section_line
,
1575 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1579 _cleanup_free_
char *p
= NULL
;
1586 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1588 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1589 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1593 if (!endswith(p
, ".service")) {
1594 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1595 "Unit must be of type service, ignoring: %s", rvalue
);
1599 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1601 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1602 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1606 unit_ref_set(&s
->service
, x
);
1611 int config_parse_service_sockets(const char *unit
,
1612 const char *filename
,
1614 const char *section
,
1615 unsigned section_line
,
1624 const char *word
, *state
;
1632 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1633 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1635 t
= strndup(word
, l
);
1639 r
= unit_name_printf(UNIT(s
), t
, &k
);
1641 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1642 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1644 if (!endswith(k
?: t
, ".socket")) {
1645 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1646 "Unit must be of type socket, ignoring: %s", k
?: t
);
1650 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
?: t
, NULL
, true);
1652 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1653 "Failed to add dependency on %s, ignoring: %s",
1654 k
?: t
, strerror(-r
));
1656 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
?: t
, NULL
, true);
1660 if (!isempty(state
))
1661 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1662 "Trailing garbage, ignoring.");
1667 int config_parse_service_timeout(const char *unit
,
1668 const char *filename
,
1670 const char *section
,
1671 unsigned section_line
,
1678 Service
*s
= userdata
;
1686 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1687 rvalue
, data
, userdata
);
1691 if (streq(lvalue
, "TimeoutSec")) {
1692 s
->start_timeout_defined
= true;
1693 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1694 } else if (streq(lvalue
, "TimeoutStartSec"))
1695 s
->start_timeout_defined
= true;
1700 int config_parse_busname_service(
1702 const char *filename
,
1704 const char *section
,
1705 unsigned section_line
,
1712 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1716 _cleanup_free_
char *p
= NULL
;
1723 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1725 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1726 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1730 if (!endswith(p
, ".service")) {
1731 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1732 "Unit must be of type service, ignoring: %s", rvalue
);
1736 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1738 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1739 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1743 unit_ref_set(&n
->service
, x
);
1748 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1750 int config_parse_bus_policy(
1752 const char *filename
,
1754 const char *section
,
1755 unsigned section_line
,
1762 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1763 _cleanup_free_
char *id_str
= NULL
;
1764 BusName
*busname
= data
;
1772 p
= new0(BusNamePolicy
, 1);
1776 if (streq(lvalue
, "AllowUser"))
1777 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1778 else if (streq(lvalue
, "AllowGroup"))
1779 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1781 assert_not_reached("Unknown lvalue");
1783 id_str
= strdup(rvalue
);
1787 access_str
= strpbrk(id_str
, WHITESPACE
);
1789 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1790 "Invalid busname policy value '%s'", rvalue
);
1796 access_str
+= strspn(access_str
, WHITESPACE
);
1798 p
->access
= bus_policy_access_from_string(access_str
);
1799 if (p
->access
< 0) {
1800 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1801 "Invalid busname policy access type '%s'", access_str
);
1808 LIST_PREPEND(policy
, busname
->policy
, p
);
1814 int config_parse_bus_endpoint_policy(
1816 const char *filename
,
1818 const char *section
,
1819 unsigned section_line
,
1826 _cleanup_free_
char *name
= NULL
;
1827 BusPolicyAccess access
;
1828 ExecContext
*c
= data
;
1837 name
= strdup(rvalue
);
1841 access_str
= strpbrk(name
, WHITESPACE
);
1843 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1844 "Invalid endpoint policy value '%s'", rvalue
);
1850 access_str
+= strspn(access_str
, WHITESPACE
);
1852 access
= bus_policy_access_from_string(access_str
);
1853 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1854 access
>= _BUS_POLICY_ACCESS_MAX
) {
1855 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1856 "Invalid endpoint policy access type '%s'", access_str
);
1860 if (!c
->bus_endpoint
) {
1861 r
= bus_endpoint_new(&c
->bus_endpoint
);
1867 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1870 int config_parse_unit_env_file(const char *unit
,
1871 const char *filename
,
1873 const char *section
,
1874 unsigned section_line
,
1883 _cleanup_free_
char *n
= NULL
;
1892 if (isempty(rvalue
)) {
1893 /* Empty assignment frees the list */
1899 r
= unit_full_printf(u
, rvalue
, &n
);
1901 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1902 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1905 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1906 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1907 "Path '%s' is not absolute, ignoring.", s
);
1911 r
= strv_extend(env
, s
);
1918 int config_parse_environ(const char *unit
,
1919 const char *filename
,
1921 const char *section
,
1922 unsigned section_line
,
1931 const char *word
, *state
;
1933 _cleanup_free_
char *k
= NULL
;
1941 if (isempty(rvalue
)) {
1942 /* Empty assignment resets the list */
1949 r
= unit_full_printf(u
, rvalue
, &k
);
1951 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1952 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1960 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1961 _cleanup_free_
char *n
;
1964 n
= cunescape_length(word
, l
);
1968 if (!env_assignment_is_valid(n
)) {
1969 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1970 "Invalid environment assignment, ignoring: %s", rvalue
);
1974 x
= strv_env_set(*env
, n
);
1981 if (!isempty(state
))
1982 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1983 "Trailing garbage, ignoring.");
1988 int config_parse_ip_tos(const char *unit
,
1989 const char *filename
,
1991 const char *section
,
1992 unsigned section_line
,
1999 int *ip_tos
= data
, x
;
2006 x
= ip_tos_from_string(rvalue
);
2008 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2009 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2017 int config_parse_unit_condition_path(
2019 const char *filename
,
2021 const char *section
,
2022 unsigned section_line
,
2029 _cleanup_free_
char *p
= NULL
;
2030 Condition
**list
= data
, *c
;
2031 ConditionType t
= ltype
;
2032 bool trigger
, negate
;
2041 if (isempty(rvalue
)) {
2042 /* Empty assignment resets the list */
2043 condition_free_list(*list
);
2048 trigger
= rvalue
[0] == '|';
2052 negate
= rvalue
[0] == '!';
2056 r
= unit_full_printf(u
, rvalue
, &p
);
2058 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2062 if (!path_is_absolute(p
)) {
2063 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2067 c
= condition_new(t
, p
, trigger
, negate
);
2071 LIST_PREPEND(conditions
, *list
, c
);
2075 int config_parse_unit_condition_string(
2077 const char *filename
,
2079 const char *section
,
2080 unsigned section_line
,
2087 _cleanup_free_
char *s
= NULL
;
2088 Condition
**list
= data
, *c
;
2089 ConditionType t
= ltype
;
2090 bool trigger
, negate
;
2099 if (isempty(rvalue
)) {
2100 /* Empty assignment resets the list */
2101 condition_free_list(*list
);
2106 trigger
= rvalue
[0] == '|';
2110 negate
= rvalue
[0] == '!';
2114 r
= unit_full_printf(u
, rvalue
, &s
);
2116 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2120 c
= condition_new(t
, s
, trigger
, negate
);
2124 LIST_PREPEND(conditions
, *list
, c
);
2128 int config_parse_unit_condition_null(
2130 const char *filename
,
2132 const char *section
,
2133 unsigned section_line
,
2140 Condition
**list
= data
, *c
;
2141 bool trigger
, negate
;
2149 if (isempty(rvalue
)) {
2150 /* Empty assignment resets the list */
2151 condition_free_list(*list
);
2156 trigger
= rvalue
[0] == '|';
2160 negate
= rvalue
[0] == '!';
2164 b
= parse_boolean(rvalue
);
2166 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2173 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2177 LIST_PREPEND(conditions
, *list
, c
);
2181 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2182 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2184 int config_parse_unit_requires_mounts_for(
2186 const char *filename
,
2188 const char *section
,
2189 unsigned section_line
,
2197 const char *word
, *state
;
2205 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2207 _cleanup_free_
char *n
;
2209 n
= strndup(word
, l
);
2213 if (!utf8_is_valid(n
)) {
2214 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2218 r
= unit_require_mounts_for(u
, n
);
2220 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2221 "Failed to add required mount for, ignoring: %s", rvalue
);
2225 if (!isempty(state
))
2226 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2227 "Trailing garbage, ignoring.");
2232 int config_parse_documentation(const char *unit
,
2233 const char *filename
,
2235 const char *section
,
2236 unsigned section_line
,
2252 if (isempty(rvalue
)) {
2253 /* Empty assignment resets the list */
2254 strv_free(u
->documentation
);
2255 u
->documentation
= NULL
;
2259 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2260 rvalue
, data
, userdata
);
2264 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2266 if (is_valid_documentation_url(*a
))
2269 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2270 "Invalid URL, ignoring: %s", *a
);
2281 int config_parse_syscall_filter(
2283 const char *filename
,
2285 const char *section
,
2286 unsigned section_line
,
2293 static const char default_syscalls
[] =
2300 ExecContext
*c
= data
;
2302 bool invert
= false;
2303 const char *word
, *state
;
2312 if (isempty(rvalue
)) {
2313 /* Empty assignment resets the list */
2314 set_free(c
->syscall_filter
);
2315 c
->syscall_filter
= NULL
;
2316 c
->syscall_whitelist
= false;
2320 if (rvalue
[0] == '~') {
2325 if (!c
->syscall_filter
) {
2326 c
->syscall_filter
= set_new(NULL
);
2327 if (!c
->syscall_filter
)
2331 /* Allow everything but the ones listed */
2332 c
->syscall_whitelist
= false;
2336 /* Allow nothing but the ones listed */
2337 c
->syscall_whitelist
= true;
2339 /* Accept default syscalls if we are on a whitelist */
2340 NULSTR_FOREACH(i
, default_syscalls
) {
2343 id
= seccomp_syscall_resolve_name(i
);
2347 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2356 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2357 _cleanup_free_
char *t
= NULL
;
2360 t
= strndup(word
, l
);
2364 id
= seccomp_syscall_resolve_name(t
);
2366 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2367 "Failed to parse system call, ignoring: %s", t
);
2371 /* If we previously wanted to forbid a syscall and now
2372 * we want to allow it, then remove it from the list
2374 if (!invert
== c
->syscall_whitelist
) {
2375 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2381 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2383 if (!isempty(state
))
2384 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2385 "Trailing garbage, ignoring.");
2387 /* Turn on NNP, but only if it wasn't configured explicitly
2388 * before, and only if we are in user mode. */
2389 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== SYSTEMD_USER
)
2390 c
->no_new_privileges
= true;
2395 int config_parse_syscall_archs(
2397 const char *filename
,
2399 const char *section
,
2400 unsigned section_line
,
2408 const char *word
, *state
;
2412 if (isempty(rvalue
)) {
2418 r
= set_ensure_allocated(archs
, NULL
);
2422 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2423 _cleanup_free_
char *t
= NULL
;
2426 t
= strndup(word
, l
);
2430 r
= seccomp_arch_from_string(t
, &a
);
2432 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2433 "Failed to parse system call architecture, ignoring: %s", t
);
2437 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2443 if (!isempty(state
))
2444 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2445 "Trailing garbage, ignoring.");
2450 int config_parse_syscall_errno(
2452 const char *filename
,
2454 const char *section
,
2455 unsigned section_line
,
2462 ExecContext
*c
= data
;
2469 if (isempty(rvalue
)) {
2470 /* Empty assignment resets to KILL */
2471 c
->syscall_errno
= 0;
2475 e
= errno_from_name(rvalue
);
2477 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2478 "Failed to parse error number, ignoring: %s", rvalue
);
2482 c
->syscall_errno
= e
;
2486 int config_parse_address_families(
2488 const char *filename
,
2490 const char *section
,
2491 unsigned section_line
,
2498 ExecContext
*c
= data
;
2499 bool invert
= false;
2500 const char *word
, *state
;
2508 if (isempty(rvalue
)) {
2509 /* Empty assignment resets the list */
2510 set_free(c
->address_families
);
2511 c
->address_families
= NULL
;
2512 c
->address_families_whitelist
= false;
2516 if (rvalue
[0] == '~') {
2521 if (!c
->address_families
) {
2522 c
->address_families
= set_new(NULL
);
2523 if (!c
->address_families
)
2526 c
->address_families_whitelist
= !invert
;
2529 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2530 _cleanup_free_
char *t
= NULL
;
2533 t
= strndup(word
, l
);
2537 af
= af_from_name(t
);
2539 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2540 "Failed to parse address family, ignoring: %s", t
);
2544 /* If we previously wanted to forbid an address family and now
2545 * we want to allow it, then remove it from the list
2547 if (!invert
== c
->address_families_whitelist
) {
2548 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2554 set_remove(c
->address_families
, INT_TO_PTR(af
));
2556 if (!isempty(state
))
2557 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2558 "Trailing garbage, ignoring.");
2564 int config_parse_unit_slice(
2566 const char *filename
,
2568 const char *section
,
2569 unsigned section_line
,
2576 _cleanup_free_
char *k
= NULL
;
2577 Unit
*u
= userdata
, *slice
;
2585 r
= unit_name_printf(u
, rvalue
, &k
);
2587 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2588 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2595 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2597 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2598 "Failed to load slice unit %s. Ignoring.", k
);
2602 if (slice
->type
!= UNIT_SLICE
) {
2603 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2604 "Slice unit %s is not a slice. Ignoring.", k
);
2608 unit_ref_set(&u
->slice
, slice
);
2612 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2614 int config_parse_cpu_shares(
2616 const char *filename
,
2618 const char *section
,
2619 unsigned section_line
,
2626 unsigned long *shares
= data
, lu
;
2633 if (isempty(rvalue
)) {
2634 *shares
= (unsigned long) -1;
2638 r
= safe_atolu(rvalue
, &lu
);
2639 if (r
< 0 || lu
<= 0) {
2640 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2641 "CPU shares '%s' invalid. Ignoring.", rvalue
);
2649 int config_parse_cpu_quota(
2651 const char *filename
,
2653 const char *section
,
2654 unsigned section_line
,
2661 CGroupContext
*c
= data
;
2668 if (isempty(rvalue
)) {
2669 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2673 if (!endswith(rvalue
, "%")) {
2675 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2676 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2680 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2681 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2682 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2686 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2691 int config_parse_memory_limit(
2693 const char *filename
,
2695 const char *section
,
2696 unsigned section_line
,
2703 CGroupContext
*c
= data
;
2707 if (isempty(rvalue
)) {
2708 c
->memory_limit
= (uint64_t) -1;
2712 assert_cc(sizeof(uint64_t) == sizeof(off_t
));
2714 r
= parse_size(rvalue
, 1024, &bytes
);
2716 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2717 "Memory limit '%s' invalid. Ignoring.", rvalue
);
2721 c
->memory_limit
= (uint64_t) bytes
;
2725 int config_parse_device_allow(
2727 const char *filename
,
2729 const char *section
,
2730 unsigned section_line
,
2737 _cleanup_free_
char *path
= NULL
;
2738 CGroupContext
*c
= data
;
2739 CGroupDeviceAllow
*a
;
2743 if (isempty(rvalue
)) {
2744 while (c
->device_allow
)
2745 cgroup_context_free_device_allow(c
, c
->device_allow
);
2750 n
= strcspn(rvalue
, WHITESPACE
);
2751 path
= strndup(rvalue
, n
);
2755 if (!startswith(path
, "/dev/") &&
2756 !startswith(path
, "block-") &&
2757 !startswith(path
, "char-")) {
2758 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2759 "Invalid device node path '%s'. Ignoring.", path
);
2763 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2767 if (!in_charset(m
, "rwm")) {
2768 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2769 "Invalid device rights '%s'. Ignoring.", m
);
2773 a
= new0(CGroupDeviceAllow
, 1);
2779 a
->r
= !!strchr(m
, 'r');
2780 a
->w
= !!strchr(m
, 'w');
2781 a
->m
= !!strchr(m
, 'm');
2783 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2787 int config_parse_blockio_weight(
2789 const char *filename
,
2791 const char *section
,
2792 unsigned section_line
,
2799 unsigned long *weight
= data
, lu
;
2806 if (isempty(rvalue
)) {
2807 *weight
= (unsigned long) -1;
2811 r
= safe_atolu(rvalue
, &lu
);
2812 if (r
< 0 || lu
< 10 || lu
> 1000) {
2813 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2814 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2822 int config_parse_blockio_device_weight(
2824 const char *filename
,
2826 const char *section
,
2827 unsigned section_line
,
2834 _cleanup_free_
char *path
= NULL
;
2835 CGroupBlockIODeviceWeight
*w
;
2836 CGroupContext
*c
= data
;
2846 if (isempty(rvalue
)) {
2847 while (c
->blockio_device_weights
)
2848 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2853 n
= strcspn(rvalue
, WHITESPACE
);
2854 weight
= rvalue
+ n
;
2856 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2857 "Expected block device and device weight. Ignoring.");
2861 path
= strndup(rvalue
, n
);
2865 if (!path_startswith(path
, "/dev")) {
2866 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2867 "Invalid device node path '%s'. Ignoring.", path
);
2871 weight
+= strspn(weight
, WHITESPACE
);
2872 r
= safe_atolu(weight
, &lu
);
2873 if (r
< 0 || lu
< 10 || lu
> 1000) {
2874 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2875 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2879 w
= new0(CGroupBlockIODeviceWeight
, 1);
2888 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2892 int config_parse_blockio_bandwidth(
2894 const char *filename
,
2896 const char *section
,
2897 unsigned section_line
,
2904 _cleanup_free_
char *path
= NULL
;
2905 CGroupBlockIODeviceBandwidth
*b
;
2906 CGroupContext
*c
= data
;
2907 const char *bandwidth
;
2917 read
= streq("BlockIOReadBandwidth", lvalue
);
2919 if (isempty(rvalue
)) {
2920 CGroupBlockIODeviceBandwidth
*next
;
2922 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2923 if (b
->read
== read
)
2924 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2929 n
= strcspn(rvalue
, WHITESPACE
);
2930 bandwidth
= rvalue
+ n
;
2931 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2934 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2935 "Expected space separated pair of device node and bandwidth. Ignoring.");
2939 path
= strndup(rvalue
, n
);
2943 if (!path_startswith(path
, "/dev")) {
2944 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2945 "Invalid device node path '%s'. Ignoring.", path
);
2949 r
= parse_size(bandwidth
, 1000, &bytes
);
2950 if (r
< 0 || bytes
<= 0) {
2951 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2952 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2956 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2962 b
->bandwidth
= (uint64_t) bytes
;
2965 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2970 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
2972 int config_parse_job_mode_isolate(
2974 const char *filename
,
2976 const char *section
,
2977 unsigned section_line
,
2991 r
= parse_boolean(rvalue
);
2993 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2994 "Failed to parse boolean, ignoring: %s", rvalue
);
2998 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3002 int config_parse_personality(
3004 const char *filename
,
3006 const char *section
,
3007 unsigned section_line
,
3014 unsigned long *personality
= data
, p
;
3019 assert(personality
);
3021 p
= personality_from_string(rvalue
);
3022 if (p
== 0xffffffffUL
) {
3023 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3024 "Failed to parse personality, ignoring: %s", rvalue
);
3032 int config_parse_runtime_directory(
3034 const char *filename
,
3036 const char *section
,
3037 unsigned section_line
,
3045 const char *word
, *state
;
3054 if (isempty(rvalue
)) {
3055 /* Empty assignment resets the list */
3061 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3062 _cleanup_free_
char *n
;
3064 n
= strndup(word
, l
);
3068 if (!filename_is_safe(n
)) {
3069 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3070 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3074 r
= strv_push(rt
, n
);
3080 if (!isempty(state
))
3081 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3082 "Trailing garbage, ignoring.");
3087 int config_parse_set_status(
3089 const char *filename
,
3091 const char *section
,
3092 unsigned section_line
,
3100 const char *word
, *state
;
3102 ExitStatusSet
*status_set
= data
;
3109 /* Empty assignment resets the list */
3110 if (isempty(rvalue
)) {
3111 exit_status_set_free(status_set
);
3115 FOREACH_WORD(word
, l
, rvalue
, state
) {
3116 _cleanup_free_
char *temp
;
3119 temp
= strndup(word
, l
);
3123 r
= safe_atoi(temp
, &val
);
3125 val
= signal_from_string_try_harder(temp
);
3128 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3129 "Failed to parse value, ignoring: %s", word
);
3133 if (val
< 0 || val
> 255) {
3134 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3135 "Value %d is outside range 0-255, ignoring", val
);
3140 r
= set_ensure_allocated(&status_set
->status
, NULL
);
3144 r
= set_put(status_set
->status
, INT_TO_PTR(val
));
3146 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3147 "Unable to store: %s", word
);
3151 if (!isempty(state
))
3152 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3153 "Trailing garbage, ignoring.");
3158 int config_parse_namespace_path_strv(
3160 const char *filename
,
3162 const char *section
,
3163 unsigned section_line
,
3171 const char *word
, *state
;
3180 if (isempty(rvalue
)) {
3181 /* Empty assignment resets the list */
3187 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3188 _cleanup_free_
char *n
;
3191 n
= strndup(word
, l
);
3195 if (!utf8_is_valid(n
)) {
3196 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3200 offset
= n
[0] == '-';
3201 if (!path_is_absolute(n
+ offset
)) {
3202 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3203 "Not an absolute path, ignoring: %s", rvalue
);
3207 path_kill_slashes(n
);
3209 r
= strv_push(sv
, n
);
3215 if (!isempty(state
))
3216 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3217 "Trailing garbage, ignoring.");
3222 int config_parse_no_new_privileges(
3224 const char *filename
,
3226 const char *section
,
3227 unsigned section_line
,
3234 ExecContext
*c
= data
;
3242 k
= parse_boolean(rvalue
);
3244 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3245 "Failed to parse boolean value, ignoring: %s", rvalue
);
3249 c
->no_new_privileges
= !!k
;
3250 c
->no_new_privileges_set
= true;
3255 int config_parse_protect_home(
3257 const char *filename
,
3259 const char *section
,
3260 unsigned section_line
,
3267 ExecContext
*c
= data
;
3275 /* Our enum shall be a superset of booleans, hence first try
3276 * to parse as as boolean, and then as enum */
3278 k
= parse_boolean(rvalue
);
3280 c
->protect_home
= PROTECT_HOME_YES
;
3282 c
->protect_home
= PROTECT_HOME_NO
;
3286 h
= protect_home_from_string(rvalue
);
3288 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3289 "Failed to parse protect home value, ignoring: %s", rvalue
);
3293 c
->protect_home
= h
;
3299 int config_parse_protect_system(
3301 const char *filename
,
3303 const char *section
,
3304 unsigned section_line
,
3311 ExecContext
*c
= data
;
3319 /* Our enum shall be a superset of booleans, hence first try
3320 * to parse as as boolean, and then as enum */
3322 k
= parse_boolean(rvalue
);
3324 c
->protect_system
= PROTECT_SYSTEM_YES
;
3326 c
->protect_system
= PROTECT_SYSTEM_NO
;
3330 s
= protect_system_from_string(rvalue
);
3332 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3333 "Failed to parse protect system value, ignoring: %s", rvalue
);
3337 c
->protect_system
= s
;
3343 #define FOLLOW_MAX 8
3345 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3356 /* This will update the filename pointer if the loaded file is
3357 * reached by a symlink. The old string will be freed. */
3360 char *target
, *name
;
3362 if (c
++ >= FOLLOW_MAX
)
3365 path_kill_slashes(*filename
);
3367 /* Add the file name we are currently looking at to
3368 * the names of this unit, but only if it is a valid
3370 name
= basename(*filename
);
3372 if (unit_name_is_valid(name
, TEMPLATE_VALID
)) {
3374 id
= set_get(names
, name
);
3380 r
= set_consume(names
, id
);
3386 /* Try to open the file name, but don't if its a symlink */
3387 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3394 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3395 r
= readlink_and_make_absolute(*filename
, &target
);
3403 f
= fdopen(fd
, "re");
3415 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3423 /* Let's try to add in all symlink names we found */
3424 while ((k
= set_steal_first(names
))) {
3426 /* First try to merge in the other name into our
3428 r
= unit_merge_by_name(*u
, k
);
3432 /* Hmm, we couldn't merge the other unit into
3433 * ours? Then let's try it the other way
3436 other
= manager_get_unit((*u
)->manager
, k
);
3440 r
= unit_merge(other
, *u
);
3443 return merge_by_names(u
, names
, NULL
);
3451 unit_choose_id(*u
, id
);
3459 static int load_from_path(Unit
*u
, const char *path
) {
3461 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3462 _cleanup_fclose_
FILE *f
= NULL
;
3463 _cleanup_free_
char *filename
= NULL
;
3471 symlink_names
= set_new(&string_hash_ops
);
3475 if (path_is_absolute(path
)) {
3477 filename
= strdup(path
);
3481 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3493 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3495 /* Instead of opening the path right away, we manually
3496 * follow all symlinks and add their name to our unit
3497 * name set while doing so */
3498 filename
= path_make_absolute(path
, *p
);
3502 if (u
->manager
->unit_path_cache
&&
3503 !set_get(u
->manager
->unit_path_cache
, filename
))
3506 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3515 /* Empty the symlink names for the next run */
3516 set_clear_free(symlink_names
);
3525 /* Hmm, no suitable file found? */
3529 r
= merge_by_names(&merged
, symlink_names
, id
);
3534 u
->load_state
= UNIT_MERGED
;
3538 if (fstat(fileno(f
), &st
) < 0)
3541 if (null_or_empty(&st
))
3542 u
->load_state
= UNIT_MASKED
;
3544 u
->load_state
= UNIT_LOADED
;
3546 /* Now, parse the file contents */
3547 r
= config_parse(u
->id
, filename
, f
,
3548 UNIT_VTABLE(u
)->sections
,
3549 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3550 false, true, false, u
);
3555 free(u
->fragment_path
);
3556 u
->fragment_path
= filename
;
3559 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3561 if (u
->source_path
) {
3562 if (stat(u
->source_path
, &st
) >= 0)
3563 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3565 u
->source_mtime
= 0;
3571 int unit_load_fragment(Unit
*u
) {
3577 assert(u
->load_state
== UNIT_STUB
);
3580 /* First, try to find the unit under its id. We always look
3581 * for unit files in the default directories, to make it easy
3582 * to override things by placing things in /etc/systemd/system */
3583 r
= load_from_path(u
, u
->id
);
3587 /* Try to find an alias we can load this with */
3588 if (u
->load_state
== UNIT_STUB
)
3589 SET_FOREACH(t
, u
->names
, i
) {
3594 r
= load_from_path(u
, t
);
3598 if (u
->load_state
!= UNIT_STUB
)
3602 /* And now, try looking for it under the suggested (originally linked) path */
3603 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3605 r
= load_from_path(u
, u
->fragment_path
);
3609 if (u
->load_state
== UNIT_STUB
) {
3610 /* Hmm, this didn't work? Then let's get rid
3611 * of the fragment path stored for us, so that
3612 * we don't point to an invalid location. */
3613 free(u
->fragment_path
);
3614 u
->fragment_path
= NULL
;
3618 /* Look for a template */
3619 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3620 _cleanup_free_
char *k
;
3622 k
= unit_name_template(u
->id
);
3626 r
= load_from_path(u
, k
);
3630 if (u
->load_state
== UNIT_STUB
)
3631 SET_FOREACH(t
, u
->names
, i
) {
3632 _cleanup_free_
char *z
= NULL
;
3637 z
= unit_name_template(t
);
3641 r
= load_from_path(u
, z
);
3645 if (u
->load_state
!= UNIT_STUB
)
3653 void unit_dump_config_items(FILE *f
) {
3654 static const struct {
3655 const ConfigParserCallback callback
;
3658 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3659 { config_parse_warn_compat
, "NOTSUPPORTED" },
3661 { config_parse_int
, "INTEGER" },
3662 { config_parse_unsigned
, "UNSIGNED" },
3663 { config_parse_iec_size
, "SIZE" },
3664 { config_parse_iec_off
, "SIZE" },
3665 { config_parse_si_size
, "SIZE" },
3666 { config_parse_bool
, "BOOLEAN" },
3667 { config_parse_string
, "STRING" },
3668 { config_parse_path
, "PATH" },
3669 { config_parse_unit_path_printf
, "PATH" },
3670 { config_parse_strv
, "STRING [...]" },
3671 { config_parse_exec_nice
, "NICE" },
3672 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3673 { config_parse_exec_io_class
, "IOCLASS" },
3674 { config_parse_exec_io_priority
, "IOPRIORITY" },
3675 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3676 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3677 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3678 { config_parse_mode
, "MODE" },
3679 { config_parse_unit_env_file
, "FILE" },
3680 { config_parse_output
, "OUTPUT" },
3681 { config_parse_input
, "INPUT" },
3682 { config_parse_log_facility
, "FACILITY" },
3683 { config_parse_log_level
, "LEVEL" },
3684 { config_parse_exec_capabilities
, "CAPABILITIES" },
3685 { config_parse_exec_secure_bits
, "SECUREBITS" },
3686 { config_parse_bounding_set
, "BOUNDINGSET" },
3687 { config_parse_limit
, "LIMIT" },
3688 { config_parse_unit_deps
, "UNIT [...]" },
3689 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3690 { config_parse_service_type
, "SERVICETYPE" },
3691 { config_parse_service_restart
, "SERVICERESTART" },
3692 #ifdef HAVE_SYSV_COMPAT
3693 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3695 { config_parse_kill_mode
, "KILLMODE" },
3696 { config_parse_kill_signal
, "SIGNAL" },
3697 { config_parse_socket_listen
, "SOCKET [...]" },
3698 { config_parse_socket_bind
, "SOCKETBIND" },
3699 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3700 { config_parse_sec
, "SECONDS" },
3701 { config_parse_nsec
, "NANOSECONDS" },
3702 { config_parse_namespace_path_strv
, "PATH [...]" },
3703 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3704 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3705 { config_parse_unit_string_printf
, "STRING" },
3706 { config_parse_trigger_unit
, "UNIT" },
3707 { config_parse_timer
, "TIMER" },
3708 { config_parse_path_spec
, "PATH" },
3709 { config_parse_notify_access
, "ACCESS" },
3710 { config_parse_ip_tos
, "TOS" },
3711 { config_parse_unit_condition_path
, "CONDITION" },
3712 { config_parse_unit_condition_string
, "CONDITION" },
3713 { config_parse_unit_condition_null
, "CONDITION" },
3714 { config_parse_unit_slice
, "SLICE" },
3715 { config_parse_documentation
, "URL" },
3716 { config_parse_service_timeout
, "SECONDS" },
3717 { config_parse_failure_action
, "ACTION" },
3718 { config_parse_set_status
, "STATUS" },
3719 { config_parse_service_sockets
, "SOCKETS" },
3720 { config_parse_environ
, "ENVIRON" },
3722 { config_parse_syscall_filter
, "SYSCALLS" },
3723 { config_parse_syscall_archs
, "ARCHS" },
3724 { config_parse_syscall_errno
, "ERRNO" },
3725 { config_parse_address_families
, "FAMILIES" },
3727 { config_parse_cpu_shares
, "SHARES" },
3728 { config_parse_memory_limit
, "LIMIT" },
3729 { config_parse_device_allow
, "DEVICE" },
3730 { config_parse_device_policy
, "POLICY" },
3731 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3732 { config_parse_blockio_weight
, "WEIGHT" },
3733 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3734 { config_parse_long
, "LONG" },
3735 { config_parse_socket_service
, "SERVICE" },
3737 { config_parse_exec_selinux_context
, "LABEL" },
3739 { config_parse_job_mode
, "MODE" },
3740 { config_parse_job_mode_isolate
, "BOOLEAN" },
3741 { config_parse_personality
, "PERSONALITY" },
3744 const char *prev
= NULL
;
3749 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3750 const char *rvalue
= "OTHER", *lvalue
;
3754 const ConfigPerfItem
*p
;
3756 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3758 dot
= strchr(i
, '.');
3759 lvalue
= dot
? dot
+ 1 : i
;
3763 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3767 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3770 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3771 if (p
->parse
== table
[j
].callback
) {
3772 rvalue
= table
[j
].rvalue
;
3776 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);