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>
37 #include <systemd/sd-messages.h>
41 #include "conf-parser.h"
42 #include "load-fragment.h"
45 #include "securebits.h"
47 #include "unit-name.h"
48 #include "unit-printf.h"
49 #include "dbus-common.h"
51 #include "path-util.h"
52 #include "syscall-list.h"
56 #ifndef HAVE_SYSV_COMPAT
57 int config_parse_warn_compat(const char *unit,
67 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
68 "Support for option %s= has been disabled at compile time and is ignored",
74 int config_parse_unit_deps(const char* unit,
84 UnitDependency d = ltype;
94 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
95 _cleanup_free_ char *t = NULL, *k = NULL;
102 r = unit_name_printf(u, t, &k);
104 log_syntax(unit, LOG_ERR, filename, line, -r,
105 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
109 r = unit_add_dependency_by_name(u, d, k, NULL, true);
111 log_syntax(unit, LOG_ERR, filename, line, -r,
112 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
118 int config_parse_unit_string_printf(const char *unit,
119 const char *filename,
129 _cleanup_free_ char *k = NULL;
137 r = unit_full_printf(u, rvalue, &k);
139 log_syntax(unit, LOG_ERR, filename, line, -r,
140 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
142 return config_parse_string(unit, filename, line, section, lvalue, ltype,
143 k ? k : rvalue, data, userdata);
146 int config_parse_unit_strv_printf(const char *unit,
147 const char *filename,
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_strv(unit, filename, line, section, lvalue, ltype,
171 k ? k : rvalue, data, userdata);
174 int config_parse_unit_path_printf(const char *unit,
175 const char *filename,
185 _cleanup_free_ char *k = NULL;
193 r = unit_full_printf(u, rvalue, &k);
195 log_syntax(unit, LOG_ERR, filename, line, -r,
196 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
198 return config_parse_path(unit, filename, line, section, lvalue, ltype,
199 k ? k : rvalue, data, userdata);
202 int config_parse_socket_listen(const char *unit,
203 const char *filename,
212 SocketPort *p, *tail;
223 if (isempty(rvalue)) {
224 /* An empty assignment removes all ports */
225 socket_free_ports(s);
229 p = new0(SocketPort, 1);
233 if (ltype != SOCKET_SOCKET) {
236 r = unit_full_printf(UNIT(s), rvalue, &p->path);
238 p->path = strdup(rvalue);
243 log_syntax(unit, LOG_ERR, filename, line, -r,
244 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
247 path_kill_slashes(p->path);
249 } else if (streq(lvalue, "ListenNetlink")) {
250 _cleanup_free_ char *k = NULL;
252 p->type = SOCKET_SOCKET;
253 r = unit_full_printf(UNIT(s), rvalue, &k);
255 log_syntax(unit, LOG_ERR, filename, line, -r,
256 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
258 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
260 log_syntax(unit, LOG_ERR, filename, line, -r,
261 "Failed to parse address value, ignoring: %s", rvalue);
267 _cleanup_free_ char *k = NULL;
269 p->type = SOCKET_SOCKET;
270 r = unit_full_printf(UNIT(s), rvalue, &k);
272 log_syntax(unit, LOG_ERR, filename, line, -r,
273 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
275 r = socket_address_parse(&p->address, k ? k : rvalue);
277 log_syntax(unit, LOG_ERR, filename, line, -r,
278 "Failed to parse address value, ignoring: %s", rvalue);
283 if (streq(lvalue, "ListenStream"))
284 p->address.type = SOCK_STREAM;
285 else if (streq(lvalue, "ListenDatagram"))
286 p->address.type = SOCK_DGRAM;
288 assert(streq(lvalue, "ListenSequentialPacket"));
289 p->address.type = SOCK_SEQPACKET;
292 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
293 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
294 "Address family not supported, ignoring: %s", rvalue);
303 LIST_FIND_TAIL(SocketPort, port, s->ports, tail);
304 LIST_INSERT_AFTER(SocketPort, port, s->ports, tail, p);
306 LIST_PREPEND(SocketPort, port, s->ports, p);
311 int config_parse_socket_bind(const char *unit,
312 const char *filename,
322 SocketAddressBindIPv6Only b;
331 b = socket_address_bind_ipv6_only_from_string(rvalue);
335 r = parse_boolean(rvalue);
337 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
338 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
342 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
344 s->bind_ipv6_only = b;
349 int config_parse_exec_nice(const char *unit,
350 const char *filename,
359 ExecContext *c = data;
367 r = safe_atoi(rvalue, &priority);
369 log_syntax(unit, LOG_ERR, filename, line, -r,
370 "Failed to parse nice priority, ignoring: %s. ", rvalue);
374 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
375 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
376 "Nice priority out of range, ignoring: %s", rvalue);
386 int config_parse_exec_oom_score_adjust(const char* unit,
387 const char *filename,
396 ExecContext *c = data;
404 r = safe_atoi(rvalue, &oa);
406 log_syntax(unit, LOG_ERR, filename, line, -r,
407 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
411 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
412 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
413 "OOM score adjust value out of range, ignoring: %s", rvalue);
417 c->oom_score_adjust = oa;
418 c->oom_score_adjust_set = true;
423 int config_parse_exec(const char *unit,
424 const char *filename,
433 ExecCommand **e = data, *nce;
445 if (isempty(rvalue)) {
446 /* An empty assignment resets the list */
447 exec_command_free_list(*e);
452 /* We accept an absolute path as first argument, or
453 * alternatively an absolute prefixed with @ to allow
454 * overriding of argv[0]. */
460 bool honour_argv0 = false, ignore = false;
466 rvalue += strspn(rvalue, WHITESPACE);
471 for (i = 0; i < 2; i++) {
472 if (rvalue[0] == '-' && !ignore) {
477 if (rvalue[0] == '@' && !honour_argv0) {
483 if (*rvalue != '/') {
484 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
485 "Executable path is not absolute, ignoring: %s", rvalue);
490 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
491 if (strneq(w, ";", MAX(l, 1U)))
497 n = new(char*, k + !honour_argv0);
502 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
503 if (strneq(w, ";", MAX(l, 1U)))
505 else if (strneq(w, "\\;", MAX(l, 1U)))
508 if (honour_argv0 && w == rvalue) {
511 path = strndup(w, l);
517 if (!utf8_is_valid(path)) {
518 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
519 "Path is not UTF-8 clean, ignoring assignment: %s",
528 c = n[k++] = cunescape_length(w, l);
534 if (!utf8_is_valid(c)) {
535 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
536 "Path is not UTF-8 clean, ignoring assignment: %s",
547 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
548 "Invalid command line, ignoring: %s", rvalue);
561 assert(path_is_absolute(path));
563 nce = new0(ExecCommand, 1);
571 nce->ignore = ignore;
573 path_kill_slashes(nce->path);
575 exec_command_append_list(e, nce);
591 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
592 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
594 int config_parse_socket_bindtodevice(const char* unit,
595 const char *filename,
612 if (rvalue[0] && !streq(rvalue, "*")) {
619 free(s->bind_to_device);
620 s->bind_to_device = n;
625 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
626 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
628 int config_parse_exec_io_class(const char *unit,
629 const char *filename,
638 ExecContext *c = data;
646 x = ioprio_class_from_string(rvalue);
648 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
649 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
653 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
654 c->ioprio_set = true;
659 int config_parse_exec_io_priority(const char *unit,
660 const char *filename,
669 ExecContext *c = data;
677 r = safe_atoi(rvalue, &i);
678 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
679 log_syntax(unit, LOG_ERR, filename, line, -r,
680 "Failed to parse IO priority, ignoring: %s", rvalue);
684 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
685 c->ioprio_set = true;
690 int config_parse_exec_cpu_sched_policy(const char *unit,
691 const char *filename,
701 ExecContext *c = data;
709 x = sched_policy_from_string(rvalue);
711 log_syntax(unit, LOG_ERR, filename, line, -x,
712 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
716 c->cpu_sched_policy = x;
717 /* Moving to or from real-time policy? We need to adjust the priority */
718 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
719 c->cpu_sched_set = true;
724 int config_parse_exec_cpu_sched_prio(const char *unit,
725 const char *filename,
734 ExecContext *c = data;
742 r = safe_atoi(rvalue, &i);
744 log_syntax(unit, LOG_ERR, filename, line, -r,
745 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
749 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
750 min = sched_get_priority_min(c->cpu_sched_policy);
751 max = sched_get_priority_max(c->cpu_sched_policy);
753 if (i < min || i > max) {
754 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
755 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
759 c->cpu_sched_priority = i;
760 c->cpu_sched_set = true;
765 int config_parse_exec_cpu_affinity(const char *unit,
766 const char *filename,
775 ExecContext *c = data;
785 if (isempty(rvalue)) {
786 /* An empty assignment resets the CPU list */
793 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
794 _cleanup_free_ char *t = NULL;
802 r = safe_atou(t, &cpu);
805 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
810 if (r < 0 || cpu >= c->cpuset_ncpus) {
811 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
812 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
816 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
822 int config_parse_exec_capabilities(const char *unit,
823 const char *filename,
832 ExecContext *c = data;
840 cap = cap_from_text(rvalue);
842 log_syntax(unit, LOG_ERR, filename, line, errno,
843 "Failed to parse capabilities, ignoring: %s", rvalue);
848 cap_free(c->capabilities);
849 c->capabilities = cap;
854 int config_parse_exec_secure_bits(const char *unit,
855 const char *filename,
864 ExecContext *c = data;
874 if (isempty(rvalue)) {
875 /* An empty assignment resets the field */
880 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
881 if (first_word(w, "keep-caps"))
882 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
883 else if (first_word(w, "keep-caps-locked"))
884 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
885 else if (first_word(w, "no-setuid-fixup"))
886 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
887 else if (first_word(w, "no-setuid-fixup-locked"))
888 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
889 else if (first_word(w, "noroot"))
890 c->secure_bits |= 1<<SECURE_NOROOT;
891 else if (first_word(w, "noroot-locked"))
892 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
894 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
895 "Failed to parse secure bits, ignoring: %s", rvalue);
903 int config_parse_bounding_set(const char *unit,
904 const char *filename,
913 uint64_t *capability_bounding_set_drop = data;
925 if (rvalue[0] == '~') {
930 /* Note that we store this inverted internally, since the
931 * kernel wants it like this. But we actually expose it
932 * non-inverted everywhere to have a fully normalized
935 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
936 _cleanup_free_ char *t = NULL;
944 r = cap_from_name(t, &cap);
946 log_syntax(unit, LOG_ERR, filename, line, errno,
947 "Failed to parse capability in bounding set, ignoring: %s", t);
951 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
955 *capability_bounding_set_drop |= sum;
957 *capability_bounding_set_drop |= ~sum;
962 int config_parse_limit(const char *unit,
963 const char *filename,
972 struct rlimit **rl = data;
973 unsigned long long u;
982 if (streq(rvalue, "infinity"))
983 u = (unsigned long long) RLIM_INFINITY;
987 r = safe_atollu(rvalue, &u);
989 log_syntax(unit, LOG_ERR, filename, line, -r,
990 "Failed to parse resource value, ignoring: %s", rvalue);
996 *rl = new(struct rlimit, 1);
1001 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
1005 #ifdef HAVE_SYSV_COMPAT
1006 int config_parse_sysv_priority(const char *unit,
1007 const char *filename,
1009 const char *section,
1016 int *priority = data;
1024 r = safe_atoi(rvalue, &i);
1025 if (r < 0 || i < 0) {
1026 log_syntax(unit, LOG_ERR, filename, line, -r,
1027 "Failed to parse SysV start priority, ignoring: %s", rvalue);
1031 *priority = (int) i;
1036 int config_parse_fsck_passno(const char *unit,
1037 const char *filename,
1039 const char *section,
1054 r = safe_atoi(rvalue, &i);
1056 log_syntax(unit, LOG_ERR, filename, line, -r,
1057 "Failed to parse fsck pass number, ignoring: %s", rvalue);
1065 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1067 int config_parse_kill_signal(const char *unit,
1068 const char *filename,
1070 const char *section,
1085 r = signal_from_string_try_harder(rvalue);
1087 log_syntax(unit, LOG_ERR, filename, line, -r,
1088 "Failed to parse kill signal, ignoring: %s", rvalue);
1096 int config_parse_exec_mount_flags(const char *unit,
1097 const char *filename,
1099 const char *section,
1106 ExecContext *c = data;
1110 unsigned long flags = 0;
1117 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
1118 _cleanup_free_ char *t;
1124 if (streq(t, "shared"))
1126 else if (streq(t, "slave"))
1128 else if (streq(w, "private"))
1129 flags |= MS_PRIVATE;
1131 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1132 "Failed to parse mount flag %s, ignoring: %s",
1138 c->mount_flags = flags;
1142 int config_parse_timer(const char *unit,
1143 const char *filename,
1145 const char *section,
1156 CalendarSpec *c = NULL;
1164 if (isempty(rvalue)) {
1165 /* Empty assignment resets list */
1166 timer_free_values(t);
1170 b = timer_base_from_string(lvalue);
1172 log_syntax(unit, LOG_ERR, filename, line, -b,
1173 "Failed to parse timer base, ignoring: %s", lvalue);
1177 if (b == TIMER_CALENDAR) {
1178 if (calendar_spec_from_string(rvalue, &c) < 0) {
1179 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1180 "Failed to parse calendar specification, ignoring: %s",
1185 id = CLOCK_REALTIME;
1187 if (parse_sec(rvalue, &u) < 0) {
1188 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1189 "Failed to parse timer value, ignoring: %s",
1194 id = CLOCK_MONOTONIC;
1197 v = new0(TimerValue, 1);
1204 v->calendar_spec = c;
1206 LIST_PREPEND(TimerValue, value, t->values, v);
1211 int config_parse_trigger_unit(
1213 const char *filename,
1215 const char *section,
1222 _cleanup_free_ char *p = NULL;
1232 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1233 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1234 "Multiple units to trigger specified, ignoring: %s", rvalue);
1238 r = unit_name_printf(u, rvalue, &p);
1240 log_syntax(unit, LOG_ERR, filename, line, -r,
1241 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1243 type = unit_name_to_type(p ?: rvalue);
1245 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1246 "Unit type not valid, ignoring: %s", rvalue);
1250 if (type == u->type) {
1251 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1252 "Trigger cannot be of same type, ignoring: %s", rvalue);
1256 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
1258 log_syntax(unit, LOG_ERR, filename, line, -r,
1259 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
1266 int config_parse_path_spec(const char *unit,
1267 const char *filename,
1269 const char *section,
1279 _cleanup_free_ char *k = NULL;
1287 if (isempty(rvalue)) {
1288 /* Empty assignment clears list */
1293 b = path_type_from_string(lvalue);
1295 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1296 "Failed to parse path type, ignoring: %s", lvalue);
1300 r = unit_full_printf(UNIT(p), rvalue, &k);
1306 log_syntax(unit, LOG_ERR, filename, line, -r,
1307 "Failed to resolve unit specifiers on %s. Ignoring.",
1311 if (!path_is_absolute(k)) {
1312 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1313 "Path is not absolute, ignoring: %s", k);
1317 s = new0(PathSpec, 1);
1321 s->path = path_kill_slashes(k);
1326 LIST_PREPEND(PathSpec, spec, p->specs, s);
1331 int config_parse_socket_service(const char *unit,
1332 const char *filename,
1334 const char *section,
1345 _cleanup_free_ char *p = NULL;
1352 dbus_error_init(&error);
1354 r = unit_name_printf(UNIT(s), rvalue, &p);
1356 log_syntax(unit, LOG_ERR, filename, line, -r,
1357 "Failed to resolve specifiers, ignoring: %s", rvalue);
1359 if (!endswith(p ?: rvalue, ".service")) {
1360 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1361 "Unit must be of type service, ignoring: %s", rvalue);
1365 r = manager_load_unit(UNIT(s)->manager, p ?: rvalue, NULL, &error, &x);
1367 log_syntax(unit, LOG_ERR, filename, line, r,
1368 "Failed to load unit %s, ignoring: %s",
1369 rvalue, bus_error(&error, r));
1370 dbus_error_free(&error);
1374 unit_ref_set(&s->service, x);
1379 int config_parse_service_sockets(const char *unit,
1380 const char *filename,
1382 const char *section,
1399 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1400 _cleanup_free_ char *t = NULL, *k = NULL;
1406 r = unit_name_printf(UNIT(s), t, &k);
1408 log_syntax(unit, LOG_ERR, filename, line, -r,
1409 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1411 if (!endswith(k ?: t, ".socket")) {
1412 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1413 "Unit must be of type socket, ignoring: %s", k ?: t);
1417 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
1419 log_syntax(unit, LOG_ERR, filename, line, -r,
1420 "Failed to add dependency on %s, ignoring: %s",
1421 k ?: t, strerror(-r));
1423 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
1431 int config_parse_service_timeout(const char *unit,
1432 const char *filename,
1434 const char *section,
1441 Service *s = userdata;
1449 r = config_parse_sec(unit, filename, line, section, lvalue, ltype,
1450 rvalue, data, userdata);
1454 if (streq(lvalue, "TimeoutSec")) {
1455 s->start_timeout_defined = true;
1456 s->timeout_stop_usec = s->timeout_start_usec;
1457 } else if (streq(lvalue, "TimeoutStartSec"))
1458 s->start_timeout_defined = true;
1463 int config_parse_unit_env_file(const char *unit,
1464 const char *filename,
1466 const char *section,
1475 _cleanup_free_ char *n = NULL;
1484 if (isempty(rvalue)) {
1485 /* Empty assignment frees the list */
1491 r = unit_full_printf(u, rvalue, &n);
1493 log_syntax(unit, LOG_ERR, filename, line, r,
1494 "Failed to resolve specifiers, ignoring: %s", rvalue);
1497 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1498 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1499 "Path '%s' is not absolute, ignoring.", s);
1503 r = strv_extend(env, s);
1510 int config_parse_environ(const char *unit,
1511 const char *filename,
1513 const char *section,
1521 char*** env = data, *w, *state;
1523 _cleanup_free_ char *k = NULL;
1531 if (isempty(rvalue)) {
1532 /* Empty assignment resets the list */
1539 r = unit_full_printf(u, rvalue, &k);
1541 log_syntax(unit, LOG_ERR, filename, line, -r,
1542 "Failed to resolve specifiers, ignoring: %s", rvalue);
1550 FOREACH_WORD_QUOTED(w, l, k, state) {
1551 _cleanup_free_ char *n;
1554 n = cunescape_length(w, l);
1558 if (!env_assignment_is_valid(n)) {
1559 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1560 "Invalid environment assignment, ignoring: %s", rvalue);
1564 x = strv_env_set(*env, n);
1575 int config_parse_ip_tos(const char *unit,
1576 const char *filename,
1578 const char *section,
1585 int *ip_tos = data, x;
1592 x = ip_tos_from_string(rvalue);
1594 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1595 "Failed to parse IP TOS value, ignoring: %s", rvalue);
1603 int config_parse_unit_condition_path(const char *unit,
1604 const char *filename,
1606 const char *section,
1613 ConditionType cond = ltype;
1615 bool trigger, negate;
1617 _cleanup_free_ char *p = NULL;
1625 if (isempty(rvalue)) {
1626 /* Empty assignment resets the list */
1627 condition_free_list(u->conditions);
1628 u->conditions = NULL;
1632 trigger = rvalue[0] == '|';
1636 negate = rvalue[0] == '!';
1640 r = unit_full_printf(u, rvalue, &p);
1642 log_syntax(unit, LOG_ERR, filename, line, -r,
1643 "Failed to resolve specifiers, ignoring: %s", rvalue);
1650 if (!path_is_absolute(p)) {
1651 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1652 "Path in condition not absolute, ignoring: %s", p);
1656 c = condition_new(cond, p, trigger, negate);
1660 LIST_PREPEND(Condition, conditions, u->conditions, c);
1664 int config_parse_unit_condition_string(const char *unit,
1665 const char *filename,
1667 const char *section,
1674 ConditionType cond = ltype;
1676 bool trigger, negate;
1678 _cleanup_free_ char *s = NULL;
1686 if (isempty(rvalue)) {
1687 /* Empty assignment resets the list */
1688 condition_free_list(u->conditions);
1689 u->conditions = NULL;
1693 trigger = rvalue[0] == '|';
1697 negate = rvalue[0] == '!';
1701 r = unit_full_printf(u, rvalue, &s);
1703 log_syntax(unit, LOG_ERR, filename, line, -r,
1704 "Failed to resolve specifiers, ignoring: %s", rvalue);
1711 c = condition_new(cond, s, trigger, negate);
1715 LIST_PREPEND(Condition, conditions, u->conditions, c);
1719 int config_parse_unit_condition_null(const char *unit,
1720 const char *filename,
1722 const char *section,
1731 bool trigger, negate;
1739 if (isempty(rvalue)) {
1740 /* Empty assignment resets the list */
1741 condition_free_list(u->conditions);
1742 u->conditions = NULL;
1746 trigger = rvalue[0] == '|';
1750 negate = rvalue[0] == '!';
1754 b = parse_boolean(rvalue);
1756 log_syntax(unit, LOG_ERR, filename, line, -b,
1757 "Failed to parse boolean value in condition, ignoring: %s",
1765 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1769 LIST_PREPEND(Condition, conditions, u->conditions, c);
1773 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1774 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
1776 int config_parse_unit_requires_mounts_for(
1778 const char *filename,
1780 const char *section,
1797 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1799 _cleanup_free_ char *n;
1805 if (!utf8_is_valid(n)) {
1806 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1807 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
1811 r = unit_require_mounts_for(u, n);
1813 log_syntax(unit, LOG_ERR, filename, line, r,
1814 "Failed to add required mount for, ignoring: %s", rvalue);
1822 int config_parse_documentation(const char *unit,
1823 const char *filename,
1825 const char *section,
1841 if (isempty(rvalue)) {
1842 /* Empty assignment resets the list */
1843 strv_free(u->documentation);
1844 u->documentation = NULL;
1848 r = config_parse_unit_strv_printf(unit, filename, line, section, lvalue, ltype,
1849 rvalue, data, userdata);
1853 for (a = b = u->documentation; a && *a; a++) {
1855 if (is_valid_documentation_url(*a))
1858 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1859 "Invalid URL, ignoring: %s", *a);
1869 static void syscall_set(uint32_t *p, int nr) {
1870 nr = SYSCALL_TO_INDEX(nr);
1871 p[nr >> 4] |= 1 << (nr & 31);
1874 static void syscall_unset(uint32_t *p, int nr) {
1875 nr = SYSCALL_TO_INDEX(nr);
1876 p[nr >> 4] &= ~(1 << (nr & 31));
1879 int config_parse_syscall_filter(const char *unit,
1880 const char *filename,
1882 const char *section,
1889 ExecContext *c = data;
1891 bool invert = false;
1901 if (isempty(rvalue)) {
1902 /* Empty assignment resets the list */
1903 free(c->syscall_filter);
1904 c->syscall_filter = NULL;
1908 if (rvalue[0] == '~') {
1913 if (!c->syscall_filter) {
1916 n = (syscall_max() + 31) >> 4;
1917 c->syscall_filter = new(uint32_t, n);
1918 if (!c->syscall_filter)
1921 memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
1923 /* Add these by default */
1924 syscall_set(c->syscall_filter, __NR_execve);
1925 syscall_set(c->syscall_filter, __NR_rt_sigreturn);
1926 #ifdef __NR_sigreturn
1927 syscall_set(c->syscall_filter, __NR_sigreturn);
1929 syscall_set(c->syscall_filter, __NR_exit_group);
1930 syscall_set(c->syscall_filter, __NR_exit);
1933 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1935 _cleanup_free_ char *t = NULL;
1941 id = syscall_from_name(t);
1943 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1944 "Failed to parse syscall, ignoring: %s", t);
1949 syscall_unset(c->syscall_filter, id);
1951 syscall_set(c->syscall_filter, id);
1954 c->no_new_privileges = true;
1959 int config_parse_unit_slice(
1961 const char *filename,
1963 const char *section,
1970 _cleanup_free_ char *k = NULL;
1971 Unit *u = userdata, *slice;
1979 r = unit_name_printf(u, rvalue, &k);
1981 log_syntax(unit, LOG_ERR, filename, line, -r,
1982 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
1989 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
1991 log_syntax(unit, LOG_ERR, filename, line, -r,
1992 "Failed to load slice unit %s. Ignoring.", k);
1996 if (slice->type != UNIT_SLICE) {
1997 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1998 "Slice unit %s is not a slice. Ignoring.", k);
2002 unit_ref_set(&u->slice, slice);
2006 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2008 int config_parse_cpu_shares(
2010 const char *filename,
2012 const char *section,
2019 CGroupContext *c = data;
2027 if (isempty(rvalue)) {
2028 c->cpu_shares = 1024;
2032 r = safe_atolu(rvalue, &lu);
2033 if (r < 0 || lu <= 0) {
2034 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2035 "CPU shares '%s' invalid. Ignoring.", rvalue);
2043 int config_parse_memory_limit(
2045 const char *filename,
2047 const char *section,
2054 CGroupContext *c = data;
2058 if (isempty(rvalue)) {
2059 c->memory_limit = (uint64_t) -1;
2063 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2065 r = parse_bytes(rvalue, &bytes);
2067 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2068 "Memory limit '%s' invalid. Ignoring.", rvalue);
2072 c->memory_limit = (uint64_t) bytes;
2076 int config_parse_device_allow(
2078 const char *filename,
2080 const char *section,
2087 _cleanup_free_ char *path = NULL;
2088 CGroupContext *c = data;
2089 CGroupDeviceAllow *a;
2093 if (isempty(rvalue)) {
2094 while (c->device_allow)
2095 cgroup_context_free_device_allow(c, c->device_allow);
2100 n = strcspn(rvalue, WHITESPACE);
2101 path = strndup(rvalue, n);
2105 if (!path_startswith(path, "/dev")) {
2106 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2107 "Invalid device node path '%s'. Ignoring.", path);
2111 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2115 if (!in_charset(m, "rwm")) {
2116 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2117 "Invalid device rights '%s'. Ignoring.", m);
2121 a = new0(CGroupDeviceAllow, 1);
2127 a->r = !!strchr(m, 'r');
2128 a->w = !!strchr(m, 'w');
2129 a->m = !!strchr(m, 'm');
2131 LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
2135 int config_parse_blockio_weight(
2137 const char *filename,
2139 const char *section,
2146 CGroupContext *c = data;
2154 if (isempty(rvalue)) {
2155 c->blockio_weight = 1000;
2159 r = safe_atolu(rvalue, &lu);
2160 if (r < 0 || lu < 10 || lu > 1000) {
2161 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2162 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2166 c->blockio_weight = lu;
2171 int config_parse_blockio_device_weight(
2173 const char *filename,
2175 const char *section,
2182 _cleanup_free_ char *path = NULL;
2183 CGroupBlockIODeviceWeight *w;
2184 CGroupContext *c = data;
2194 if (isempty(rvalue)) {
2195 while (c->blockio_device_weights)
2196 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2201 n = strcspn(rvalue, WHITESPACE);
2202 weight = rvalue + n;
2204 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2205 "Expected block device and device weight. Ignoring.");
2209 path = strndup(rvalue, n);
2213 if (!path_startswith(path, "/dev")) {
2214 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2215 "Invalid device node path '%s'. Ignoring.", path);
2219 weight += strspn(weight, WHITESPACE);
2220 r = safe_atolu(weight, &lu);
2221 if (r < 0 || lu < 10 || lu > 1000) {
2222 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2223 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2228 w = new0(CGroupBlockIODeviceWeight, 1);
2237 LIST_PREPEND(CGroupBlockIODeviceWeight, device_weights, c->blockio_device_weights, w);
2241 int config_parse_blockio_bandwidth(
2243 const char *filename,
2245 const char *section,
2252 _cleanup_free_ char *path = NULL;
2253 CGroupBlockIODeviceBandwidth *b;
2254 CGroupContext *c = data;
2255 const char *bandwidth;
2265 read = streq("BlockIOReadBandwidth", lvalue);
2267 if (isempty(rvalue)) {
2268 CGroupBlockIODeviceBandwidth *next;
2270 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2271 if (b->read == read)
2272 cgroup_context_free_blockio_device_bandwidth(c, b);
2277 n = strcspn(rvalue, WHITESPACE);
2278 bandwidth = rvalue + n;
2279 bandwidth += strspn(bandwidth, WHITESPACE);
2282 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2283 "Expected space separated pair of device node and bandwidth. Ignoring.");
2287 path = strndup(rvalue, n);
2291 if (!path_startswith(path, "/dev")) {
2292 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2293 "Invalid device node path '%s'. Ignoring.", path);
2297 r = parse_bytes(bandwidth, &bytes);
2298 if (r < 0 || bytes <= 0) {
2299 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2300 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2304 b = new0(CGroupBlockIODeviceBandwidth, 1);
2310 b->bandwidth = (uint64_t) bytes;
2313 LIST_PREPEND(CGroupBlockIODeviceBandwidth, device_bandwidths, c->blockio_device_bandwidths, b);
2318 #define FOLLOW_MAX 8
2320 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2331 /* This will update the filename pointer if the loaded file is
2332 * reached by a symlink. The old string will be freed. */
2335 char *target, *name;
2337 if (c++ >= FOLLOW_MAX)
2340 path_kill_slashes(*filename);
2342 /* Add the file name we are currently looking at to
2343 * the names of this unit, but only if it is a valid
2345 name = path_get_file_name(*filename);
2347 if (unit_name_is_valid(name, true)) {
2349 id = set_get(names, name);
2355 r = set_consume(names, id);
2361 /* Try to open the file name, but don't if its a symlink */
2362 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2369 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2370 r = readlink_and_make_absolute(*filename, &target);
2378 f = fdopen(fd, "re");
2381 close_nointr_nofail(fd);
2390 static int merge_by_names(Unit **u, Set *names, const char *id) {
2398 /* Let's try to add in all symlink names we found */
2399 while ((k = set_steal_first(names))) {
2401 /* First try to merge in the other name into our
2403 r = unit_merge_by_name(*u, k);
2407 /* Hmm, we couldn't merge the other unit into
2408 * ours? Then let's try it the other way
2411 other = manager_get_unit((*u)->manager, k);
2415 r = unit_merge(other, *u);
2418 return merge_by_names(u, names, NULL);
2426 unit_choose_id(*u, id);
2434 static int load_from_path(Unit *u, const char *path) {
2438 char *filename = NULL, *id = NULL;
2445 symlink_names = set_new(string_hash_func, string_compare_func);
2449 if (path_is_absolute(path)) {
2451 filename = strdup(path);
2457 r = open_follow(&filename, &f, symlink_names, &id);
2469 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
2471 /* Instead of opening the path right away, we manually
2472 * follow all symlinks and add their name to our unit
2473 * name set while doing so */
2474 filename = path_make_absolute(path, *p);
2480 if (u->manager->unit_path_cache &&
2481 !set_get(u->manager->unit_path_cache, filename))
2484 r = open_follow(&filename, &f, symlink_names, &id);
2493 /* Empty the symlink names for the next run */
2494 set_clear_free(symlink_names);
2503 /* Hmm, no suitable file found? */
2509 r = merge_by_names(&merged, symlink_names, id);
2514 u->load_state = UNIT_MERGED;
2519 if (fstat(fileno(f), &st) < 0) {
2524 if (null_or_empty(&st))
2525 u->load_state = UNIT_MASKED;
2527 u->load_state = UNIT_LOADED;
2529 /* Now, parse the file contents */
2530 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
2531 config_item_perf_lookup,
2532 (void*) load_fragment_gperf_lookup, false, true, u);
2537 free(u->fragment_path);
2538 u->fragment_path = filename;
2541 u->fragment_mtime = timespec_load(&st.st_mtim);
2543 if (u->source_path) {
2544 if (stat(u->source_path, &st) >= 0)
2545 u->source_mtime = timespec_load(&st.st_mtim);
2547 u->source_mtime = 0;
2553 set_free_free(symlink_names);
2562 int unit_load_fragment(Unit *u) {
2568 assert(u->load_state == UNIT_STUB);
2571 /* First, try to find the unit under its id. We always look
2572 * for unit files in the default directories, to make it easy
2573 * to override things by placing things in /etc/systemd/system */
2574 r = load_from_path(u, u->id);
2578 /* Try to find an alias we can load this with */
2579 if (u->load_state == UNIT_STUB)
2580 SET_FOREACH(t, u->names, i) {
2585 r = load_from_path(u, t);
2589 if (u->load_state != UNIT_STUB)
2593 /* And now, try looking for it under the suggested (originally linked) path */
2594 if (u->load_state == UNIT_STUB && u->fragment_path) {
2596 r = load_from_path(u, u->fragment_path);
2600 if (u->load_state == UNIT_STUB) {
2601 /* Hmm, this didn't work? Then let's get rid
2602 * of the fragment path stored for us, so that
2603 * we don't point to an invalid location. */
2604 free(u->fragment_path);
2605 u->fragment_path = NULL;
2609 /* Look for a template */
2610 if (u->load_state == UNIT_STUB && u->instance) {
2613 k = unit_name_template(u->id);
2617 r = load_from_path(u, k);
2623 if (u->load_state == UNIT_STUB)
2624 SET_FOREACH(t, u->names, i) {
2629 k = unit_name_template(t);
2633 r = load_from_path(u, k);
2639 if (u->load_state != UNIT_STUB)
2647 void unit_dump_config_items(FILE *f) {
2648 static const struct {
2649 const ConfigParserCallback callback;
2652 { config_parse_int, "INTEGER" },
2653 { config_parse_unsigned, "UNSIGNED" },
2654 { config_parse_bytes_size, "SIZE" },
2655 { config_parse_bool, "BOOLEAN" },
2656 { config_parse_string, "STRING" },
2657 { config_parse_path, "PATH" },
2658 { config_parse_unit_path_printf, "PATH" },
2659 { config_parse_strv, "STRING [...]" },
2660 { config_parse_exec_nice, "NICE" },
2661 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2662 { config_parse_exec_io_class, "IOCLASS" },
2663 { config_parse_exec_io_priority, "IOPRIORITY" },
2664 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2665 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2666 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2667 { config_parse_mode, "MODE" },
2668 { config_parse_unit_env_file, "FILE" },
2669 { config_parse_output, "OUTPUT" },
2670 { config_parse_input, "INPUT" },
2671 { config_parse_facility, "FACILITY" },
2672 { config_parse_level, "LEVEL" },
2673 { config_parse_exec_capabilities, "CAPABILITIES" },
2674 { config_parse_exec_secure_bits, "SECUREBITS" },
2675 { config_parse_bounding_set, "BOUNDINGSET" },
2676 { config_parse_limit, "LIMIT" },
2677 { config_parse_unit_deps, "UNIT [...]" },
2678 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2679 { config_parse_service_type, "SERVICETYPE" },
2680 { config_parse_service_restart, "SERVICERESTART" },
2681 #ifdef HAVE_SYSV_COMPAT
2682 { config_parse_sysv_priority, "SYSVPRIORITY" },
2684 { config_parse_warn_compat, "NOTSUPPORTED" },
2686 { config_parse_kill_mode, "KILLMODE" },
2687 { config_parse_kill_signal, "SIGNAL" },
2688 { config_parse_socket_listen, "SOCKET [...]" },
2689 { config_parse_socket_bind, "SOCKETBIND" },
2690 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
2691 { config_parse_sec, "SECONDS" },
2692 { config_parse_nsec, "NANOSECONDS" },
2693 { config_parse_path_strv, "PATH [...]" },
2694 { config_parse_unit_requires_mounts_for, "PATH [...]" },
2695 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2696 { config_parse_unit_string_printf, "STRING" },
2697 { config_parse_trigger_unit, "UNIT" },
2698 { config_parse_timer, "TIMER" },
2699 { config_parse_path_spec, "PATH" },
2700 { config_parse_notify_access, "ACCESS" },
2701 { config_parse_ip_tos, "TOS" },
2702 { config_parse_unit_condition_path, "CONDITION" },
2703 { config_parse_unit_condition_string, "CONDITION" },
2704 { config_parse_unit_condition_null, "CONDITION" },
2705 { config_parse_unit_slice, "SLICE" },
2706 { config_parse_documentation, "URL" },
2707 { config_parse_service_timeout, "SECONDS" },
2708 { config_parse_start_limit_action, "ACTION" },
2709 { config_parse_set_status, "STATUS" },
2710 { config_parse_service_sockets, "SOCKETS" },
2711 { config_parse_fsck_passno, "PASSNO" },
2712 { config_parse_environ, "ENVIRON" },
2713 { config_parse_syscall_filter, "SYSCALL" },
2714 { config_parse_cpu_shares, "SHARES" },
2715 { config_parse_memory_limit, "LIMIT" },
2716 { config_parse_device_allow, "DEVICE" },
2717 { config_parse_device_policy, "POLICY" },
2718 { config_parse_blockio_bandwidth, "BANDWIDTH" },
2719 { config_parse_blockio_weight, "WEIGHT" },
2720 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
2721 { config_parse_long, "LONG" },
2722 { config_parse_socket_service, "SERVICE" },
2725 const char *prev = NULL;
2730 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2731 const char *rvalue = "OTHER", *lvalue;
2735 const ConfigPerfItem *p;
2737 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2739 dot = strchr(i, '.');
2740 lvalue = dot ? dot + 1 : i;
2744 if (!prev || !strneq(prev, i, prefix_len+1)) {
2748 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2751 for (j = 0; j < ELEMENTSOF(table); j++)
2752 if (p->parse == table[j].callback) {
2753 rvalue = table[j].rvalue;
2757 fprintf(f, "%s=%s\n", lvalue, rvalue);