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"
65 #include "seccomp-util.h"
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
);
87 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
88 "Support for option %s= has been removed and it is ignored", lvalue
);
90 case DISABLED_EXPERIMENTAL
:
91 log_syntax(unit
, LOG_INFO
, filename
, line
, EINVAL
,
92 "Support for option %s= has not yet been enabled and it is ignored", lvalue
);
99 int config_parse_unit_deps(const char *unit
,
100 const char *filename
,
103 unsigned section_line
,
110 UnitDependency d
= ltype
;
112 const char *word
, *state
;
119 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
120 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
123 t
= strndup(word
, l
);
127 r
= unit_name_printf(u
, t
, &k
);
129 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
130 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
134 r
= unit_add_dependency_by_name(u
, d
, k
, NULL
, true);
136 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
137 "Failed to add dependency on %s, ignoring: %s", k
, strerror(-r
));
140 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
145 int config_parse_unit_string_printf(const char *unit
,
146 const char *filename
,
149 unsigned section_line
,
157 _cleanup_free_
char *k
= NULL
;
165 r
= unit_full_printf(u
, rvalue
, &k
);
167 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
168 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
170 return config_parse_string(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
171 k
? k
: rvalue
, data
, userdata
);
174 int config_parse_unit_strv_printf(const char *unit
,
175 const char *filename
,
178 unsigned section_line
,
186 _cleanup_free_
char *k
= NULL
;
194 r
= unit_full_printf(u
, rvalue
, &k
);
196 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
197 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
199 return config_parse_strv(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
200 k
? k
: rvalue
, data
, userdata
);
203 int config_parse_unit_path_printf(const char *unit
,
204 const char *filename
,
207 unsigned section_line
,
214 _cleanup_free_
char *k
= NULL
;
223 r
= unit_full_printf(u
, rvalue
, &k
);
225 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
229 return config_parse_path(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
, k
, data
, userdata
);
232 int config_parse_unit_path_strv_printf(
234 const char *filename
,
237 unsigned section_line
,
245 const char *word
, *state
;
255 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
256 _cleanup_free_
char *k
= NULL
;
262 r
= unit_full_printf(u
, t
, &k
);
264 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve unit specifiers on %s, ignoring: %s", t
, strerror(-r
));
268 if (!utf8_is_valid(k
)) {
269 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
273 if (!path_is_absolute(k
)) {
274 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Symlink path %s is not absolute, ignoring: %s", k
, strerror(-r
));
278 path_kill_slashes(k
);
287 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Invalid syntax, ignoring.");
292 int config_parse_socket_listen(const char *unit
,
293 const char *filename
,
296 unsigned section_line
,
303 _cleanup_free_ SocketPort
*p
= NULL
;
315 if (isempty(rvalue
)) {
316 /* An empty assignment removes all ports */
317 socket_free_ports(s
);
321 p
= new0(SocketPort
, 1);
325 if (ltype
!= SOCKET_SOCKET
) {
328 r
= unit_full_printf(UNIT(s
), rvalue
, &p
->path
);
330 p
->path
= strdup(rvalue
);
334 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
335 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
338 path_kill_slashes(p
->path
);
340 } else if (streq(lvalue
, "ListenNetlink")) {
341 _cleanup_free_
char *k
= NULL
;
343 p
->type
= SOCKET_SOCKET
;
344 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
346 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
347 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
349 r
= socket_address_parse_netlink(&p
->address
, k
?: rvalue
);
351 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
352 "Failed to parse address value, ignoring: %s", rvalue
);
357 _cleanup_free_
char *k
= NULL
;
359 p
->type
= SOCKET_SOCKET
;
360 r
= unit_full_printf(UNIT(s
), rvalue
, &k
);
362 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
363 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue
, strerror(-r
));
365 r
= socket_address_parse(&p
->address
, k
? k
: rvalue
);
367 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
368 "Failed to parse address value, ignoring: %s", rvalue
);
372 if (streq(lvalue
, "ListenStream"))
373 p
->address
.type
= SOCK_STREAM
;
374 else if (streq(lvalue
, "ListenDatagram"))
375 p
->address
.type
= SOCK_DGRAM
;
377 assert(streq(lvalue
, "ListenSequentialPacket"));
378 p
->address
.type
= SOCK_SEQPACKET
;
381 if (socket_address_family(&p
->address
) != AF_LOCAL
&& p
->address
.type
== SOCK_SEQPACKET
) {
382 log_syntax(unit
, LOG_ERR
, filename
, line
, ENOTSUP
,
383 "Address family not supported, ignoring: %s", rvalue
);
392 LIST_FIND_TAIL(port
, s
->ports
, tail
);
393 LIST_INSERT_AFTER(port
, s
->ports
, tail
, p
);
395 LIST_PREPEND(port
, s
->ports
, p
);
401 int config_parse_socket_bind(const char *unit
,
402 const char *filename
,
405 unsigned section_line
,
413 SocketAddressBindIPv6Only b
;
422 b
= socket_address_bind_ipv6_only_from_string(rvalue
);
426 r
= parse_boolean(rvalue
);
428 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
429 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue
);
433 s
->bind_ipv6_only
= r
? SOCKET_ADDRESS_IPV6_ONLY
: SOCKET_ADDRESS_BOTH
;
435 s
->bind_ipv6_only
= b
;
440 int config_parse_exec_nice(const char *unit
,
441 const char *filename
,
444 unsigned section_line
,
451 ExecContext
*c
= data
;
459 r
= safe_atoi(rvalue
, &priority
);
461 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
462 "Failed to parse nice priority, ignoring: %s. ", rvalue
);
466 if (priority
< PRIO_MIN
|| priority
>= PRIO_MAX
) {
467 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
468 "Nice priority out of range, ignoring: %s", rvalue
);
478 int config_parse_exec_oom_score_adjust(const char* unit
,
479 const char *filename
,
482 unsigned section_line
,
489 ExecContext
*c
= data
;
497 r
= safe_atoi(rvalue
, &oa
);
499 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
500 "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
, ERANGE
,
506 "OOM score adjust value out of range, ignoring: %s", rvalue
);
510 c
->oom_score_adjust
= oa
;
511 c
->oom_score_adjust_set
= true;
516 int config_parse_exec(const char *unit
,
517 const char *filename
,
520 unsigned section_line
,
527 ExecCommand
**e
= data
, *nce
;
539 if (isempty(rvalue
)) {
540 /* An empty assignment resets the list */
541 exec_command_free_list(*e
);
546 /* We accept an absolute path as first argument, or
547 * alternatively an absolute prefixed with @ to allow
548 * overriding of argv[0]. */
551 const char *word
, *state
;
553 bool honour_argv0
= false, ignore
= false;
559 rvalue
+= strspn(rvalue
, WHITESPACE
);
564 for (i
= 0; i
< 2; i
++) {
565 if (rvalue
[0] == '-' && !ignore
) {
570 if (rvalue
[0] == '@' && !honour_argv0
) {
576 if (*rvalue
!= '/') {
577 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
578 "Executable path is not absolute, ignoring: %s", rvalue
);
583 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
584 if (strneq(word
, ";", MAX(l
, 1U)))
589 if (!isempty(state
)) {
590 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
591 "Trailing garbage, ignoring.");
596 n
= new(char*, k
+ !honour_argv0
);
601 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
602 if (strneq(word
, ";", MAX(l
, 1U)))
604 else if (strneq(word
, "\\;", MAX(l
, 1U))) {
609 if (honour_argv0
&& word
== rvalue
) {
612 path
= strndup(word
, l
);
618 if (!utf8_is_valid(path
)) {
619 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
627 c
= n
[k
++] = cunescape_length(word
, l
);
633 if (!utf8_is_valid(c
)) {
634 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
644 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
645 "Invalid command line, ignoring: %s", rvalue
);
658 assert(path_is_absolute(path
));
660 nce
= new0(ExecCommand
, 1);
668 nce
->ignore
= ignore
;
670 path_kill_slashes(nce
->path
);
672 exec_command_append_list(e
, nce
);
688 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type
, service_type
, ServiceType
, "Failed to parse service type");
689 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart
, service_restart
, ServiceRestart
, "Failed to parse service restart specifier");
691 int config_parse_socket_bindtodevice(const char* unit
,
692 const char *filename
,
695 unsigned section_line
,
710 if (rvalue
[0] && !streq(rvalue
, "*")) {
717 free(s
->bind_to_device
);
718 s
->bind_to_device
= n
;
723 DEFINE_CONFIG_PARSE_ENUM(config_parse_output
, exec_output
, ExecOutput
, "Failed to parse output specifier");
724 DEFINE_CONFIG_PARSE_ENUM(config_parse_input
, exec_input
, ExecInput
, "Failed to parse input specifier");
726 int config_parse_exec_io_class(const char *unit
,
727 const char *filename
,
730 unsigned section_line
,
737 ExecContext
*c
= data
;
745 x
= ioprio_class_from_string(rvalue
);
747 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
748 "Failed to parse IO scheduling class, ignoring: %s", rvalue
);
752 c
->ioprio
= IOPRIO_PRIO_VALUE(x
, IOPRIO_PRIO_DATA(c
->ioprio
));
753 c
->ioprio_set
= true;
758 int config_parse_exec_io_priority(const char *unit
,
759 const char *filename
,
762 unsigned section_line
,
769 ExecContext
*c
= data
;
777 r
= safe_atoi(rvalue
, &i
);
778 if (r
< 0 || i
< 0 || i
>= IOPRIO_BE_NR
) {
779 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
780 "Failed to parse IO priority, ignoring: %s", rvalue
);
784 c
->ioprio
= IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c
->ioprio
), i
);
785 c
->ioprio_set
= true;
790 int config_parse_exec_cpu_sched_policy(const char *unit
,
791 const char *filename
,
794 unsigned section_line
,
802 ExecContext
*c
= data
;
810 x
= sched_policy_from_string(rvalue
);
812 log_syntax(unit
, LOG_ERR
, filename
, line
, -x
,
813 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
817 c
->cpu_sched_policy
= x
;
818 /* Moving to or from real-time policy? We need to adjust the priority */
819 c
->cpu_sched_priority
= CLAMP(c
->cpu_sched_priority
, sched_get_priority_min(x
), sched_get_priority_max(x
));
820 c
->cpu_sched_set
= true;
825 int config_parse_exec_cpu_sched_prio(const char *unit
,
826 const char *filename
,
829 unsigned section_line
,
836 ExecContext
*c
= data
;
844 r
= safe_atoi(rvalue
, &i
);
846 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
847 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue
);
851 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
852 min
= sched_get_priority_min(c
->cpu_sched_policy
);
853 max
= sched_get_priority_max(c
->cpu_sched_policy
);
855 if (i
< min
|| i
> max
) {
856 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
857 "CPU scheduling priority is out of range, ignoring: %s", rvalue
);
861 c
->cpu_sched_priority
= i
;
862 c
->cpu_sched_set
= true;
867 int config_parse_exec_cpu_affinity(const char *unit
,
868 const char *filename
,
871 unsigned section_line
,
878 ExecContext
*c
= data
;
879 const char *word
, *state
;
887 if (isempty(rvalue
)) {
888 /* An empty assignment resets the CPU list */
895 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
896 _cleanup_free_
char *t
= NULL
;
900 t
= strndup(word
, l
);
904 r
= safe_atou(t
, &cpu
);
907 c
->cpuset
= cpu_set_malloc(&c
->cpuset_ncpus
);
912 if (r
< 0 || cpu
>= c
->cpuset_ncpus
) {
913 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
914 "Failed to parse CPU affinity '%s', ignoring: %s", t
, rvalue
);
918 CPU_SET_S(cpu
, CPU_ALLOC_SIZE(c
->cpuset_ncpus
), c
->cpuset
);
921 log_syntax(unit
, LOG_WARNING
, filename
, line
, EINVAL
,
922 "Trailing garbage, ignoring.");
927 int config_parse_exec_capabilities(const char *unit
,
928 const char *filename
,
931 unsigned section_line
,
938 ExecContext
*c
= data
;
946 cap
= cap_from_text(rvalue
);
948 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
,
949 "Failed to parse capabilities, ignoring: %s", rvalue
);
954 cap_free(c
->capabilities
);
955 c
->capabilities
= cap
;
960 int config_parse_exec_secure_bits(const char *unit
,
961 const char *filename
,
964 unsigned section_line
,
971 ExecContext
*c
= data
;
973 const char *word
, *state
;
980 if (isempty(rvalue
)) {
981 /* An empty assignment resets the field */
986 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
987 if (first_word(word
, "keep-caps"))
988 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS
;
989 else if (first_word(word
, "keep-caps-locked"))
990 c
->secure_bits
|= 1<<SECURE_KEEP_CAPS_LOCKED
;
991 else if (first_word(word
, "no-setuid-fixup"))
992 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP
;
993 else if (first_word(word
, "no-setuid-fixup-locked"))
994 c
->secure_bits
|= 1<<SECURE_NO_SETUID_FIXUP_LOCKED
;
995 else if (first_word(word
, "noroot"))
996 c
->secure_bits
|= 1<<SECURE_NOROOT
;
997 else if (first_word(word
, "noroot-locked"))
998 c
->secure_bits
|= 1<<SECURE_NOROOT_LOCKED
;
1000 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1001 "Failed to parse secure bits, ignoring: %s", rvalue
);
1005 if (!isempty(state
))
1006 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1007 "Invalid syntax, garbage at the end, ignoring.");
1012 int config_parse_bounding_set(const char *unit
,
1013 const char *filename
,
1015 const char *section
,
1016 unsigned section_line
,
1023 uint64_t *capability_bounding_set_drop
= data
;
1024 const char *word
, *state
;
1026 bool invert
= false;
1034 if (rvalue
[0] == '~') {
1039 /* Note that we store this inverted internally, since the
1040 * kernel wants it like this. But we actually expose it
1041 * non-inverted everywhere to have a fully normalized
1044 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1045 _cleanup_free_
char *t
= NULL
;
1048 t
= strndup(word
, l
);
1052 cap
= capability_from_name(t
);
1054 log_syntax(unit
, LOG_ERR
, filename
, line
, errno
, "Failed to parse capability in bounding set, ignoring: %s", t
);
1058 sum
|= ((uint64_t) 1ULL) << (uint64_t) cap
;
1060 if (!isempty(state
))
1061 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1062 "Trailing garbage, ignoring.");
1065 *capability_bounding_set_drop
|= sum
;
1067 *capability_bounding_set_drop
|= ~sum
;
1072 int config_parse_limit(const char *unit
,
1073 const char *filename
,
1075 const char *section
,
1076 unsigned section_line
,
1083 struct rlimit
**rl
= data
;
1084 unsigned long long u
;
1093 if (streq(rvalue
, "infinity"))
1094 u
= (unsigned long long) RLIM_INFINITY
;
1098 r
= safe_atollu(rvalue
, &u
);
1100 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1101 "Failed to parse resource value, ignoring: %s", rvalue
);
1107 *rl
= new(struct rlimit
, 1);
1112 (*rl
)->rlim_cur
= (*rl
)->rlim_max
= (rlim_t
) u
;
1116 #ifdef HAVE_SYSV_COMPAT
1117 int config_parse_sysv_priority(const char *unit
,
1118 const char *filename
,
1120 const char *section
,
1121 unsigned section_line
,
1128 int *priority
= data
;
1136 r
= safe_atoi(rvalue
, &i
);
1137 if (r
< 0 || i
< 0) {
1138 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1139 "Failed to parse SysV start priority, ignoring: %s", rvalue
);
1143 *priority
= (int) i
;
1148 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode
, kill_mode
, KillMode
, "Failed to parse kill mode");
1150 int config_parse_kill_signal(const char *unit
,
1151 const char *filename
,
1153 const char *section
,
1154 unsigned section_line
,
1169 r
= signal_from_string_try_harder(rvalue
);
1171 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1172 "Failed to parse kill signal, ignoring: %s", rvalue
);
1180 int config_parse_exec_mount_flags(const char *unit
,
1181 const char *filename
,
1183 const char *section
,
1184 unsigned section_line
,
1191 ExecContext
*c
= data
;
1192 const char *word
, *state
;
1194 unsigned long flags
= 0;
1201 FOREACH_WORD_SEPARATOR(word
, l
, rvalue
, ", ", state
) {
1202 _cleanup_free_
char *t
;
1204 t
= strndup(word
, l
);
1208 if (streq(t
, "shared"))
1210 else if (streq(t
, "slave"))
1212 else if (streq(word
, "private"))
1215 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1216 "Failed to parse mount flag %s, ignoring: %s", t
, rvalue
);
1220 if (!isempty(state
))
1221 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1222 "Trailing garbage, ignoring.");
1224 c
->mount_flags
= flags
;
1228 int config_parse_exec_selinux_context(
1230 const char *filename
,
1232 const char *section
,
1233 unsigned section_line
,
1240 ExecContext
*c
= data
;
1251 if (isempty(rvalue
)) {
1252 free(c
->selinux_context
);
1253 c
->selinux_context
= NULL
;
1254 c
->selinux_context_ignore
= false;
1258 if (rvalue
[0] == '-') {
1264 r
= unit_name_printf(u
, rvalue
, &k
);
1266 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1267 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1271 free(c
->selinux_context
);
1272 c
->selinux_context
= k
;
1273 c
->selinux_context_ignore
= ignore
;
1278 int config_parse_exec_apparmor_profile(
1280 const char *filename
,
1282 const char *section
,
1283 unsigned section_line
,
1290 ExecContext
*c
= data
;
1301 if (isempty(rvalue
)) {
1302 free(c
->apparmor_profile
);
1303 c
->apparmor_profile
= NULL
;
1304 c
->apparmor_profile_ignore
= false;
1308 if (rvalue
[0] == '-') {
1314 r
= unit_name_printf(u
, rvalue
, &k
);
1316 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1317 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1321 free(c
->apparmor_profile
);
1322 c
->apparmor_profile
= k
;
1323 c
->apparmor_profile_ignore
= ignore
;
1328 int config_parse_exec_smack_process_label(
1330 const char *filename
,
1332 const char *section
,
1333 unsigned section_line
,
1340 ExecContext
*c
= data
;
1351 if (isempty(rvalue
)) {
1352 free(c
->smack_process_label
);
1353 c
->smack_process_label
= NULL
;
1354 c
->smack_process_label_ignore
= false;
1358 if (rvalue
[0] == '-') {
1364 r
= unit_name_printf(u
, rvalue
, &k
);
1366 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1367 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1371 free(c
->smack_process_label
);
1372 c
->smack_process_label
= k
;
1373 c
->smack_process_label_ignore
= ignore
;
1378 int config_parse_timer(const char *unit
,
1379 const char *filename
,
1381 const char *section
,
1382 unsigned section_line
,
1393 CalendarSpec
*c
= NULL
;
1400 if (isempty(rvalue
)) {
1401 /* Empty assignment resets list */
1402 timer_free_values(t
);
1406 b
= timer_base_from_string(lvalue
);
1408 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
,
1409 "Failed to parse timer base, ignoring: %s", lvalue
);
1413 if (b
== TIMER_CALENDAR
) {
1414 if (calendar_spec_from_string(rvalue
, &c
) < 0) {
1415 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1416 "Failed to parse calendar specification, ignoring: %s",
1421 if (parse_sec(rvalue
, &u
) < 0) {
1422 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1423 "Failed to parse timer value, ignoring: %s",
1429 v
= new0(TimerValue
, 1);
1431 calendar_spec_free(c
);
1437 v
->calendar_spec
= c
;
1439 LIST_PREPEND(value
, t
->values
, v
);
1444 int config_parse_trigger_unit(
1446 const char *filename
,
1448 const char *section
,
1449 unsigned section_line
,
1456 _cleanup_free_
char *p
= NULL
;
1466 if (!set_isempty(u
->dependencies
[UNIT_TRIGGERS
])) {
1467 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1468 "Multiple units to trigger specified, ignoring: %s", rvalue
);
1472 r
= unit_name_printf(u
, rvalue
, &p
);
1474 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1475 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1477 type
= unit_name_to_type(p
?: rvalue
);
1479 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1480 "Unit type not valid, ignoring: %s", rvalue
);
1484 if (type
== u
->type
) {
1485 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1486 "Trigger cannot be of same type, ignoring: %s", rvalue
);
1490 r
= unit_add_two_dependencies_by_name(u
, UNIT_BEFORE
, UNIT_TRIGGERS
, p
?: rvalue
, NULL
, true);
1492 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1493 "Failed to add trigger on %s, ignoring: %s", p
?: rvalue
, strerror(-r
));
1500 int config_parse_path_spec(const char *unit
,
1501 const char *filename
,
1503 const char *section
,
1504 unsigned section_line
,
1514 _cleanup_free_
char *k
= NULL
;
1522 if (isempty(rvalue
)) {
1523 /* Empty assignment clears list */
1528 b
= path_type_from_string(lvalue
);
1530 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1531 "Failed to parse path type, ignoring: %s", lvalue
);
1535 r
= unit_full_printf(UNIT(p
), rvalue
, &k
);
1541 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1542 "Failed to resolve unit specifiers on %s. Ignoring.",
1546 if (!path_is_absolute(k
)) {
1547 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1548 "Path is not absolute, ignoring: %s", k
);
1552 s
= new0(PathSpec
, 1);
1557 s
->path
= path_kill_slashes(k
);
1562 LIST_PREPEND(spec
, p
->specs
, s
);
1567 int config_parse_socket_service(const char *unit
,
1568 const char *filename
,
1570 const char *section
,
1571 unsigned section_line
,
1578 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1582 _cleanup_free_
char *p
= NULL
;
1589 r
= unit_name_printf(UNIT(s
), rvalue
, &p
);
1591 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1592 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1596 if (!endswith(p
, ".service")) {
1597 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1598 "Unit must be of type service, ignoring: %s", rvalue
);
1602 r
= manager_load_unit(UNIT(s
)->manager
, p
, NULL
, &error
, &x
);
1604 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1605 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1609 unit_ref_set(&s
->service
, x
);
1614 int config_parse_service_sockets(const char *unit
,
1615 const char *filename
,
1617 const char *section
,
1618 unsigned section_line
,
1627 const char *word
, *state
;
1635 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
1636 _cleanup_free_
char *t
= NULL
, *k
= NULL
;
1638 t
= strndup(word
, l
);
1642 r
= unit_name_printf(UNIT(s
), t
, &k
);
1644 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1645 "Failed to resolve specifiers, ignoring: %s", strerror(-r
));
1647 if (!endswith(k
?: t
, ".socket")) {
1648 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1649 "Unit must be of type socket, ignoring: %s", k
?: t
);
1653 r
= unit_add_two_dependencies_by_name(UNIT(s
), UNIT_WANTS
, UNIT_AFTER
, k
?: t
, NULL
, true);
1655 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1656 "Failed to add dependency on %s, ignoring: %s",
1657 k
?: t
, strerror(-r
));
1659 r
= unit_add_dependency_by_name(UNIT(s
), UNIT_TRIGGERED_BY
, k
?: t
, NULL
, true);
1663 if (!isempty(state
))
1664 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1665 "Trailing garbage, ignoring.");
1670 int config_parse_service_timeout(const char *unit
,
1671 const char *filename
,
1673 const char *section
,
1674 unsigned section_line
,
1681 Service
*s
= userdata
;
1689 r
= config_parse_sec(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
1690 rvalue
, data
, userdata
);
1694 if (streq(lvalue
, "TimeoutSec")) {
1695 s
->start_timeout_defined
= true;
1696 s
->timeout_stop_usec
= s
->timeout_start_usec
;
1697 } else if (streq(lvalue
, "TimeoutStartSec"))
1698 s
->start_timeout_defined
= true;
1703 int config_parse_busname_service(
1705 const char *filename
,
1707 const char *section
,
1708 unsigned section_line
,
1715 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1719 _cleanup_free_
char *p
= NULL
;
1726 r
= unit_name_printf(UNIT(n
), rvalue
, &p
);
1728 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1729 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1733 if (!endswith(p
, ".service")) {
1734 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1735 "Unit must be of type service, ignoring: %s", rvalue
);
1739 r
= manager_load_unit(UNIT(n
)->manager
, p
, NULL
, &error
, &x
);
1741 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1742 "Failed to load unit %s, ignoring: %s", rvalue
, bus_error_message(&error
, r
));
1746 unit_ref_set(&n
->service
, x
);
1751 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world
, bus_policy_access
, BusPolicyAccess
, "Failed to parse bus name policy access");
1753 int config_parse_bus_policy(
1755 const char *filename
,
1757 const char *section
,
1758 unsigned section_line
,
1765 _cleanup_free_ BusNamePolicy
*p
= NULL
;
1766 _cleanup_free_
char *id_str
= NULL
;
1767 BusName
*busname
= data
;
1775 p
= new0(BusNamePolicy
, 1);
1779 if (streq(lvalue
, "AllowUser"))
1780 p
->type
= BUSNAME_POLICY_TYPE_USER
;
1781 else if (streq(lvalue
, "AllowGroup"))
1782 p
->type
= BUSNAME_POLICY_TYPE_GROUP
;
1784 assert_not_reached("Unknown lvalue");
1786 id_str
= strdup(rvalue
);
1790 access_str
= strpbrk(id_str
, WHITESPACE
);
1792 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1793 "Invalid busname policy value '%s'", rvalue
);
1799 access_str
+= strspn(access_str
, WHITESPACE
);
1801 p
->access
= bus_policy_access_from_string(access_str
);
1802 if (p
->access
< 0) {
1803 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1804 "Invalid busname policy access type '%s'", access_str
);
1811 LIST_PREPEND(policy
, busname
->policy
, p
);
1817 int config_parse_bus_endpoint_policy(
1819 const char *filename
,
1821 const char *section
,
1822 unsigned section_line
,
1829 _cleanup_free_
char *name
= NULL
;
1830 BusPolicyAccess access
;
1831 ExecContext
*c
= data
;
1840 name
= strdup(rvalue
);
1844 access_str
= strpbrk(name
, WHITESPACE
);
1846 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1847 "Invalid endpoint policy value '%s'", rvalue
);
1853 access_str
+= strspn(access_str
, WHITESPACE
);
1855 access
= bus_policy_access_from_string(access_str
);
1856 if (access
<= _BUS_POLICY_ACCESS_INVALID
||
1857 access
>= _BUS_POLICY_ACCESS_MAX
) {
1858 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1859 "Invalid endpoint policy access type '%s'", access_str
);
1863 if (!c
->bus_endpoint
) {
1864 r
= bus_endpoint_new(&c
->bus_endpoint
);
1870 return bus_endpoint_add_policy(c
->bus_endpoint
, name
, access
);
1873 int config_parse_unit_env_file(const char *unit
,
1874 const char *filename
,
1876 const char *section
,
1877 unsigned section_line
,
1886 _cleanup_free_
char *n
= NULL
;
1895 if (isempty(rvalue
)) {
1896 /* Empty assignment frees the list */
1902 r
= unit_full_printf(u
, rvalue
, &n
);
1904 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1905 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1908 if (!path_is_absolute(s
[0] == '-' ? s
+ 1 : s
)) {
1909 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1910 "Path '%s' is not absolute, ignoring.", s
);
1914 r
= strv_extend(env
, s
);
1921 int config_parse_environ(const char *unit
,
1922 const char *filename
,
1924 const char *section
,
1925 unsigned section_line
,
1934 const char *word
, *state
;
1936 _cleanup_free_
char *k
= NULL
;
1944 if (isempty(rvalue
)) {
1945 /* Empty assignment resets the list */
1952 r
= unit_full_printf(u
, rvalue
, &k
);
1954 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
1955 "Failed to resolve specifiers, ignoring: %s", rvalue
);
1963 FOREACH_WORD_QUOTED(word
, l
, k
, state
) {
1964 _cleanup_free_
char *n
;
1967 n
= cunescape_length(word
, l
);
1971 if (!env_assignment_is_valid(n
)) {
1972 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1973 "Invalid environment assignment, ignoring: %s", rvalue
);
1977 x
= strv_env_set(*env
, n
);
1984 if (!isempty(state
))
1985 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
1986 "Trailing garbage, ignoring.");
1991 int config_parse_ip_tos(const char *unit
,
1992 const char *filename
,
1994 const char *section
,
1995 unsigned section_line
,
2002 int *ip_tos
= data
, x
;
2009 x
= ip_tos_from_string(rvalue
);
2011 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2012 "Failed to parse IP TOS value, ignoring: %s", rvalue
);
2020 int config_parse_unit_condition_path(
2022 const char *filename
,
2024 const char *section
,
2025 unsigned section_line
,
2032 _cleanup_free_
char *p
= NULL
;
2033 Condition
**list
= data
, *c
;
2034 ConditionType t
= ltype
;
2035 bool trigger
, negate
;
2044 if (isempty(rvalue
)) {
2045 /* Empty assignment resets the list */
2046 condition_free_list(*list
);
2051 trigger
= rvalue
[0] == '|';
2055 negate
= rvalue
[0] == '!';
2059 r
= unit_full_printf(u
, rvalue
, &p
);
2061 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2065 if (!path_is_absolute(p
)) {
2066 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
, "Path in condition not absolute, ignoring: %s", p
);
2070 c
= condition_new(t
, p
, trigger
, negate
);
2074 LIST_PREPEND(conditions
, *list
, c
);
2078 int config_parse_unit_condition_string(
2080 const char *filename
,
2082 const char *section
,
2083 unsigned section_line
,
2090 _cleanup_free_
char *s
= NULL
;
2091 Condition
**list
= data
, *c
;
2092 ConditionType t
= ltype
;
2093 bool trigger
, negate
;
2102 if (isempty(rvalue
)) {
2103 /* Empty assignment resets the list */
2104 condition_free_list(*list
);
2109 trigger
= rvalue
[0] == '|';
2113 negate
= rvalue
[0] == '!';
2117 r
= unit_full_printf(u
, rvalue
, &s
);
2119 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
, "Failed to resolve specifiers, ignoring: %s", rvalue
);
2123 c
= condition_new(t
, s
, trigger
, negate
);
2127 LIST_PREPEND(conditions
, *list
, c
);
2131 int config_parse_unit_condition_null(
2133 const char *filename
,
2135 const char *section
,
2136 unsigned section_line
,
2143 Condition
**list
= data
, *c
;
2144 bool trigger
, negate
;
2152 if (isempty(rvalue
)) {
2153 /* Empty assignment resets the list */
2154 condition_free_list(*list
);
2159 trigger
= rvalue
[0] == '|';
2163 negate
= rvalue
[0] == '!';
2167 b
= parse_boolean(rvalue
);
2169 log_syntax(unit
, LOG_ERR
, filename
, line
, -b
, "Failed to parse boolean value in condition, ignoring: %s", rvalue
);
2176 c
= condition_new(CONDITION_NULL
, NULL
, trigger
, negate
);
2180 LIST_PREPEND(conditions
, *list
, c
);
2184 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access
, notify_access
, NotifyAccess
, "Failed to parse notify access specifier");
2185 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action
, failure_action
, FailureAction
, "Failed to parse failure action specifier");
2187 int config_parse_unit_requires_mounts_for(
2189 const char *filename
,
2191 const char *section
,
2192 unsigned section_line
,
2200 const char *word
, *state
;
2208 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2210 _cleanup_free_
char *n
;
2212 n
= strndup(word
, l
);
2216 if (!utf8_is_valid(n
)) {
2217 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
2221 r
= unit_require_mounts_for(u
, n
);
2223 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2224 "Failed to add required mount for, ignoring: %s", rvalue
);
2228 if (!isempty(state
))
2229 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2230 "Trailing garbage, ignoring.");
2235 int config_parse_documentation(const char *unit
,
2236 const char *filename
,
2238 const char *section
,
2239 unsigned section_line
,
2255 if (isempty(rvalue
)) {
2256 /* Empty assignment resets the list */
2257 strv_free(u
->documentation
);
2258 u
->documentation
= NULL
;
2262 r
= config_parse_unit_strv_printf(unit
, filename
, line
, section
, section_line
, lvalue
, ltype
,
2263 rvalue
, data
, userdata
);
2267 for (a
= b
= u
->documentation
; a
&& *a
; a
++) {
2269 if (is_valid_documentation_url(*a
))
2272 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2273 "Invalid URL, ignoring: %s", *a
);
2284 int config_parse_syscall_filter(
2286 const char *filename
,
2288 const char *section
,
2289 unsigned section_line
,
2296 static const char default_syscalls
[] =
2303 ExecContext
*c
= data
;
2305 bool invert
= false;
2306 const char *word
, *state
;
2315 if (isempty(rvalue
)) {
2316 /* Empty assignment resets the list */
2317 set_free(c
->syscall_filter
);
2318 c
->syscall_filter
= NULL
;
2319 c
->syscall_whitelist
= false;
2323 if (rvalue
[0] == '~') {
2328 if (!c
->syscall_filter
) {
2329 c
->syscall_filter
= set_new(NULL
);
2330 if (!c
->syscall_filter
)
2334 /* Allow everything but the ones listed */
2335 c
->syscall_whitelist
= false;
2339 /* Allow nothing but the ones listed */
2340 c
->syscall_whitelist
= true;
2342 /* Accept default syscalls if we are on a whitelist */
2343 NULSTR_FOREACH(i
, default_syscalls
) {
2346 id
= seccomp_syscall_resolve_name(i
);
2350 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2359 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2360 _cleanup_free_
char *t
= NULL
;
2363 t
= strndup(word
, l
);
2367 id
= seccomp_syscall_resolve_name(t
);
2369 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2370 "Failed to parse system call, ignoring: %s", t
);
2374 /* If we previously wanted to forbid a syscall and now
2375 * we want to allow it, then remove it from the list
2377 if (!invert
== c
->syscall_whitelist
) {
2378 r
= set_put(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2384 set_remove(c
->syscall_filter
, INT_TO_PTR(id
+ 1));
2386 if (!isempty(state
))
2387 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2388 "Trailing garbage, ignoring.");
2390 /* Turn on NNP, but only if it wasn't configured explicitly
2391 * before, and only if we are in user mode. */
2392 if (!c
->no_new_privileges_set
&& u
->manager
->running_as
== SYSTEMD_USER
)
2393 c
->no_new_privileges
= true;
2398 int config_parse_syscall_archs(
2400 const char *filename
,
2402 const char *section
,
2403 unsigned section_line
,
2411 const char *word
, *state
;
2415 if (isempty(rvalue
)) {
2421 r
= set_ensure_allocated(archs
, NULL
);
2425 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2426 _cleanup_free_
char *t
= NULL
;
2429 t
= strndup(word
, l
);
2433 r
= seccomp_arch_from_string(t
, &a
);
2435 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2436 "Failed to parse system call architecture, ignoring: %s", t
);
2440 r
= set_put(*archs
, UINT32_TO_PTR(a
+ 1));
2446 if (!isempty(state
))
2447 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2448 "Trailing garbage, ignoring.");
2453 int config_parse_syscall_errno(
2455 const char *filename
,
2457 const char *section
,
2458 unsigned section_line
,
2465 ExecContext
*c
= data
;
2472 if (isempty(rvalue
)) {
2473 /* Empty assignment resets to KILL */
2474 c
->syscall_errno
= 0;
2478 e
= errno_from_name(rvalue
);
2480 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2481 "Failed to parse error number, ignoring: %s", rvalue
);
2485 c
->syscall_errno
= e
;
2489 int config_parse_address_families(
2491 const char *filename
,
2493 const char *section
,
2494 unsigned section_line
,
2501 ExecContext
*c
= data
;
2502 bool invert
= false;
2503 const char *word
, *state
;
2511 if (isempty(rvalue
)) {
2512 /* Empty assignment resets the list */
2513 set_free(c
->address_families
);
2514 c
->address_families
= NULL
;
2515 c
->address_families_whitelist
= false;
2519 if (rvalue
[0] == '~') {
2524 if (!c
->address_families
) {
2525 c
->address_families
= set_new(NULL
);
2526 if (!c
->address_families
)
2529 c
->address_families_whitelist
= !invert
;
2532 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
2533 _cleanup_free_
char *t
= NULL
;
2536 t
= strndup(word
, l
);
2540 af
= af_from_name(t
);
2542 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2543 "Failed to parse address family, ignoring: %s", t
);
2547 /* If we previously wanted to forbid an address family and now
2548 * we want to allow it, then remove it from the list
2550 if (!invert
== c
->address_families_whitelist
) {
2551 r
= set_put(c
->address_families
, INT_TO_PTR(af
));
2557 set_remove(c
->address_families
, INT_TO_PTR(af
));
2559 if (!isempty(state
))
2560 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2561 "Trailing garbage, ignoring.");
2567 int config_parse_unit_slice(
2569 const char *filename
,
2571 const char *section
,
2572 unsigned section_line
,
2579 _cleanup_free_
char *k
= NULL
;
2580 Unit
*u
= userdata
, *slice
;
2588 r
= unit_name_printf(u
, rvalue
, &k
);
2590 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2591 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue
);
2598 r
= manager_load_unit(u
->manager
, k
, NULL
, NULL
, &slice
);
2600 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
2601 "Failed to load slice unit %s. Ignoring.", k
);
2605 if (slice
->type
!= UNIT_SLICE
) {
2606 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2607 "Slice unit %s is not a slice. Ignoring.", k
);
2611 unit_ref_set(&u
->slice
, slice
);
2615 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy
, cgroup_device_policy
, CGroupDevicePolicy
, "Failed to parse device policy");
2617 int config_parse_cpu_shares(
2619 const char *filename
,
2621 const char *section
,
2622 unsigned section_line
,
2629 unsigned long *shares
= data
, lu
;
2636 if (isempty(rvalue
)) {
2637 *shares
= (unsigned long) -1;
2641 r
= safe_atolu(rvalue
, &lu
);
2642 if (r
< 0 || lu
<= 0) {
2643 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2644 "CPU shares '%s' invalid. Ignoring.", rvalue
);
2652 int config_parse_cpu_quota(
2654 const char *filename
,
2656 const char *section
,
2657 unsigned section_line
,
2664 CGroupContext
*c
= data
;
2671 if (isempty(rvalue
)) {
2672 c
->cpu_quota_per_sec_usec
= USEC_INFINITY
;
2676 if (!endswith(rvalue
, "%")) {
2678 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2679 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue
);
2683 if (sscanf(rvalue
, "%lf%%", &percent
) != 1 || percent
<= 0) {
2684 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2685 "CPU quota '%s' invalid. Ignoring.", rvalue
);
2689 c
->cpu_quota_per_sec_usec
= (usec_t
) (percent
* USEC_PER_SEC
/ 100);
2694 int config_parse_memory_limit(
2696 const char *filename
,
2698 const char *section
,
2699 unsigned section_line
,
2706 CGroupContext
*c
= data
;
2710 if (isempty(rvalue
)) {
2711 c
->memory_limit
= (uint64_t) -1;
2715 assert_cc(sizeof(uint64_t) == sizeof(off_t
));
2717 r
= parse_size(rvalue
, 1024, &bytes
);
2719 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2720 "Memory limit '%s' invalid. Ignoring.", rvalue
);
2724 c
->memory_limit
= (uint64_t) bytes
;
2728 int config_parse_device_allow(
2730 const char *filename
,
2732 const char *section
,
2733 unsigned section_line
,
2740 _cleanup_free_
char *path
= NULL
;
2741 CGroupContext
*c
= data
;
2742 CGroupDeviceAllow
*a
;
2746 if (isempty(rvalue
)) {
2747 while (c
->device_allow
)
2748 cgroup_context_free_device_allow(c
, c
->device_allow
);
2753 n
= strcspn(rvalue
, WHITESPACE
);
2754 path
= strndup(rvalue
, n
);
2758 if (!startswith(path
, "/dev/") &&
2759 !startswith(path
, "block-") &&
2760 !startswith(path
, "char-")) {
2761 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2762 "Invalid device node path '%s'. Ignoring.", path
);
2766 m
= rvalue
+ n
+ strspn(rvalue
+ n
, WHITESPACE
);
2770 if (!in_charset(m
, "rwm")) {
2771 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2772 "Invalid device rights '%s'. Ignoring.", m
);
2776 a
= new0(CGroupDeviceAllow
, 1);
2782 a
->r
= !!strchr(m
, 'r');
2783 a
->w
= !!strchr(m
, 'w');
2784 a
->m
= !!strchr(m
, 'm');
2786 LIST_PREPEND(device_allow
, c
->device_allow
, a
);
2790 int config_parse_blockio_weight(
2792 const char *filename
,
2794 const char *section
,
2795 unsigned section_line
,
2802 unsigned long *weight
= data
, lu
;
2809 if (isempty(rvalue
)) {
2810 *weight
= (unsigned long) -1;
2814 r
= safe_atolu(rvalue
, &lu
);
2815 if (r
< 0 || lu
< 10 || lu
> 1000) {
2816 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2817 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2825 int config_parse_blockio_device_weight(
2827 const char *filename
,
2829 const char *section
,
2830 unsigned section_line
,
2837 _cleanup_free_
char *path
= NULL
;
2838 CGroupBlockIODeviceWeight
*w
;
2839 CGroupContext
*c
= data
;
2849 if (isempty(rvalue
)) {
2850 while (c
->blockio_device_weights
)
2851 cgroup_context_free_blockio_device_weight(c
, c
->blockio_device_weights
);
2856 n
= strcspn(rvalue
, WHITESPACE
);
2857 weight
= rvalue
+ n
;
2859 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2860 "Expected block device and device weight. Ignoring.");
2864 path
= strndup(rvalue
, n
);
2868 if (!path_startswith(path
, "/dev")) {
2869 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2870 "Invalid device node path '%s'. Ignoring.", path
);
2874 weight
+= strspn(weight
, WHITESPACE
);
2875 r
= safe_atolu(weight
, &lu
);
2876 if (r
< 0 || lu
< 10 || lu
> 1000) {
2877 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2878 "Block IO weight '%s' invalid. Ignoring.", rvalue
);
2882 w
= new0(CGroupBlockIODeviceWeight
, 1);
2891 LIST_PREPEND(device_weights
, c
->blockio_device_weights
, w
);
2895 int config_parse_blockio_bandwidth(
2897 const char *filename
,
2899 const char *section
,
2900 unsigned section_line
,
2907 _cleanup_free_
char *path
= NULL
;
2908 CGroupBlockIODeviceBandwidth
*b
;
2909 CGroupContext
*c
= data
;
2910 const char *bandwidth
;
2920 read
= streq("BlockIOReadBandwidth", lvalue
);
2922 if (isempty(rvalue
)) {
2923 CGroupBlockIODeviceBandwidth
*next
;
2925 LIST_FOREACH_SAFE (device_bandwidths
, b
, next
, c
->blockio_device_bandwidths
)
2926 if (b
->read
== read
)
2927 cgroup_context_free_blockio_device_bandwidth(c
, b
);
2932 n
= strcspn(rvalue
, WHITESPACE
);
2933 bandwidth
= rvalue
+ n
;
2934 bandwidth
+= strspn(bandwidth
, WHITESPACE
);
2937 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2938 "Expected space separated pair of device node and bandwidth. Ignoring.");
2942 path
= strndup(rvalue
, n
);
2946 if (!path_startswith(path
, "/dev")) {
2947 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2948 "Invalid device node path '%s'. Ignoring.", path
);
2952 r
= parse_size(bandwidth
, 1000, &bytes
);
2953 if (r
< 0 || bytes
<= 0) {
2954 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2955 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue
);
2959 b
= new0(CGroupBlockIODeviceBandwidth
, 1);
2965 b
->bandwidth
= (uint64_t) bytes
;
2968 LIST_PREPEND(device_bandwidths
, c
->blockio_device_bandwidths
, b
);
2973 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode
, job_mode
, JobMode
, "Failed to parse job mode");
2975 int config_parse_job_mode_isolate(
2977 const char *filename
,
2979 const char *section
,
2980 unsigned section_line
,
2994 r
= parse_boolean(rvalue
);
2996 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
2997 "Failed to parse boolean, ignoring: %s", rvalue
);
3001 *m
= r
? JOB_ISOLATE
: JOB_REPLACE
;
3005 int config_parse_personality(
3007 const char *filename
,
3009 const char *section
,
3010 unsigned section_line
,
3017 unsigned long *personality
= data
, p
;
3022 assert(personality
);
3024 p
= personality_from_string(rvalue
);
3025 if (p
== 0xffffffffUL
) {
3026 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3027 "Failed to parse personality, ignoring: %s", rvalue
);
3035 int config_parse_runtime_directory(
3037 const char *filename
,
3039 const char *section
,
3040 unsigned section_line
,
3048 const char *word
, *state
;
3057 if (isempty(rvalue
)) {
3058 /* Empty assignment resets the list */
3064 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3065 _cleanup_free_
char *n
;
3067 n
= strndup(word
, l
);
3071 if (!filename_is_valid(n
)) {
3072 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3073 "Runtime directory is not valid, ignoring assignment: %s", rvalue
);
3077 r
= strv_push(rt
, n
);
3083 if (!isempty(state
))
3084 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3085 "Trailing garbage, ignoring.");
3090 int config_parse_set_status(
3092 const char *filename
,
3094 const char *section
,
3095 unsigned section_line
,
3103 const char *word
, *state
;
3105 ExitStatusSet
*status_set
= data
;
3112 /* Empty assignment resets the list */
3113 if (isempty(rvalue
)) {
3114 exit_status_set_free(status_set
);
3118 FOREACH_WORD(word
, l
, rvalue
, state
) {
3119 _cleanup_free_
char *temp
;
3122 temp
= strndup(word
, l
);
3126 r
= safe_atoi(temp
, &val
);
3128 val
= signal_from_string_try_harder(temp
);
3131 log_syntax(unit
, LOG_ERR
, filename
, line
, -val
,
3132 "Failed to parse value, ignoring: %s", word
);
3136 if (val
< 0 || val
> 255) {
3137 log_syntax(unit
, LOG_ERR
, filename
, line
, ERANGE
,
3138 "Value %d is outside range 0-255, ignoring", val
);
3143 r
= set_ensure_allocated(&status_set
->status
, NULL
);
3147 r
= set_put(status_set
->status
, INT_TO_PTR(val
));
3149 log_syntax(unit
, LOG_ERR
, filename
, line
, -r
,
3150 "Unable to store: %s", word
);
3154 if (!isempty(state
))
3155 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3156 "Trailing garbage, ignoring.");
3161 int config_parse_namespace_path_strv(
3163 const char *filename
,
3165 const char *section
,
3166 unsigned section_line
,
3174 const char *word
, *state
;
3183 if (isempty(rvalue
)) {
3184 /* Empty assignment resets the list */
3190 FOREACH_WORD_QUOTED(word
, l
, rvalue
, state
) {
3191 _cleanup_free_
char *n
;
3194 n
= strndup(word
, l
);
3198 if (!utf8_is_valid(n
)) {
3199 log_invalid_utf8(unit
, LOG_ERR
, filename
, line
, EINVAL
, rvalue
);
3203 offset
= n
[0] == '-';
3204 if (!path_is_absolute(n
+ offset
)) {
3205 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3206 "Not an absolute path, ignoring: %s", rvalue
);
3210 path_kill_slashes(n
);
3212 r
= strv_push(sv
, n
);
3218 if (!isempty(state
))
3219 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
3220 "Trailing garbage, ignoring.");
3225 int config_parse_no_new_privileges(
3227 const char *filename
,
3229 const char *section
,
3230 unsigned section_line
,
3237 ExecContext
*c
= data
;
3245 k
= parse_boolean(rvalue
);
3247 log_syntax(unit
, LOG_ERR
, filename
, line
, -k
,
3248 "Failed to parse boolean value, ignoring: %s", rvalue
);
3252 c
->no_new_privileges
= !!k
;
3253 c
->no_new_privileges_set
= true;
3258 int config_parse_protect_home(
3260 const char *filename
,
3262 const char *section
,
3263 unsigned section_line
,
3270 ExecContext
*c
= data
;
3278 /* Our enum shall be a superset of booleans, hence first try
3279 * to parse as as boolean, and then as enum */
3281 k
= parse_boolean(rvalue
);
3283 c
->protect_home
= PROTECT_HOME_YES
;
3285 c
->protect_home
= PROTECT_HOME_NO
;
3289 h
= protect_home_from_string(rvalue
);
3291 log_syntax(unit
, LOG_ERR
, filename
, line
, -h
,
3292 "Failed to parse protect home value, ignoring: %s", rvalue
);
3296 c
->protect_home
= h
;
3302 int config_parse_protect_system(
3304 const char *filename
,
3306 const char *section
,
3307 unsigned section_line
,
3314 ExecContext
*c
= data
;
3322 /* Our enum shall be a superset of booleans, hence first try
3323 * to parse as as boolean, and then as enum */
3325 k
= parse_boolean(rvalue
);
3327 c
->protect_system
= PROTECT_SYSTEM_YES
;
3329 c
->protect_system
= PROTECT_SYSTEM_NO
;
3333 s
= protect_system_from_string(rvalue
);
3335 log_syntax(unit
, LOG_ERR
, filename
, line
, -s
,
3336 "Failed to parse protect system value, ignoring: %s", rvalue
);
3340 c
->protect_system
= s
;
3346 #define FOLLOW_MAX 8
3348 static int open_follow(char **filename
, FILE **_f
, Set
*names
, char **_final
) {
3359 /* This will update the filename pointer if the loaded file is
3360 * reached by a symlink. The old string will be freed. */
3363 char *target
, *name
;
3365 if (c
++ >= FOLLOW_MAX
)
3368 path_kill_slashes(*filename
);
3370 /* Add the file name we are currently looking at to
3371 * the names of this unit, but only if it is a valid
3373 name
= basename(*filename
);
3375 if (unit_name_is_valid(name
, TEMPLATE_VALID
)) {
3377 id
= set_get(names
, name
);
3383 r
= set_consume(names
, id
);
3389 /* Try to open the file name, but don't if its a symlink */
3390 fd
= open(*filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
3397 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3398 r
= readlink_and_make_absolute(*filename
, &target
);
3406 f
= fdopen(fd
, "re");
3418 static int merge_by_names(Unit
**u
, Set
*names
, const char *id
) {
3426 /* Let's try to add in all symlink names we found */
3427 while ((k
= set_steal_first(names
))) {
3429 /* First try to merge in the other name into our
3431 r
= unit_merge_by_name(*u
, k
);
3435 /* Hmm, we couldn't merge the other unit into
3436 * ours? Then let's try it the other way
3439 other
= manager_get_unit((*u
)->manager
, k
);
3443 r
= unit_merge(other
, *u
);
3446 return merge_by_names(u
, names
, NULL
);
3454 unit_choose_id(*u
, id
);
3462 static int load_from_path(Unit
*u
, const char *path
) {
3464 _cleanup_set_free_free_ Set
*symlink_names
= NULL
;
3465 _cleanup_fclose_
FILE *f
= NULL
;
3466 _cleanup_free_
char *filename
= NULL
;
3474 symlink_names
= set_new(&string_hash_ops
);
3478 if (path_is_absolute(path
)) {
3480 filename
= strdup(path
);
3484 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3496 STRV_FOREACH(p
, u
->manager
->lookup_paths
.unit_path
) {
3498 /* Instead of opening the path right away, we manually
3499 * follow all symlinks and add their name to our unit
3500 * name set while doing so */
3501 filename
= path_make_absolute(path
, *p
);
3505 if (u
->manager
->unit_path_cache
&&
3506 !set_get(u
->manager
->unit_path_cache
, filename
))
3509 r
= open_follow(&filename
, &f
, symlink_names
, &id
);
3518 /* Empty the symlink names for the next run */
3519 set_clear_free(symlink_names
);
3528 /* Hmm, no suitable file found? */
3532 r
= merge_by_names(&merged
, symlink_names
, id
);
3537 u
->load_state
= UNIT_MERGED
;
3541 if (fstat(fileno(f
), &st
) < 0)
3544 if (null_or_empty(&st
))
3545 u
->load_state
= UNIT_MASKED
;
3547 u
->load_state
= UNIT_LOADED
;
3549 /* Now, parse the file contents */
3550 r
= config_parse(u
->id
, filename
, f
,
3551 UNIT_VTABLE(u
)->sections
,
3552 config_item_perf_lookup
, load_fragment_gperf_lookup
,
3553 false, true, false, u
);
3558 free(u
->fragment_path
);
3559 u
->fragment_path
= filename
;
3562 u
->fragment_mtime
= timespec_load(&st
.st_mtim
);
3564 if (u
->source_path
) {
3565 if (stat(u
->source_path
, &st
) >= 0)
3566 u
->source_mtime
= timespec_load(&st
.st_mtim
);
3568 u
->source_mtime
= 0;
3574 int unit_load_fragment(Unit
*u
) {
3580 assert(u
->load_state
== UNIT_STUB
);
3583 /* First, try to find the unit under its id. We always look
3584 * for unit files in the default directories, to make it easy
3585 * to override things by placing things in /etc/systemd/system */
3586 r
= load_from_path(u
, u
->id
);
3590 /* Try to find an alias we can load this with */
3591 if (u
->load_state
== UNIT_STUB
) {
3592 SET_FOREACH(t
, u
->names
, i
) {
3597 r
= load_from_path(u
, t
);
3601 if (u
->load_state
!= UNIT_STUB
)
3606 /* And now, try looking for it under the suggested (originally linked) path */
3607 if (u
->load_state
== UNIT_STUB
&& u
->fragment_path
) {
3609 r
= load_from_path(u
, u
->fragment_path
);
3613 if (u
->load_state
== UNIT_STUB
) {
3614 /* Hmm, this didn't work? Then let's get rid
3615 * of the fragment path stored for us, so that
3616 * we don't point to an invalid location. */
3617 free(u
->fragment_path
);
3618 u
->fragment_path
= NULL
;
3622 /* Look for a template */
3623 if (u
->load_state
== UNIT_STUB
&& u
->instance
) {
3624 _cleanup_free_
char *k
;
3626 k
= unit_name_template(u
->id
);
3630 r
= load_from_path(u
, k
);
3634 if (u
->load_state
== UNIT_STUB
) {
3635 SET_FOREACH(t
, u
->names
, i
) {
3636 _cleanup_free_
char *z
= NULL
;
3641 z
= unit_name_template(t
);
3645 r
= load_from_path(u
, z
);
3649 if (u
->load_state
!= UNIT_STUB
)
3658 void unit_dump_config_items(FILE *f
) {
3659 static const struct {
3660 const ConfigParserCallback callback
;
3663 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3664 { config_parse_warn_compat
, "NOTSUPPORTED" },
3666 { config_parse_int
, "INTEGER" },
3667 { config_parse_unsigned
, "UNSIGNED" },
3668 { config_parse_iec_size
, "SIZE" },
3669 { config_parse_iec_off
, "SIZE" },
3670 { config_parse_si_size
, "SIZE" },
3671 { config_parse_bool
, "BOOLEAN" },
3672 { config_parse_string
, "STRING" },
3673 { config_parse_path
, "PATH" },
3674 { config_parse_unit_path_printf
, "PATH" },
3675 { config_parse_strv
, "STRING [...]" },
3676 { config_parse_exec_nice
, "NICE" },
3677 { config_parse_exec_oom_score_adjust
, "OOMSCOREADJUST" },
3678 { config_parse_exec_io_class
, "IOCLASS" },
3679 { config_parse_exec_io_priority
, "IOPRIORITY" },
3680 { config_parse_exec_cpu_sched_policy
, "CPUSCHEDPOLICY" },
3681 { config_parse_exec_cpu_sched_prio
, "CPUSCHEDPRIO" },
3682 { config_parse_exec_cpu_affinity
, "CPUAFFINITY" },
3683 { config_parse_mode
, "MODE" },
3684 { config_parse_unit_env_file
, "FILE" },
3685 { config_parse_output
, "OUTPUT" },
3686 { config_parse_input
, "INPUT" },
3687 { config_parse_log_facility
, "FACILITY" },
3688 { config_parse_log_level
, "LEVEL" },
3689 { config_parse_exec_capabilities
, "CAPABILITIES" },
3690 { config_parse_exec_secure_bits
, "SECUREBITS" },
3691 { config_parse_bounding_set
, "BOUNDINGSET" },
3692 { config_parse_limit
, "LIMIT" },
3693 { config_parse_unit_deps
, "UNIT [...]" },
3694 { config_parse_exec
, "PATH [ARGUMENT [...]]" },
3695 { config_parse_service_type
, "SERVICETYPE" },
3696 { config_parse_service_restart
, "SERVICERESTART" },
3697 #ifdef HAVE_SYSV_COMPAT
3698 { config_parse_sysv_priority
, "SYSVPRIORITY" },
3700 { config_parse_kill_mode
, "KILLMODE" },
3701 { config_parse_kill_signal
, "SIGNAL" },
3702 { config_parse_socket_listen
, "SOCKET [...]" },
3703 { config_parse_socket_bind
, "SOCKETBIND" },
3704 { config_parse_socket_bindtodevice
, "NETWORKINTERFACE" },
3705 { config_parse_sec
, "SECONDS" },
3706 { config_parse_nsec
, "NANOSECONDS" },
3707 { config_parse_namespace_path_strv
, "PATH [...]" },
3708 { config_parse_unit_requires_mounts_for
, "PATH [...]" },
3709 { config_parse_exec_mount_flags
, "MOUNTFLAG [...]" },
3710 { config_parse_unit_string_printf
, "STRING" },
3711 { config_parse_trigger_unit
, "UNIT" },
3712 { config_parse_timer
, "TIMER" },
3713 { config_parse_path_spec
, "PATH" },
3714 { config_parse_notify_access
, "ACCESS" },
3715 { config_parse_ip_tos
, "TOS" },
3716 { config_parse_unit_condition_path
, "CONDITION" },
3717 { config_parse_unit_condition_string
, "CONDITION" },
3718 { config_parse_unit_condition_null
, "CONDITION" },
3719 { config_parse_unit_slice
, "SLICE" },
3720 { config_parse_documentation
, "URL" },
3721 { config_parse_service_timeout
, "SECONDS" },
3722 { config_parse_failure_action
, "ACTION" },
3723 { config_parse_set_status
, "STATUS" },
3724 { config_parse_service_sockets
, "SOCKETS" },
3725 { config_parse_environ
, "ENVIRON" },
3727 { config_parse_syscall_filter
, "SYSCALLS" },
3728 { config_parse_syscall_archs
, "ARCHS" },
3729 { config_parse_syscall_errno
, "ERRNO" },
3730 { config_parse_address_families
, "FAMILIES" },
3732 { config_parse_cpu_shares
, "SHARES" },
3733 { config_parse_memory_limit
, "LIMIT" },
3734 { config_parse_device_allow
, "DEVICE" },
3735 { config_parse_device_policy
, "POLICY" },
3736 { config_parse_blockio_bandwidth
, "BANDWIDTH" },
3737 { config_parse_blockio_weight
, "WEIGHT" },
3738 { config_parse_blockio_device_weight
, "DEVICEWEIGHT" },
3739 { config_parse_long
, "LONG" },
3740 { config_parse_socket_service
, "SERVICE" },
3742 { config_parse_exec_selinux_context
, "LABEL" },
3744 { config_parse_job_mode
, "MODE" },
3745 { config_parse_job_mode_isolate
, "BOOLEAN" },
3746 { config_parse_personality
, "PERSONALITY" },
3749 const char *prev
= NULL
;
3754 NULSTR_FOREACH(i
, load_fragment_gperf_nulstr
) {
3755 const char *rvalue
= "OTHER", *lvalue
;
3759 const ConfigPerfItem
*p
;
3761 assert_se(p
= load_fragment_gperf_lookup(i
, strlen(i
)));
3763 dot
= strchr(i
, '.');
3764 lvalue
= dot
? dot
+ 1 : i
;
3768 if (!prev
|| !strneq(prev
, i
, prefix_len
+1)) {
3772 fprintf(f
, "[%.*s]\n", (int) prefix_len
, i
);
3775 for (j
= 0; j
< ELEMENTSOF(table
); j
++)
3776 if (p
->parse
== table
[j
].callback
) {
3777 rvalue
= table
[j
].rvalue
;
3781 fprintf(f
, "%s=%s\n", lvalue
, rvalue
);