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);
1868 static void syscall_set(uint32_t *p, int nr) {
1869 nr = SYSCALL_TO_INDEX(nr);
1870 p[nr >> 4] |= 1 << (nr & 31);
1873 static void syscall_unset(uint32_t *p, int nr) {
1874 nr = SYSCALL_TO_INDEX(nr);
1875 p[nr >> 4] &= ~(1 << (nr & 31));
1878 int config_parse_syscall_filter(const char *unit,
1879 const char *filename,
1881 const char *section,
1888 ExecContext *c = data;
1890 bool invert = false;
1900 if (isempty(rvalue)) {
1901 /* Empty assignment resets the list */
1902 free(c->syscall_filter);
1903 c->syscall_filter = NULL;
1907 if (rvalue[0] == '~') {
1912 if (!c->syscall_filter) {
1915 n = (syscall_max() + 31) >> 4;
1916 c->syscall_filter = new(uint32_t, n);
1917 if (!c->syscall_filter)
1920 memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
1922 /* Add these by default */
1923 syscall_set(c->syscall_filter, __NR_execve);
1924 syscall_set(c->syscall_filter, __NR_rt_sigreturn);
1925 #ifdef __NR_sigreturn
1926 syscall_set(c->syscall_filter, __NR_sigreturn);
1928 syscall_set(c->syscall_filter, __NR_exit_group);
1929 syscall_set(c->syscall_filter, __NR_exit);
1932 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1934 _cleanup_free_ char *t = NULL;
1940 id = syscall_from_name(t);
1942 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1943 "Failed to parse syscall, ignoring: %s", t);
1948 syscall_unset(c->syscall_filter, id);
1950 syscall_set(c->syscall_filter, id);
1953 c->no_new_privileges = true;
1958 int config_parse_unit_slice(
1960 const char *filename,
1962 const char *section,
1969 _cleanup_free_ char *k = NULL;
1970 Unit *u = userdata, *slice;
1978 r = unit_name_printf(u, rvalue, &k);
1980 log_syntax(unit, LOG_ERR, filename, line, -r,
1981 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
1988 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
1990 log_syntax(unit, LOG_ERR, filename, line, -r,
1991 "Failed to load slice unit %s. Ignoring.", k);
1995 if (slice->type != UNIT_SLICE) {
1996 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1997 "Slice unit %s is not a slice. Ignoring.", k);
2001 unit_ref_set(&u->slice, slice);
2005 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2007 int config_parse_cpu_shares(
2009 const char *filename,
2011 const char *section,
2018 CGroupContext *c = data;
2026 if (isempty(rvalue)) {
2027 c->cpu_shares = 1024;
2031 r = safe_atolu(rvalue, &lu);
2032 if (r < 0 || lu <= 0) {
2033 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2034 "CPU shares '%s' invalid. Ignoring.", rvalue);
2042 int config_parse_memory_limit(
2044 const char *filename,
2046 const char *section,
2053 CGroupContext *c = data;
2057 if (isempty(rvalue)) {
2058 c->memory_limit = (uint64_t) -1;
2062 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2064 r = parse_bytes(rvalue, &bytes);
2066 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2067 "Memory limit '%s' invalid. Ignoring.", rvalue);
2071 c->memory_limit = (uint64_t) bytes;
2075 int config_parse_device_allow(
2077 const char *filename,
2079 const char *section,
2086 _cleanup_free_ char *path = NULL;
2087 CGroupContext *c = data;
2088 CGroupDeviceAllow *a;
2092 if (isempty(rvalue)) {
2093 while (c->device_allow)
2094 cgroup_context_free_device_allow(c, c->device_allow);
2099 n = strcspn(rvalue, WHITESPACE);
2100 path = strndup(rvalue, n);
2104 if (!path_startswith(path, "/dev")) {
2105 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2106 "Invalid device node path '%s'. Ignoring.", path);
2110 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2114 if (!in_charset(m, "rwm")) {
2115 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2116 "Invalid device rights '%s'. Ignoring.", m);
2120 a = new0(CGroupDeviceAllow, 1);
2126 a->r = !!strchr(m, 'r');
2127 a->w = !!strchr(m, 'w');
2128 a->m = !!strchr(m, 'm');
2130 LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
2134 int config_parse_blockio_weight(
2136 const char *filename,
2138 const char *section,
2145 CGroupContext *c = data;
2153 if (isempty(rvalue)) {
2154 c->blockio_weight = 1000;
2158 r = safe_atolu(rvalue, &lu);
2159 if (r < 0 || lu < 10 || lu > 1000) {
2160 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2161 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2165 c->blockio_weight = lu;
2170 int config_parse_blockio_device_weight(
2172 const char *filename,
2174 const char *section,
2181 _cleanup_free_ char *path = NULL;
2182 CGroupBlockIODeviceWeight *w;
2183 CGroupContext *c = data;
2193 if (isempty(rvalue)) {
2194 while (c->blockio_device_weights)
2195 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2200 n = strcspn(rvalue, WHITESPACE);
2201 weight = rvalue + n;
2203 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2204 "Expected block device and device weight. Ignoring.");
2208 path = strndup(rvalue, n);
2212 if (!path_startswith(path, "/dev")) {
2213 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2214 "Invalid device node path '%s'. Ignoring.", path);
2218 weight += strspn(weight, WHITESPACE);
2219 r = safe_atolu(weight, &lu);
2220 if (r < 0 || lu < 10 || lu > 1000) {
2221 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2222 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2227 w = new0(CGroupBlockIODeviceWeight, 1);
2236 LIST_PREPEND(CGroupBlockIODeviceWeight, device_weights, c->blockio_device_weights, w);
2240 int config_parse_blockio_bandwidth(
2242 const char *filename,
2244 const char *section,
2251 _cleanup_free_ char *path = NULL;
2252 CGroupBlockIODeviceBandwidth *b;
2253 CGroupContext *c = data;
2254 const char *bandwidth;
2264 read = streq("BlockIOReadBandwidth", lvalue);
2266 if (isempty(rvalue)) {
2267 CGroupBlockIODeviceBandwidth *next;
2269 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2270 if (b->read == read)
2271 cgroup_context_free_blockio_device_bandwidth(c, b);
2276 n = strcspn(rvalue, WHITESPACE);
2277 bandwidth = rvalue + n;
2278 bandwidth += strspn(bandwidth, WHITESPACE);
2281 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2282 "Expected space separated pair of device node and bandwidth. Ignoring.");
2286 path = strndup(rvalue, n);
2290 if (!path_startswith(path, "/dev")) {
2291 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2292 "Invalid device node path '%s'. Ignoring.", path);
2296 r = parse_bytes(bandwidth, &bytes);
2297 if (r < 0 || bytes <= 0) {
2298 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2299 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2303 b = new0(CGroupBlockIODeviceBandwidth, 1);
2309 b->bandwidth = (uint64_t) bytes;
2312 LIST_PREPEND(CGroupBlockIODeviceBandwidth, device_bandwidths, c->blockio_device_bandwidths, b);
2317 #define FOLLOW_MAX 8
2319 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2330 /* This will update the filename pointer if the loaded file is
2331 * reached by a symlink. The old string will be freed. */
2334 char *target, *name;
2336 if (c++ >= FOLLOW_MAX)
2339 path_kill_slashes(*filename);
2341 /* Add the file name we are currently looking at to
2342 * the names of this unit, but only if it is a valid
2344 name = path_get_file_name(*filename);
2346 if (unit_name_is_valid(name, true)) {
2348 id = set_get(names, name);
2354 r = set_consume(names, id);
2360 /* Try to open the file name, but don't if its a symlink */
2361 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2368 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2369 r = readlink_and_make_absolute(*filename, &target);
2377 f = fdopen(fd, "re");
2380 close_nointr_nofail(fd);
2389 static int merge_by_names(Unit **u, Set *names, const char *id) {
2397 /* Let's try to add in all symlink names we found */
2398 while ((k = set_steal_first(names))) {
2400 /* First try to merge in the other name into our
2402 r = unit_merge_by_name(*u, k);
2406 /* Hmm, we couldn't merge the other unit into
2407 * ours? Then let's try it the other way
2410 other = manager_get_unit((*u)->manager, k);
2414 r = unit_merge(other, *u);
2417 return merge_by_names(u, names, NULL);
2425 unit_choose_id(*u, id);
2433 static int load_from_path(Unit *u, const char *path) {
2437 char *filename = NULL, *id = NULL;
2444 symlink_names = set_new(string_hash_func, string_compare_func);
2448 if (path_is_absolute(path)) {
2450 filename = strdup(path);
2456 r = open_follow(&filename, &f, symlink_names, &id);
2468 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
2470 /* Instead of opening the path right away, we manually
2471 * follow all symlinks and add their name to our unit
2472 * name set while doing so */
2473 filename = path_make_absolute(path, *p);
2479 if (u->manager->unit_path_cache &&
2480 !set_get(u->manager->unit_path_cache, filename))
2483 r = open_follow(&filename, &f, symlink_names, &id);
2492 /* Empty the symlink names for the next run */
2493 set_clear_free(symlink_names);
2502 /* Hmm, no suitable file found? */
2508 r = merge_by_names(&merged, symlink_names, id);
2513 u->load_state = UNIT_MERGED;
2518 if (fstat(fileno(f), &st) < 0) {
2523 if (null_or_empty(&st))
2524 u->load_state = UNIT_MASKED;
2526 u->load_state = UNIT_LOADED;
2528 /* Now, parse the file contents */
2529 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
2530 config_item_perf_lookup,
2531 (void*) load_fragment_gperf_lookup, false, true, u);
2536 free(u->fragment_path);
2537 u->fragment_path = filename;
2540 u->fragment_mtime = timespec_load(&st.st_mtim);
2542 if (u->source_path) {
2543 if (stat(u->source_path, &st) >= 0)
2544 u->source_mtime = timespec_load(&st.st_mtim);
2546 u->source_mtime = 0;
2552 set_free_free(symlink_names);
2561 int unit_load_fragment(Unit *u) {
2567 assert(u->load_state == UNIT_STUB);
2570 /* First, try to find the unit under its id. We always look
2571 * for unit files in the default directories, to make it easy
2572 * to override things by placing things in /etc/systemd/system */
2573 r = load_from_path(u, u->id);
2577 /* Try to find an alias we can load this with */
2578 if (u->load_state == UNIT_STUB)
2579 SET_FOREACH(t, u->names, i) {
2584 r = load_from_path(u, t);
2588 if (u->load_state != UNIT_STUB)
2592 /* And now, try looking for it under the suggested (originally linked) path */
2593 if (u->load_state == UNIT_STUB && u->fragment_path) {
2595 r = load_from_path(u, u->fragment_path);
2599 if (u->load_state == UNIT_STUB) {
2600 /* Hmm, this didn't work? Then let's get rid
2601 * of the fragment path stored for us, so that
2602 * we don't point to an invalid location. */
2603 free(u->fragment_path);
2604 u->fragment_path = NULL;
2608 /* Look for a template */
2609 if (u->load_state == UNIT_STUB && u->instance) {
2612 k = unit_name_template(u->id);
2616 r = load_from_path(u, k);
2622 if (u->load_state == UNIT_STUB)
2623 SET_FOREACH(t, u->names, i) {
2628 k = unit_name_template(t);
2632 r = load_from_path(u, k);
2638 if (u->load_state != UNIT_STUB)
2646 void unit_dump_config_items(FILE *f) {
2647 static const struct {
2648 const ConfigParserCallback callback;
2651 { config_parse_int, "INTEGER" },
2652 { config_parse_unsigned, "UNSIGNED" },
2653 { config_parse_bytes_size, "SIZE" },
2654 { config_parse_bool, "BOOLEAN" },
2655 { config_parse_string, "STRING" },
2656 { config_parse_path, "PATH" },
2657 { config_parse_unit_path_printf, "PATH" },
2658 { config_parse_strv, "STRING [...]" },
2659 { config_parse_exec_nice, "NICE" },
2660 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2661 { config_parse_exec_io_class, "IOCLASS" },
2662 { config_parse_exec_io_priority, "IOPRIORITY" },
2663 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2664 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2665 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2666 { config_parse_mode, "MODE" },
2667 { config_parse_unit_env_file, "FILE" },
2668 { config_parse_output, "OUTPUT" },
2669 { config_parse_input, "INPUT" },
2670 { config_parse_facility, "FACILITY" },
2671 { config_parse_level, "LEVEL" },
2672 { config_parse_exec_capabilities, "CAPABILITIES" },
2673 { config_parse_exec_secure_bits, "SECUREBITS" },
2674 { config_parse_bounding_set, "BOUNDINGSET" },
2675 { config_parse_limit, "LIMIT" },
2676 { config_parse_unit_deps, "UNIT [...]" },
2677 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2678 { config_parse_service_type, "SERVICETYPE" },
2679 { config_parse_service_restart, "SERVICERESTART" },
2680 #ifdef HAVE_SYSV_COMPAT
2681 { config_parse_sysv_priority, "SYSVPRIORITY" },
2683 { config_parse_warn_compat, "NOTSUPPORTED" },
2685 { config_parse_kill_mode, "KILLMODE" },
2686 { config_parse_kill_signal, "SIGNAL" },
2687 { config_parse_socket_listen, "SOCKET [...]" },
2688 { config_parse_socket_bind, "SOCKETBIND" },
2689 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
2690 { config_parse_sec, "SECONDS" },
2691 { config_parse_nsec, "NANOSECONDS" },
2692 { config_parse_path_strv, "PATH [...]" },
2693 { config_parse_unit_requires_mounts_for, "PATH [...]" },
2694 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2695 { config_parse_unit_string_printf, "STRING" },
2696 { config_parse_trigger_unit, "UNIT" },
2697 { config_parse_timer, "TIMER" },
2698 { config_parse_path_spec, "PATH" },
2699 { config_parse_notify_access, "ACCESS" },
2700 { config_parse_ip_tos, "TOS" },
2701 { config_parse_unit_condition_path, "CONDITION" },
2702 { config_parse_unit_condition_string, "CONDITION" },
2703 { config_parse_unit_condition_null, "CONDITION" },
2704 { config_parse_unit_slice, "SLICE" },
2705 { config_parse_documentation, "URL" },
2706 { config_parse_service_timeout, "SECONDS" },
2707 { config_parse_start_limit_action, "ACTION" },
2708 { config_parse_set_status, "STATUS" },
2709 { config_parse_service_sockets, "SOCKETS" },
2710 { config_parse_fsck_passno, "PASSNO" },
2711 { config_parse_environ, "ENVIRON" },
2712 { config_parse_syscall_filter, "SYSCALL" },
2713 { config_parse_cpu_shares, "SHARES" },
2714 { config_parse_memory_limit, "LIMIT" },
2715 { config_parse_device_allow, "DEVICE" },
2716 { config_parse_device_policy, "POLICY" },
2717 { config_parse_blockio_bandwidth, "BANDWIDTH" },
2718 { config_parse_blockio_weight, "WEIGHT" },
2719 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
2720 { config_parse_long, "LONG" },
2721 { config_parse_socket_service, "SERVICE" },
2724 const char *prev = NULL;
2729 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2730 const char *rvalue = "OTHER", *lvalue;
2734 const ConfigPerfItem *p;
2736 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2738 dot = strchr(i, '.');
2739 lvalue = dot ? dot + 1 : i;
2743 if (!prev || !strneq(prev, i, prefix_len+1)) {
2747 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2750 for (j = 0; j < ELEMENTSOF(table); j++)
2751 if (p->parse == table[j].callback) {
2752 rvalue = table[j].rvalue;
2756 fprintf(f, "%s=%s\n", lvalue, rvalue);