1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <linux/oom.h>
29 #include <sys/prctl.h>
30 #include <sys/mount.h>
34 #include <sys/resource.h>
38 #include "conf-parser.h"
39 #include "load-fragment.h"
42 #include "securebits.h"
44 #include "unit-name.h"
45 #include "unit-printf.h"
46 #include "bus-errors.h"
48 #include "path-util.h"
49 #include "syscall-list.h"
51 #ifndef HAVE_SYSV_COMPAT
52 int config_parse_warn_compat(
62 log_debug("[%s:%u] Support for option %s= has been disabled at compile time and is ignored", filename, line, lvalue);
67 int config_parse_unit_deps(
77 UnitDependency d = ltype;
87 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
95 k = unit_name_printf(u, t);
100 r = unit_add_dependency_by_name(u, d, k, NULL, true);
102 log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r));
110 int config_parse_unit_string_printf(
111 const char *filename,
129 k = unit_full_printf(u, rvalue);
133 r = config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata);
139 int config_parse_unit_strv_printf(
140 const char *filename,
158 k = unit_full_printf(u, rvalue);
162 r = config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata);
168 int config_parse_unit_path_printf(
169 const char *filename,
187 k = unit_full_printf(u, rvalue);
191 r = config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata);
197 int config_parse_socket_listen(
198 const char *filename,
207 SocketPort *p, *tail;
217 p = new0(SocketPort, 1);
221 if (streq(lvalue, "ListenFIFO")) {
222 p->type = SOCKET_FIFO;
224 if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
229 path_kill_slashes(p->path);
231 } else if (streq(lvalue, "ListenSpecial")) {
232 p->type = SOCKET_SPECIAL;
234 if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
239 path_kill_slashes(p->path);
241 } else if (streq(lvalue, "ListenMessageQueue")) {
243 p->type = SOCKET_MQUEUE;
245 if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
250 path_kill_slashes(p->path);
252 } else if (streq(lvalue, "ListenNetlink")) {
256 p->type = SOCKET_SOCKET;
257 k = unit_full_printf(UNIT(s), rvalue);
258 r = socket_address_parse_netlink(&p->address, k);
262 log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
271 p->type = SOCKET_SOCKET;
272 k = unit_full_printf(UNIT(s), rvalue);
273 r = socket_address_parse(&p->address, k);
277 log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
282 if (streq(lvalue, "ListenStream"))
283 p->address.type = SOCK_STREAM;
284 else if (streq(lvalue, "ListenDatagram"))
285 p->address.type = SOCK_DGRAM;
287 assert(streq(lvalue, "ListenSequentialPacket"));
288 p->address.type = SOCK_SEQPACKET;
291 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
292 log_error("[%s:%u] Address family not supported, ignoring: %s", filename, line, rvalue);
301 LIST_FIND_TAIL(SocketPort, port, s->ports, tail);
302 LIST_INSERT_AFTER(SocketPort, port, s->ports, tail, p);
304 LIST_PREPEND(SocketPort, port, s->ports, p);
309 int config_parse_socket_bind(
310 const char *filename,
320 SocketAddressBindIPv6Only b;
329 if ((b = socket_address_bind_ipv6_only_from_string(rvalue)) < 0) {
332 if ((r = parse_boolean(rvalue)) < 0) {
333 log_error("[%s:%u] Failed to parse bind IPv6 only value, ignoring: %s", filename, line, rvalue);
337 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
339 s->bind_ipv6_only = b;
344 int config_parse_exec_nice(
345 const char *filename,
354 ExecContext *c = data;
362 if (safe_atoi(rvalue, &priority) < 0) {
363 log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue);
367 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
368 log_error("[%s:%u] Nice priority out of range, ignoring: %s", filename, line, rvalue);
378 int config_parse_exec_oom_score_adjust(
379 const char *filename,
388 ExecContext *c = data;
396 if (safe_atoi(rvalue, &oa) < 0) {
397 log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue);
401 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
402 log_error("[%s:%u] OOM score adjust value out of range, ignoring: %s", filename, line, rvalue);
406 c->oom_score_adjust = oa;
407 c->oom_score_adjust_set = true;
412 int config_parse_exec(
413 const char *filename,
422 ExecCommand **e = data, *nce;
432 /* We accept an absolute path as first argument, or
433 * alternatively an absolute prefixed with @ to allow
434 * overriding of argv[0]. */
442 bool honour_argv0 = false, ignore = false;
448 rvalue += strspn(rvalue, WHITESPACE);
453 if (rvalue[0] == '-') {
458 if (rvalue[0] == '@') {
463 if (*rvalue != '/') {
464 log_error("[%s:%u] Invalid executable path in command line, ignoring: %s", filename, line, rvalue);
469 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
470 if (strncmp(w, ";", MAX(l, 1U)) == 0)
476 n = new(char*, k + !honour_argv0);
481 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
482 if (strncmp(w, ";", MAX(l, 1U)) == 0)
485 if (honour_argv0 && w == rvalue) {
488 path = strndup(w, l);
494 if (!utf8_is_valid(path)) {
495 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
503 c = n[k++] = cunescape_length(w, l);
509 if (!utf8_is_valid(c)) {
510 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
520 log_error("[%s:%u] Invalid command line, ignoring: %s", filename, line, rvalue);
533 assert(path_is_absolute(path));
535 nce = new0(ExecCommand, 1);
543 nce->ignore = ignore;
545 path_kill_slashes(nce->path);
547 exec_command_append_list(e, nce);
563 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
564 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
566 int config_parse_socket_bindtodevice(
567 const char *filename,
584 if (rvalue[0] && !streq(rvalue, "*")) {
585 if (!(n = strdup(rvalue)))
590 free(s->bind_to_device);
591 s->bind_to_device = n;
596 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
597 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
599 int config_parse_exec_io_class(
600 const char *filename,
609 ExecContext *c = data;
617 if ((x = ioprio_class_from_string(rvalue)) < 0) {
618 log_error("[%s:%u] Failed to parse IO scheduling class, ignoring: %s", filename, line, rvalue);
622 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
623 c->ioprio_set = true;
628 int config_parse_exec_io_priority(
629 const char *filename,
638 ExecContext *c = data;
646 if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) {
647 log_error("[%s:%u] Failed to parse io priority, ignoring: %s", filename, line, rvalue);
651 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
652 c->ioprio_set = true;
657 int config_parse_exec_cpu_sched_policy(
658 const char *filename,
668 ExecContext *c = data;
676 if ((x = sched_policy_from_string(rvalue)) < 0) {
677 log_error("[%s:%u] Failed to parse CPU scheduling policy, ignoring: %s", filename, line, rvalue);
681 c->cpu_sched_policy = x;
682 c->cpu_sched_set = true;
687 int config_parse_exec_cpu_sched_prio(
688 const char *filename,
697 ExecContext *c = data;
705 /* On Linux RR/FIFO have the same range */
706 if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) {
707 log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, rvalue);
711 c->cpu_sched_priority = i;
712 c->cpu_sched_set = true;
717 int config_parse_exec_cpu_affinity(
718 const char *filename,
727 ExecContext *c = data;
737 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
742 if (!(t = strndup(w, l)))
745 r = safe_atou(t, &cpu);
749 if (!(c->cpuset = cpu_set_malloc(&c->cpuset_ncpus)))
752 if (r < 0 || cpu >= c->cpuset_ncpus) {
753 log_error("[%s:%u] Failed to parse CPU affinity, ignoring: %s", filename, line, rvalue);
757 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
763 int config_parse_exec_capabilities(
764 const char *filename,
773 ExecContext *c = data;
781 if (!(cap = cap_from_text(rvalue))) {
785 log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue);
790 cap_free(c->capabilities);
791 c->capabilities = cap;
796 int config_parse_exec_secure_bits(
797 const char *filename,
806 ExecContext *c = data;
816 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
817 if (first_word(w, "keep-caps"))
818 c->secure_bits |= SECURE_KEEP_CAPS;
819 else if (first_word(w, "keep-caps-locked"))
820 c->secure_bits |= SECURE_KEEP_CAPS_LOCKED;
821 else if (first_word(w, "no-setuid-fixup"))
822 c->secure_bits |= SECURE_NO_SETUID_FIXUP;
823 else if (first_word(w, "no-setuid-fixup-locked"))
824 c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED;
825 else if (first_word(w, "noroot"))
826 c->secure_bits |= SECURE_NOROOT;
827 else if (first_word(w, "noroot-locked"))
828 c->secure_bits |= SECURE_NOROOT_LOCKED;
830 log_error("[%s:%u] Failed to parse secure bits, ignoring: %s", filename, line, rvalue);
838 int config_parse_bounding_set(
839 const char *filename,
848 uint64_t *capability_bounding_set_drop = data;
860 if (rvalue[0] == '~') {
865 /* Note that we store this inverted internally, since the
866 * kernel wants it like this. But we actually expose it
867 * non-inverted everywhere to have a fully normalized
870 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
879 r = cap_from_name(t, &cap);
883 log_error("[%s:%u] Failed to parse capability bounding set, ignoring: %s", filename, line, rvalue);
887 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
891 *capability_bounding_set_drop |= sum;
893 *capability_bounding_set_drop |= ~sum;
898 int config_parse_limit(
899 const char *filename,
908 struct rlimit **rl = data;
909 unsigned long long u;
918 if (streq(rvalue, "infinity"))
919 u = (unsigned long long) RLIM_INFINITY;
920 else if (safe_atollu(rvalue, &u) < 0) {
921 log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue);
926 if (!(*rl = new(struct rlimit, 1)))
929 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
933 int config_parse_unit_cgroup(
934 const char *filename,
948 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
956 k = unit_full_printf(u, t);
968 r = unit_add_cgroup_from_text(u, t);
972 log_error("[%s:%u] Failed to parse cgroup value, ignoring: %s", filename, line, rvalue);
980 #ifdef HAVE_SYSV_COMPAT
981 int config_parse_sysv_priority(
982 const char *filename,
991 int *priority = data;
999 if (safe_atoi(rvalue, &i) < 0 || i < 0) {
1000 log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue);
1004 *priority = (int) i;
1009 int config_parse_fsck_passno(
1010 const char *filename,
1012 const char *section,
1027 if (safe_atoi(rvalue, &i) || i < 0) {
1028 log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue);
1036 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1038 int config_parse_kill_signal(
1039 const char *filename,
1041 const char *section,
1056 if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
1057 log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue);
1065 int config_parse_exec_mount_flags(
1066 const char *filename,
1068 const char *section,
1075 ExecContext *c = data;
1079 unsigned long flags = 0;
1086 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1087 if (strncmp(w, "shared", MAX(l, 6U)) == 0)
1089 else if (strncmp(w, "slave", MAX(l, 5U)) == 0)
1091 else if (strncmp(w, "private", MAX(l, 7U)) == 0)
1092 flags |= MS_PRIVATE;
1094 log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue);
1099 c->mount_flags = flags;
1103 int config_parse_timer(
1104 const char *filename,
1106 const char *section,
1123 if ((b = timer_base_from_string(lvalue)) < 0) {
1124 log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue);
1128 if (parse_usec(rvalue, &u) < 0) {
1129 log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue);
1133 if (!(v = new0(TimerValue, 1)))
1139 LIST_PREPEND(TimerValue, value, t->values, v);
1144 int config_parse_timer_unit(
1145 const char *filename,
1147 const char *section,
1164 dbus_error_init(&error);
1166 if (endswith(rvalue, ".timer")) {
1167 log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue);
1171 r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, NULL, &u);
1173 log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
1174 dbus_error_free(&error);
1178 unit_ref_set(&t->unit, u);
1183 int config_parse_path_spec(
1184 const char *filename,
1186 const char *section,
1203 b = path_type_from_string(lvalue);
1205 log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue);
1209 k = unit_full_printf(UNIT(p), rvalue);
1213 if (!path_is_absolute(k)) {
1214 log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, k);
1219 s = new0(PathSpec, 1);
1225 s->path = path_kill_slashes(k);
1229 LIST_PREPEND(PathSpec, spec, p->specs, s);
1234 int config_parse_path_unit(
1235 const char *filename,
1237 const char *section,
1254 dbus_error_init(&error);
1256 if (endswith(rvalue, ".path")) {
1257 log_error("[%s:%u] Unit cannot be of type path, ignoring: %s", filename, line, rvalue);
1261 if ((r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, &error, &u)) < 0) {
1262 log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
1263 dbus_error_free(&error);
1267 unit_ref_set(&t->unit, u);
1272 int config_parse_socket_service(
1273 const char *filename,
1275 const char *section,
1292 dbus_error_init(&error);
1294 if (!endswith(rvalue, ".service")) {
1295 log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue);
1299 r = manager_load_unit(UNIT(s)->manager, rvalue, NULL, &error, &x);
1301 log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
1302 dbus_error_free(&error);
1306 unit_ref_set(&s->service, x);
1311 int config_parse_service_sockets(
1312 const char *filename,
1314 const char *section,
1331 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1338 k = unit_name_printf(UNIT(s), t);
1344 if (!endswith(k, ".socket")) {
1345 log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue);
1350 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
1352 log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r));
1354 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
1364 int config_parse_service_timeout(
1365 const char *filename,
1367 const char *section,
1374 Service *s = userdata;
1382 r = config_parse_usec(filename, line, section, lvalue, ltype, rvalue, data, userdata);
1387 if (streq(lvalue, "TimeoutSec")) {
1388 s->start_timeout_defined = true;
1389 s->timeout_stop_usec = s->timeout_start_usec;
1390 } else if (streq(lvalue, "TimeoutStartSec"))
1391 s->start_timeout_defined = true;
1396 int config_parse_unit_env_file(
1397 const char *filename,
1399 const char *section,
1406 char ***env = data, **k;
1415 s = unit_full_printf(u, rvalue);
1419 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1420 log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s);
1425 k = strv_append(*env, s);
1436 int config_parse_ip_tos(
1437 const char *filename,
1439 const char *section,
1446 int *ip_tos = data, x;
1453 if ((x = ip_tos_from_string(rvalue)) < 0)
1454 if (safe_atoi(rvalue, &x) < 0) {
1455 log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue);
1463 int config_parse_unit_condition_path(
1464 const char *filename,
1466 const char *section,
1473 ConditionType cond = ltype;
1475 bool trigger, negate;
1477 _cleanup_free_ char *p = NULL;
1484 trigger = rvalue[0] == '|';
1488 negate = rvalue[0] == '!';
1492 p = unit_full_printf(u, rvalue);
1496 if (!path_is_absolute(p)) {
1497 log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, p);
1501 c = condition_new(cond, p, trigger, negate);
1505 LIST_PREPEND(Condition, conditions, u->conditions, c);
1509 int config_parse_unit_condition_string(
1510 const char *filename,
1512 const char *section,
1519 ConditionType cond = ltype;
1521 bool trigger, negate;
1523 _cleanup_free_ char *s = NULL;
1530 trigger = rvalue[0] == '|';
1534 negate = rvalue[0] == '!';
1538 s = unit_full_printf(u, rvalue);
1542 c = condition_new(cond, s, trigger, negate);
1546 LIST_PREPEND(Condition, conditions, u->conditions, c);
1550 int config_parse_unit_condition_null(
1551 const char *filename,
1553 const char *section,
1562 bool trigger, negate;
1570 if ((trigger = rvalue[0] == '|'))
1573 if ((negate = rvalue[0] == '!'))
1576 if ((b = parse_boolean(rvalue)) < 0) {
1577 log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue);
1584 if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate)))
1587 LIST_PREPEND(Condition, conditions, u->conditions, c);
1591 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1592 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
1594 int config_parse_unit_cgroup_attr(
1595 const char *filename,
1597 const char *section,
1613 l = strv_split_quoted(rvalue);
1617 if (strv_length(l) != 2) {
1618 log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
1623 r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL);
1627 log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
1634 int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
1645 if (safe_atolu(rvalue, &ul) < 0 || ul < 1) {
1646 log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue);
1650 if (asprintf(&t, "%lu", ul) < 0)
1653 r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL);
1657 log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
1664 int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
1675 if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) {
1676 log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue);
1680 if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
1683 r = unit_add_cgroup_attribute(u,
1685 streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
1690 log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
1697 static int device_map(const char *controller, const char *name, const char *value, char **ret) {
1705 l = strv_split_quoted(value);
1709 assert(strv_length(l) >= 1);
1711 if (streq(l[0], "*")) {
1713 if (asprintf(ret, "a *:*%s%s",
1714 isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
1722 if (stat(l[0], &st) < 0) {
1723 log_warning("Couldn't stat device %s", l[0]);
1728 if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
1729 log_warning("%s is not a device.", l[0]);
1734 if (asprintf(ret, "%c %u:%u%s%s",
1735 S_ISCHR(st.st_mode) ? 'c' : 'b',
1736 major(st.st_rdev), minor(st.st_rdev),
1737 isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
1748 int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
1759 l = strv_split_quoted(rvalue);
1764 if (k < 1 || k > 2) {
1765 log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
1770 if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) {
1771 log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue);
1776 if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
1777 log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
1783 r = unit_add_cgroup_attribute(u, "devices",
1784 streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
1785 rvalue, device_map);
1788 log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
1795 static int blkio_map(const char *controller, const char *name, const char *value, char **ret) {
1805 l = strv_split_quoted(value);
1809 assert(strv_length(l) == 2);
1811 if (stat(l[0], &st) < 0) {
1812 log_warning("Couldn't stat device %s", l[0]);
1817 if (S_ISBLK(st.st_mode))
1819 else if (major(st.st_dev) != 0) {
1820 /* If this is not a device node then find the block
1821 * device this file is stored on */
1824 /* If this is a partition, try to get the originating
1826 block_get_whole_disk(d, &d);
1828 log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
1833 if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) {
1842 int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
1846 const char *device = NULL, *weight;
1855 l = strv_split_quoted(rvalue);
1860 if (k < 1 || k > 2) {
1861 log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue);
1873 if (device && !path_is_absolute(device)) {
1874 log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
1879 if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) {
1880 log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue);
1886 r = asprintf(&t, "%s %lu", device, ul);
1888 r = asprintf(&t, "%lu", ul);
1895 r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map);
1897 r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL);
1901 log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
1908 int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
1920 l = strv_split_quoted(rvalue);
1926 log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue);
1931 if (!path_is_absolute(l[0])) {
1932 log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
1937 if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) {
1938 log_error("[%s:%u] Failed to parse block IO bandwidth value, ignoring: %s", filename, line, rvalue);
1943 r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes);
1949 r = unit_add_cgroup_attribute(u, "blkio",
1950 streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device",
1955 log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
1962 int config_parse_unit_requires_mounts_for(
1963 const char *filename,
1965 const char *section,
1981 empty_before = !u->requires_mounts_for;
1983 r = config_parse_path_strv(filename, line, section, lvalue, ltype, rvalue, data, userdata);
1985 /* Make it easy to find units with requires_mounts set */
1986 if (empty_before && u->requires_mounts_for)
1987 LIST_PREPEND(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
1992 int config_parse_documentation(
1993 const char *filename,
1995 const char *section,
2011 r = config_parse_unit_strv_printf(filename, line, section, lvalue, ltype, rvalue, data, userdata);
2015 for (a = b = u->documentation; a && *a; a++) {
2017 if (is_valid_documentation_url(*a))
2020 log_error("[%s:%u] Invalid URL, ignoring: %s", filename, line, *a);
2029 static void syscall_set(uint32_t *p, int nr) {
2030 p[nr >> 4] |= 1 << (nr & 31);
2033 static void syscall_unset(uint32_t *p, int nr) {
2034 p[nr >> 4] &= ~(1 << (nr & 31));
2037 int config_parse_syscall_filter(
2038 const char *filename,
2040 const char *section,
2047 ExecContext *c = data;
2049 bool invert = false;
2059 if (rvalue[0] == '~') {
2064 if (!c->syscall_filter) {
2067 n = (syscall_max() + 31) >> 4;
2068 c->syscall_filter = new(uint32_t, n);
2069 if (!c->syscall_filter)
2072 memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
2074 /* Add these by default */
2075 syscall_set(c->syscall_filter, __NR_execve);
2076 syscall_set(c->syscall_filter, __NR_rt_sigreturn);
2077 #ifdef __NR_sigreturn
2078 syscall_set(c->syscall_filter, __NR_sigreturn);
2080 syscall_set(c->syscall_filter, __NR_exit_group);
2081 syscall_set(c->syscall_filter, __NR_exit);
2084 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2092 id = syscall_from_name(t);
2096 log_error("[%s:%u] Failed to parse syscall, ignoring: %s", filename, line, rvalue);
2101 syscall_unset(c->syscall_filter, id);
2103 syscall_set(c->syscall_filter, id);
2106 c->no_new_privileges = true;
2111 #define FOLLOW_MAX 8
2113 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2124 /* This will update the filename pointer if the loaded file is
2125 * reached by a symlink. The old string will be freed. */
2128 char *target, *name;
2130 if (c++ >= FOLLOW_MAX)
2133 path_kill_slashes(*filename);
2135 /* Add the file name we are currently looking at to
2136 * the names of this unit, but only if it is a valid
2138 name = path_get_file_name(*filename);
2140 if (unit_name_is_valid(name, true)) {
2142 id = set_get(names, name);
2148 r = set_put(names, id);
2156 /* Try to open the file name, but don't if its a symlink */
2157 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2164 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2165 r = readlink_and_make_absolute(*filename, &target);
2173 f = fdopen(fd, "re");
2176 close_nointr_nofail(fd);
2185 static int merge_by_names(Unit **u, Set *names, const char *id) {
2193 /* Let's try to add in all symlink names we found */
2194 while ((k = set_steal_first(names))) {
2196 /* First try to merge in the other name into our
2198 r = unit_merge_by_name(*u, k);
2202 /* Hmm, we couldn't merge the other unit into
2203 * ours? Then let's try it the other way
2206 other = manager_get_unit((*u)->manager, k);
2210 r = unit_merge(other, *u);
2213 return merge_by_names(u, names, NULL);
2221 unit_choose_id(*u, id);
2229 static int load_from_path(Unit *u, const char *path) {
2233 char *filename = NULL, *id = NULL;
2240 symlink_names = set_new(string_hash_func, string_compare_func);
2244 if (path_is_absolute(path)) {
2246 filename = strdup(path);
2252 r = open_follow(&filename, &f, symlink_names, &id);
2264 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
2266 /* Instead of opening the path right away, we manually
2267 * follow all symlinks and add their name to our unit
2268 * name set while doing so */
2269 filename = path_make_absolute(path, *p);
2275 if (u->manager->unit_path_cache &&
2276 !set_get(u->manager->unit_path_cache, filename))
2279 r = open_follow(&filename, &f, symlink_names, &id);
2288 /* Empty the symlink names for the next run */
2289 set_clear_free(symlink_names);
2298 /* Hmm, no suitable file found? */
2304 r = merge_by_names(&merged, symlink_names, id);
2309 u->load_state = UNIT_MERGED;
2314 if (fstat(fileno(f), &st) < 0) {
2319 if (null_or_empty(&st))
2320 u->load_state = UNIT_MASKED;
2322 /* Now, parse the file contents */
2323 r = config_parse(filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, (void*) load_fragment_gperf_lookup, false, u);
2327 u->load_state = UNIT_LOADED;
2330 free(u->fragment_path);
2331 u->fragment_path = filename;
2334 u->fragment_mtime = timespec_load(&st.st_mtim);
2336 if (u->source_path) {
2337 if (stat(u->source_path, &st) >= 0)
2338 u->source_mtime = timespec_load(&st.st_mtim);
2340 u->source_mtime = 0;
2346 set_free_free(symlink_names);
2355 int unit_load_fragment(Unit *u) {
2361 assert(u->load_state == UNIT_STUB);
2364 /* First, try to find the unit under its id. We always look
2365 * for unit files in the default directories, to make it easy
2366 * to override things by placing things in /etc/systemd/system */
2367 r = load_from_path(u, u->id);
2371 /* Try to find an alias we can load this with */
2372 if (u->load_state == UNIT_STUB)
2373 SET_FOREACH(t, u->names, i) {
2378 r = load_from_path(u, t);
2382 if (u->load_state != UNIT_STUB)
2386 /* And now, try looking for it under the suggested (originally linked) path */
2387 if (u->load_state == UNIT_STUB && u->fragment_path) {
2389 r = load_from_path(u, u->fragment_path);
2393 if (u->load_state == UNIT_STUB) {
2394 /* Hmm, this didn't work? Then let's get rid
2395 * of the fragment path stored for us, so that
2396 * we don't point to an invalid location. */
2397 free(u->fragment_path);
2398 u->fragment_path = NULL;
2402 /* Look for a template */
2403 if (u->load_state == UNIT_STUB && u->instance) {
2406 k = unit_name_template(u->id);
2410 r = load_from_path(u, k);
2416 if (u->load_state == UNIT_STUB)
2417 SET_FOREACH(t, u->names, i) {
2422 k = unit_name_template(t);
2426 r = load_from_path(u, k);
2432 if (u->load_state != UNIT_STUB)
2440 void unit_dump_config_items(FILE *f) {
2441 static const struct {
2442 const ConfigParserCallback callback;
2445 { config_parse_int, "INTEGER" },
2446 { config_parse_unsigned, "UNSIGNED" },
2447 { config_parse_bytes_size, "SIZE" },
2448 { config_parse_bool, "BOOLEAN" },
2449 { config_parse_string, "STRING" },
2450 { config_parse_path, "PATH" },
2451 { config_parse_unit_path_printf, "PATH" },
2452 { config_parse_strv, "STRING [...]" },
2453 { config_parse_exec_nice, "NICE" },
2454 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2455 { config_parse_exec_io_class, "IOCLASS" },
2456 { config_parse_exec_io_priority, "IOPRIORITY" },
2457 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2458 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2459 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2460 { config_parse_mode, "MODE" },
2461 { config_parse_unit_env_file, "FILE" },
2462 { config_parse_output, "OUTPUT" },
2463 { config_parse_input, "INPUT" },
2464 { config_parse_facility, "FACILITY" },
2465 { config_parse_level, "LEVEL" },
2466 { config_parse_exec_capabilities, "CAPABILITIES" },
2467 { config_parse_exec_secure_bits, "SECUREBITS" },
2468 { config_parse_bounding_set, "BOUNDINGSET" },
2469 { config_parse_limit, "LIMIT" },
2470 { config_parse_unit_cgroup, "CGROUP [...]" },
2471 { config_parse_unit_deps, "UNIT [...]" },
2472 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2473 { config_parse_service_type, "SERVICETYPE" },
2474 { config_parse_service_restart, "SERVICERESTART" },
2475 #ifdef HAVE_SYSV_COMPAT
2476 { config_parse_sysv_priority, "SYSVPRIORITY" },
2478 { config_parse_warn_compat, "NOTSUPPORTED" },
2480 { config_parse_kill_mode, "KILLMODE" },
2481 { config_parse_kill_signal, "SIGNAL" },
2482 { config_parse_socket_listen, "SOCKET [...]" },
2483 { config_parse_socket_bind, "SOCKETBIND" },
2484 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
2485 { config_parse_usec, "SECONDS" },
2486 { config_parse_nsec, "NANOSECONDS" },
2487 { config_parse_path_strv, "PATH [...]" },
2488 { config_parse_unit_requires_mounts_for, "PATH [...]" },
2489 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2490 { config_parse_unit_string_printf, "STRING" },
2491 { config_parse_timer, "TIMER" },
2492 { config_parse_timer_unit, "NAME" },
2493 { config_parse_path_spec, "PATH" },
2494 { config_parse_path_unit, "UNIT" },
2495 { config_parse_notify_access, "ACCESS" },
2496 { config_parse_ip_tos, "TOS" },
2497 { config_parse_unit_condition_path, "CONDITION" },
2498 { config_parse_unit_condition_string, "CONDITION" },
2499 { config_parse_unit_condition_null, "CONDITION" },
2502 const char *prev = NULL;
2507 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2508 const char *rvalue = "OTHER", *lvalue;
2512 const ConfigPerfItem *p;
2514 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2516 dot = strchr(i, '.');
2517 lvalue = dot ? dot + 1 : i;
2521 if (!prev || strncmp(prev, i, prefix_len+1) != 0) {
2525 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2528 for (j = 0; j < ELEMENTSOF(table); j++)
2529 if (p->parse == table[j].callback) {
2530 rvalue = table[j].rvalue;
2534 fprintf(f, "%s=%s\n", lvalue, rvalue);