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 "bus-errors.h"
51 #include "path-util.h"
52 #include "syscall-list.h"
55 #ifndef HAVE_SYSV_COMPAT
56 int config_parse_warn_compat(const char *unit,
66 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
67 "Support for option %s= has been disabled at compile time and is ignored",
73 int config_parse_unit_deps(const char* unit,
83 UnitDependency d = ltype;
93 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
94 char _cleanup_free_ *t = NULL, *k = NULL;
101 k = unit_name_printf(u, t);
105 r = unit_add_dependency_by_name(u, d, k, NULL, true);
107 log_syntax(unit, LOG_ERR, filename, line, -r,
108 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
114 int config_parse_unit_string_printf(const char *unit,
115 const char *filename,
125 _cleanup_free_ char *k = NULL;
132 k = unit_full_printf(u, rvalue);
134 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
135 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
137 return config_parse_string(unit, filename, line, section, lvalue, ltype,
138 k ? k : rvalue, data, userdata);
141 int config_parse_unit_strv_printf(const char *unit,
142 const char *filename,
152 _cleanup_free_ char *k = NULL;
159 k = unit_full_printf(u, rvalue);
161 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
162 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
164 return config_parse_strv(unit, filename, line, section, lvalue, ltype,
165 k ? k : rvalue, data, userdata);
168 int config_parse_unit_path_printf(const char *unit,
169 const char *filename,
179 _cleanup_free_ char *k = NULL;
186 k = unit_full_printf(u, rvalue);
188 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
189 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
191 return config_parse_path(unit, filename, line, section, lvalue, ltype,
192 k ? k : rvalue, data, userdata);
195 int config_parse_socket_listen(const char *unit,
196 const char *filename,
205 SocketPort *p, *tail;
215 if (isempty(rvalue)) {
216 /* An empty assignment removes all ports */
217 socket_free_ports(s);
221 p = new0(SocketPort, 1);
225 if (ltype != SOCKET_SOCKET) {
228 p->path = unit_full_printf(UNIT(s), rvalue);
230 p->path = strdup(rvalue);
235 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
236 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
239 path_kill_slashes(p->path);
241 } else if (streq(lvalue, "ListenNetlink")) {
242 _cleanup_free_ char *k = NULL;
245 p->type = SOCKET_SOCKET;
246 k = unit_full_printf(UNIT(s), rvalue);
248 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
249 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
251 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
253 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
254 "Failed to parse address value, ignoring: %s", rvalue);
260 _cleanup_free_ char *k = NULL;
263 p->type = SOCKET_SOCKET;
264 k = unit_full_printf(UNIT(s), rvalue);
266 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
267 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
269 r = socket_address_parse(&p->address, k ? k : rvalue);
271 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
272 "Failed to parse address value, ignoring: %s", rvalue);
277 if (streq(lvalue, "ListenStream"))
278 p->address.type = SOCK_STREAM;
279 else if (streq(lvalue, "ListenDatagram"))
280 p->address.type = SOCK_DGRAM;
282 assert(streq(lvalue, "ListenSequentialPacket"));
283 p->address.type = SOCK_SEQPACKET;
286 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
287 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
288 "Address family not supported, ignoring: %s", rvalue);
297 LIST_FIND_TAIL(SocketPort, port, s->ports, tail);
298 LIST_INSERT_AFTER(SocketPort, port, s->ports, tail, p);
300 LIST_PREPEND(SocketPort, port, s->ports, p);
305 int config_parse_socket_bind(const char *unit,
306 const char *filename,
316 SocketAddressBindIPv6Only b;
325 b = socket_address_bind_ipv6_only_from_string(rvalue);
329 r = parse_boolean(rvalue);
331 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
332 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
336 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
338 s->bind_ipv6_only = b;
343 int config_parse_exec_nice(const char *unit,
344 const char *filename,
353 ExecContext *c = data;
361 r = safe_atoi(rvalue, &priority);
363 log_syntax(unit, LOG_ERR, filename, line, -r,
364 "Failed to parse nice priority, ignoring: %s. ", rvalue);
368 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
369 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
370 "Nice priority out of range, ignoring: %s", rvalue);
380 int config_parse_exec_oom_score_adjust(const char* unit,
381 const char *filename,
390 ExecContext *c = data;
398 r = safe_atoi(rvalue, &oa);
400 log_syntax(unit, LOG_ERR, filename, line, -r,
401 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
405 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
406 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
407 "OOM score adjust value out of range, ignoring: %s", rvalue);
411 c->oom_score_adjust = oa;
412 c->oom_score_adjust_set = true;
417 int config_parse_exec(const char *unit,
418 const char *filename,
427 ExecCommand **e = data, *nce;
439 if (isempty(rvalue)) {
440 /* An empty assignment resets the list */
441 exec_command_free_list(*e);
446 /* We accept an absolute path as first argument, or
447 * alternatively an absolute prefixed with @ to allow
448 * overriding of argv[0]. */
454 bool honour_argv0 = false, ignore = false;
460 rvalue += strspn(rvalue, WHITESPACE);
465 for (i = 0; i < 2; i++) {
466 if (rvalue[0] == '-' && !ignore) {
471 if (rvalue[0] == '@' && !honour_argv0) {
477 if (*rvalue != '/') {
478 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
479 "Executable path is not absolute, ignoring: %s", rvalue);
484 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
485 if (strneq(w, ";", MAX(l, 1U)))
491 n = new(char*, k + !honour_argv0);
496 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
497 if (strneq(w, ";", MAX(l, 1U)))
499 else if (strneq(w, "\\;", MAX(l, 1U)))
502 if (honour_argv0 && w == rvalue) {
505 path = strndup(w, l);
511 if (!utf8_is_valid(path)) {
512 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
513 "Path is not UTF-8 clean, ignoring assignment: %s",
522 c = n[k++] = cunescape_length(w, l);
528 if (!utf8_is_valid(c)) {
529 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
530 "Path is not UTF-8 clean, ignoring assignment: %s",
541 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
542 "Invalid command line, ignoring: %s", rvalue);
555 assert(path_is_absolute(path));
557 nce = new0(ExecCommand, 1);
565 nce->ignore = ignore;
567 path_kill_slashes(nce->path);
569 exec_command_append_list(e, nce);
585 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
586 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
588 int config_parse_socket_bindtodevice(const char* unit,
589 const char *filename,
606 if (rvalue[0] && !streq(rvalue, "*")) {
613 free(s->bind_to_device);
614 s->bind_to_device = n;
619 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
620 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
622 int config_parse_exec_io_class(const char *unit,
623 const char *filename,
632 ExecContext *c = data;
640 x = ioprio_class_from_string(rvalue);
642 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
643 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
647 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
648 c->ioprio_set = true;
653 int config_parse_exec_io_priority(const char *unit,
654 const char *filename,
663 ExecContext *c = data;
671 r = safe_atoi(rvalue, &i);
672 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
673 log_syntax(unit, LOG_ERR, filename, line, -r,
674 "Failed to parse IO priority, ignoring: %s", rvalue);
678 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
679 c->ioprio_set = true;
684 int config_parse_exec_cpu_sched_policy(const char *unit,
685 const char *filename,
695 ExecContext *c = data;
703 x = sched_policy_from_string(rvalue);
705 log_syntax(unit, LOG_ERR, filename, line, -x,
706 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
710 c->cpu_sched_policy = x;
711 /* Moving to or from real-time policy? We need to adjust the priority */
712 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
713 c->cpu_sched_set = true;
718 int config_parse_exec_cpu_sched_prio(const char *unit,
719 const char *filename,
728 ExecContext *c = data;
736 r = safe_atoi(rvalue, &i);
738 log_syntax(unit, LOG_ERR, filename, line, -r,
739 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
743 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
744 min = sched_get_priority_min(c->cpu_sched_policy);
745 max = sched_get_priority_max(c->cpu_sched_policy);
747 if (i < min || i > max) {
748 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
749 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
753 c->cpu_sched_priority = i;
754 c->cpu_sched_set = true;
759 int config_parse_exec_cpu_affinity(const char *unit,
760 const char *filename,
769 ExecContext *c = data;
779 if (isempty(rvalue)) {
780 /* An empty assignment resets the CPU list */
787 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
788 char _cleanup_free_ *t = NULL;
796 r = safe_atou(t, &cpu);
799 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
804 if (r < 0 || cpu >= c->cpuset_ncpus) {
805 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
806 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
810 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
816 int config_parse_exec_capabilities(const char *unit,
817 const char *filename,
826 ExecContext *c = data;
834 cap = cap_from_text(rvalue);
836 log_syntax(unit, LOG_ERR, filename, line, errno,
837 "Failed to parse capabilities, ignoring: %s", rvalue);
842 cap_free(c->capabilities);
843 c->capabilities = cap;
848 int config_parse_exec_secure_bits(const char *unit,
849 const char *filename,
858 ExecContext *c = data;
868 if (isempty(rvalue)) {
869 /* An empty assignment resets the field */
874 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
875 if (first_word(w, "keep-caps"))
876 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
877 else if (first_word(w, "keep-caps-locked"))
878 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
879 else if (first_word(w, "no-setuid-fixup"))
880 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
881 else if (first_word(w, "no-setuid-fixup-locked"))
882 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
883 else if (first_word(w, "noroot"))
884 c->secure_bits |= 1<<SECURE_NOROOT;
885 else if (first_word(w, "noroot-locked"))
886 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
888 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
889 "Failed to parse secure bits, ignoring: %s", rvalue);
897 int config_parse_bounding_set(const char *unit,
898 const char *filename,
907 uint64_t *capability_bounding_set_drop = data;
919 if (rvalue[0] == '~') {
924 /* Note that we store this inverted internally, since the
925 * kernel wants it like this. But we actually expose it
926 * non-inverted everywhere to have a fully normalized
929 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
930 char _cleanup_free_ *t = NULL;
938 r = cap_from_name(t, &cap);
940 log_syntax(unit, LOG_ERR, filename, line, errno,
941 "Failed to parse capability in bounding set, ignoring: %s", t);
945 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
949 *capability_bounding_set_drop |= sum;
951 *capability_bounding_set_drop |= ~sum;
956 int config_parse_limit(const char *unit,
957 const char *filename,
966 struct rlimit **rl = data;
967 unsigned long long u;
976 if (streq(rvalue, "infinity"))
977 u = (unsigned long long) RLIM_INFINITY;
981 r = safe_atollu(rvalue, &u);
983 log_syntax(unit, LOG_ERR, filename, line, -r,
984 "Failed to parse resource value, ignoring: %s", rvalue);
990 *rl = new(struct rlimit, 1);
995 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
999 int config_parse_unit_cgroup(const char *unit,
1000 const char *filename,
1002 const char *section,
1014 if (isempty(rvalue)) {
1015 /* An empty assignment resets the list */
1016 cgroup_bonding_free_list(u->cgroup_bondings, false);
1017 u->cgroup_bondings = NULL;
1021 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1022 char _cleanup_free_ *t = NULL, *k = NULL, *ku = NULL;
1029 k = unit_full_printf(u, t);
1031 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1032 "Failed to resolve unit specifiers on %s. Ignoring.",
1035 ku = cunescape(k ? k : t);
1039 r = unit_add_cgroup_from_text(u, ku, true, NULL);
1041 log_syntax(unit, LOG_ERR, filename, line, -r,
1042 "Failed to parse cgroup value %s, ignoring: %s",
1051 #ifdef HAVE_SYSV_COMPAT
1052 int config_parse_sysv_priority(const char *unit,
1053 const char *filename,
1055 const char *section,
1062 int *priority = data;
1070 r = safe_atoi(rvalue, &i);
1071 if (r < 0 || i < 0) {
1072 log_syntax(unit, LOG_ERR, filename, line, -r,
1073 "Failed to parse SysV start priority, ignoring: %s", rvalue);
1077 *priority = (int) i;
1082 int config_parse_fsck_passno(const char *unit,
1083 const char *filename,
1085 const char *section,
1100 r = safe_atoi(rvalue, &i);
1102 log_syntax(unit, LOG_ERR, filename, line, -r,
1103 "Failed to parse fsck pass number, ignoring: %s", rvalue);
1111 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1113 int config_parse_kill_signal(const char *unit,
1114 const char *filename,
1116 const char *section,
1131 r = signal_from_string_try_harder(rvalue);
1133 log_syntax(unit, LOG_ERR, filename, line, -r,
1134 "Failed to parse kill signal, ignoring: %s", rvalue);
1142 int config_parse_exec_mount_flags(const char *unit,
1143 const char *filename,
1145 const char *section,
1152 ExecContext *c = data;
1156 unsigned long flags = 0;
1163 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
1164 char _cleanup_free_ *t;
1170 if (streq(t, "shared"))
1172 else if (streq(t, "slave"))
1174 else if (streq(w, "private"))
1175 flags |= MS_PRIVATE;
1177 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1178 "Failed to parse mount flag %s, ignoring: %s",
1184 c->mount_flags = flags;
1188 int config_parse_timer(const char *unit,
1189 const char *filename,
1191 const char *section,
1202 CalendarSpec *c = NULL;
1210 if (isempty(rvalue)) {
1211 /* Empty assignment resets list */
1212 timer_free_values(t);
1216 b = timer_base_from_string(lvalue);
1218 log_syntax(unit, LOG_ERR, filename, line, -b,
1219 "Failed to parse timer base, ignoring: %s", lvalue);
1223 if (b == TIMER_CALENDAR) {
1224 if (calendar_spec_from_string(rvalue, &c) < 0) {
1225 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1226 "Failed to parse calendar specification, ignoring: %s",
1231 id = CLOCK_REALTIME;
1233 if (parse_sec(rvalue, &u) < 0) {
1234 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1235 "Failed to parse timer value, ignoring: %s",
1240 id = CLOCK_MONOTONIC;
1243 v = new0(TimerValue, 1);
1250 v->calendar_spec = c;
1252 LIST_PREPEND(TimerValue, value, t->values, v);
1257 int config_parse_timer_unit(const char *unit,
1258 const char *filename,
1260 const char *section,
1271 _cleanup_free_ char *p = NULL;
1278 dbus_error_init(&error);
1280 p = unit_name_printf(UNIT(t), rvalue);
1284 if (endswith(p, ".timer")) {
1285 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1286 "Unit cannot be of type timer, ignoring: %s", rvalue);
1290 r = manager_load_unit(UNIT(t)->manager, p, NULL, NULL, &u);
1292 log_syntax(unit, LOG_ERR, filename, line, -r,
1293 "Failed to load unit %s, ignoring: %s",
1294 rvalue, bus_error(&error, r));
1295 dbus_error_free(&error);
1299 unit_ref_set(&t->unit, u);
1304 int config_parse_path_spec(const char *unit,
1305 const char *filename,
1307 const char *section,
1324 if (isempty(rvalue)) {
1325 /* Empty assignment clears list */
1330 b = path_type_from_string(lvalue);
1332 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1333 "Failed to parse path type, ignoring: %s", lvalue);
1337 k = unit_full_printf(UNIT(p), rvalue);
1343 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1344 "Failed to resolve unit specifiers on %s. Ignoring.",
1348 if (!path_is_absolute(k)) {
1349 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1350 "Path is not absolute, ignoring: %s", k);
1355 s = new0(PathSpec, 1);
1361 s->path = path_kill_slashes(k);
1365 LIST_PREPEND(PathSpec, spec, p->specs, s);
1370 int config_parse_path_unit(const char *unit,
1371 const char *filename,
1373 const char *section,
1384 _cleanup_free_ char *p = NULL;
1391 dbus_error_init(&error);
1393 p = unit_name_printf(UNIT(t), rvalue);
1397 if (endswith(p, ".path")) {
1398 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1399 "Unit cannot be of type path, ignoring: %s", p);
1403 r = manager_load_unit(UNIT(t)->manager, p, NULL, &error, &u);
1405 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1406 "Failed to load unit %s, ignoring: %s",
1407 p, bus_error(&error, r));
1408 dbus_error_free(&error);
1412 unit_ref_set(&t->unit, u);
1417 int config_parse_socket_service(const char *unit,
1418 const char *filename,
1420 const char *section,
1431 _cleanup_free_ char *p = NULL;
1438 dbus_error_init(&error);
1440 p = unit_name_printf(UNIT(s), rvalue);
1444 if (!endswith(p, ".service")) {
1445 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1446 "Unit must be of type service, ignoring: %s", rvalue);
1450 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1452 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1453 "Failed to load unit %s, ignoring: %s",
1454 rvalue, bus_error(&error, r));
1455 dbus_error_free(&error);
1459 unit_ref_set(&s->service, x);
1464 int config_parse_service_sockets(const char *unit,
1465 const char *filename,
1467 const char *section,
1484 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1485 char _cleanup_free_ *t = NULL, *k = NULL;
1491 k = unit_name_printf(UNIT(s), t);
1495 if (!endswith(k, ".socket")) {
1496 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1497 "Unit must be of type socket, ignoring: %s", k);
1501 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
1503 log_syntax(unit, LOG_ERR, filename, line, -r,
1504 "Failed to add dependency on %s, ignoring: %s",
1507 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
1515 int config_parse_service_timeout(const char *unit,
1516 const char *filename,
1518 const char *section,
1525 Service *s = userdata;
1533 r = config_parse_sec(unit, filename, line, section, lvalue, ltype,
1534 rvalue, data, userdata);
1538 if (streq(lvalue, "TimeoutSec")) {
1539 s->start_timeout_defined = true;
1540 s->timeout_stop_usec = s->timeout_start_usec;
1541 } else if (streq(lvalue, "TimeoutStartSec"))
1542 s->start_timeout_defined = true;
1547 int config_parse_unit_env_file(const char *unit,
1548 const char *filename,
1550 const char *section,
1559 _cleanup_free_ char *s = NULL;
1567 if (isempty(rvalue)) {
1568 /* Empty assignment frees the list */
1574 s = unit_full_printf(u, rvalue);
1578 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1579 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1580 "Path '%s' is not absolute, ignoring.", s);
1584 r = strv_extend(env, s);
1591 int config_parse_environ(const char *unit,
1592 const char *filename,
1594 const char *section,
1602 char*** env = data, *w, *state;
1604 _cleanup_free_ char *k = NULL;
1611 if (isempty(rvalue)) {
1612 /* Empty assignment resets the list */
1618 k = unit_full_printf(u, rvalue);
1622 FOREACH_WORD_QUOTED(w, l, k, state) {
1623 _cleanup_free_ char *n;
1626 n = cunescape_length(w, l);
1630 if (!env_assignment_is_valid(n)) {
1631 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1632 "Invalid environment assignment, ignoring: %s", rvalue);
1636 x = strv_env_set(*env, n);
1647 int config_parse_ip_tos(const char *unit,
1648 const char *filename,
1650 const char *section,
1657 int *ip_tos = data, x;
1664 x = ip_tos_from_string(rvalue);
1666 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1667 "Failed to parse IP TOS value, ignoring: %s", rvalue);
1675 int config_parse_unit_condition_path(const char *unit,
1676 const char *filename,
1678 const char *section,
1685 ConditionType cond = ltype;
1687 bool trigger, negate;
1689 _cleanup_free_ char *p = NULL;
1696 if (isempty(rvalue)) {
1697 /* Empty assignment resets the list */
1698 condition_free_list(u->conditions);
1699 u->conditions = NULL;
1703 trigger = rvalue[0] == '|';
1707 negate = rvalue[0] == '!';
1711 p = unit_full_printf(u, rvalue);
1715 if (!path_is_absolute(p)) {
1716 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1717 "Path in condition not absolute, ignoring: %s", p);
1721 c = condition_new(cond, p, trigger, negate);
1725 LIST_PREPEND(Condition, conditions, u->conditions, c);
1729 int config_parse_unit_condition_string(const char *unit,
1730 const char *filename,
1732 const char *section,
1739 ConditionType cond = ltype;
1741 bool trigger, negate;
1743 _cleanup_free_ char *s = NULL;
1750 if (isempty(rvalue)) {
1751 /* Empty assignment resets the list */
1752 condition_free_list(u->conditions);
1753 u->conditions = NULL;
1757 trigger = rvalue[0] == '|';
1761 negate = rvalue[0] == '!';
1765 s = unit_full_printf(u, rvalue);
1769 c = condition_new(cond, s, trigger, negate);
1773 LIST_PREPEND(Condition, conditions, u->conditions, c);
1777 int config_parse_unit_condition_null(const char *unit,
1778 const char *filename,
1780 const char *section,
1789 bool trigger, negate;
1797 if (isempty(rvalue)) {
1798 /* Empty assignment resets the list */
1799 condition_free_list(u->conditions);
1800 u->conditions = NULL;
1804 trigger = rvalue[0] == '|';
1808 negate = rvalue[0] == '!';
1812 b = parse_boolean(rvalue);
1814 log_syntax(unit, LOG_ERR, filename, line, -b,
1815 "Failed to parse boolean value in condition, ignoring: %s",
1823 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1827 LIST_PREPEND(Condition, conditions, u->conditions, c);
1831 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1832 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
1834 int config_parse_unit_cgroup_attr(const char *unit,
1835 const char *filename,
1837 const char *section,
1846 _cleanup_free_ char *n = NULL, *v = NULL;
1847 const CGroupSemantics *s;
1855 if (isempty(rvalue)) {
1856 /* Empty assignment clears the list */
1857 cgroup_attribute_free_list(u->cgroup_attributes);
1858 u->cgroup_attributes = NULL;
1862 a = strcspn(rvalue, WHITESPACE);
1863 b = strspn(rvalue + a, WHITESPACE);
1864 if (a <= 0 || b <= 0) {
1865 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1866 "Failed to parse cgroup attribute value, ignoring: %s",
1871 n = strndup(rvalue, a);
1875 r = cgroup_semantics_find(NULL, n, rvalue + a + b, &v, &s);
1877 log_syntax(unit, LOG_ERR, filename, line, -r,
1878 "Failed to parse cgroup attribute value, ignoring: %s",
1883 r = unit_add_cgroup_attribute(u, s, NULL, n, v ? v : rvalue + a + b, NULL);
1885 log_syntax(unit, LOG_ERR, filename, line, -r,
1886 "Failed to add cgroup attribute value, ignoring: %s", rvalue);
1893 int config_parse_unit_cgroup_attr_pretty(const char *unit,
1894 const char *filename,
1896 const char *section,
1904 _cleanup_free_ char *v = NULL;
1905 const CGroupSemantics *s;
1913 r = cgroup_semantics_find(NULL, lvalue, rvalue, &v, &s);
1915 log_syntax(unit, LOG_ERR, filename, line, -r,
1916 "Failed to parse cgroup attribute value, ignoring: %s",
1919 } else if (r == 0) {
1920 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
1921 "Unknown or unsupported cgroup attribute %s, ignoring: %s",
1926 r = unit_add_cgroup_attribute(u, s, NULL, NULL, v, NULL);
1928 log_syntax(unit, LOG_ERR, filename, line, -r,
1929 "Failed to add cgroup attribute value, ignoring: %s", rvalue);
1936 int config_parse_unit_requires_mounts_for(const char *unit,
1937 const char *filename,
1939 const char *section,
1955 empty_before = !u->requires_mounts_for;
1957 r = config_parse_path_strv(unit, filename, line, section, lvalue, ltype,
1958 rvalue, data, userdata);
1960 /* Make it easy to find units with requires_mounts set */
1961 if (empty_before && u->requires_mounts_for)
1962 LIST_PREPEND(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
1967 int config_parse_documentation(const char *unit,
1968 const char *filename,
1970 const char *section,
1986 if (isempty(rvalue)) {
1987 /* Empty assignment resets the list */
1988 strv_free(u->documentation);
1989 u->documentation = NULL;
1993 r = config_parse_unit_strv_printf(unit, filename, line, section, lvalue, ltype,
1994 rvalue, data, userdata);
1998 for (a = b = u->documentation; a && *a; a++) {
2000 if (is_valid_documentation_url(*a))
2003 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2004 "Invalid URL, ignoring: %s", *a);
2013 static void syscall_set(uint32_t *p, int nr) {
2014 nr = SYSCALL_TO_INDEX(nr);
2015 p[nr >> 4] |= 1 << (nr & 31);
2018 static void syscall_unset(uint32_t *p, int nr) {
2019 nr = SYSCALL_TO_INDEX(nr);
2020 p[nr >> 4] &= ~(1 << (nr & 31));
2023 int config_parse_syscall_filter(const char *unit,
2024 const char *filename,
2026 const char *section,
2033 ExecContext *c = data;
2035 bool invert = false;
2045 if (isempty(rvalue)) {
2046 /* Empty assignment resets the list */
2047 free(c->syscall_filter);
2048 c->syscall_filter = NULL;
2052 if (rvalue[0] == '~') {
2057 if (!c->syscall_filter) {
2060 n = (syscall_max() + 31) >> 4;
2061 c->syscall_filter = new(uint32_t, n);
2062 if (!c->syscall_filter)
2065 memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
2067 /* Add these by default */
2068 syscall_set(c->syscall_filter, __NR_execve);
2069 syscall_set(c->syscall_filter, __NR_rt_sigreturn);
2070 #ifdef __NR_sigreturn
2071 syscall_set(c->syscall_filter, __NR_sigreturn);
2073 syscall_set(c->syscall_filter, __NR_exit_group);
2074 syscall_set(c->syscall_filter, __NR_exit);
2077 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2079 char _cleanup_free_ *t = NULL;
2085 id = syscall_from_name(t);
2087 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2088 "Failed to parse syscall, ignoring: %s", t);
2093 syscall_unset(c->syscall_filter, id);
2095 syscall_set(c->syscall_filter, id);
2098 c->no_new_privileges = true;
2103 #define FOLLOW_MAX 8
2105 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2116 /* This will update the filename pointer if the loaded file is
2117 * reached by a symlink. The old string will be freed. */
2120 char *target, *name;
2122 if (c++ >= FOLLOW_MAX)
2125 path_kill_slashes(*filename);
2127 /* Add the file name we are currently looking at to
2128 * the names of this unit, but only if it is a valid
2130 name = path_get_file_name(*filename);
2132 if (unit_name_is_valid(name, true)) {
2134 id = set_get(names, name);
2140 r = set_put(names, id);
2148 /* Try to open the file name, but don't if its a symlink */
2149 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2156 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2157 r = readlink_and_make_absolute(*filename, &target);
2165 f = fdopen(fd, "re");
2168 close_nointr_nofail(fd);
2177 static int merge_by_names(Unit **u, Set *names, const char *id) {
2185 /* Let's try to add in all symlink names we found */
2186 while ((k = set_steal_first(names))) {
2188 /* First try to merge in the other name into our
2190 r = unit_merge_by_name(*u, k);
2194 /* Hmm, we couldn't merge the other unit into
2195 * ours? Then let's try it the other way
2198 other = manager_get_unit((*u)->manager, k);
2202 r = unit_merge(other, *u);
2205 return merge_by_names(u, names, NULL);
2213 unit_choose_id(*u, id);
2221 static int load_from_path(Unit *u, const char *path) {
2225 char *filename = NULL, *id = NULL;
2232 symlink_names = set_new(string_hash_func, string_compare_func);
2236 if (path_is_absolute(path)) {
2238 filename = strdup(path);
2244 r = open_follow(&filename, &f, symlink_names, &id);
2256 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
2258 /* Instead of opening the path right away, we manually
2259 * follow all symlinks and add their name to our unit
2260 * name set while doing so */
2261 filename = path_make_absolute(path, *p);
2267 if (u->manager->unit_path_cache &&
2268 !set_get(u->manager->unit_path_cache, filename))
2271 r = open_follow(&filename, &f, symlink_names, &id);
2280 /* Empty the symlink names for the next run */
2281 set_clear_free(symlink_names);
2290 /* Hmm, no suitable file found? */
2296 r = merge_by_names(&merged, symlink_names, id);
2301 u->load_state = UNIT_MERGED;
2306 if (fstat(fileno(f), &st) < 0) {
2311 if (null_or_empty(&st))
2312 u->load_state = UNIT_MASKED;
2314 /* Now, parse the file contents */
2315 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
2316 config_item_perf_lookup,
2317 (void*) load_fragment_gperf_lookup, false, u);
2321 u->load_state = UNIT_LOADED;
2324 free(u->fragment_path);
2325 u->fragment_path = filename;
2328 u->fragment_mtime = timespec_load(&st.st_mtim);
2330 if (u->source_path) {
2331 if (stat(u->source_path, &st) >= 0)
2332 u->source_mtime = timespec_load(&st.st_mtim);
2334 u->source_mtime = 0;
2340 set_free_free(symlink_names);
2349 int unit_load_fragment(Unit *u) {
2355 assert(u->load_state == UNIT_STUB);
2358 /* First, try to find the unit under its id. We always look
2359 * for unit files in the default directories, to make it easy
2360 * to override things by placing things in /etc/systemd/system */
2361 r = load_from_path(u, u->id);
2365 /* Try to find an alias we can load this with */
2366 if (u->load_state == UNIT_STUB)
2367 SET_FOREACH(t, u->names, i) {
2372 r = load_from_path(u, t);
2376 if (u->load_state != UNIT_STUB)
2380 /* And now, try looking for it under the suggested (originally linked) path */
2381 if (u->load_state == UNIT_STUB && u->fragment_path) {
2383 r = load_from_path(u, u->fragment_path);
2387 if (u->load_state == UNIT_STUB) {
2388 /* Hmm, this didn't work? Then let's get rid
2389 * of the fragment path stored for us, so that
2390 * we don't point to an invalid location. */
2391 free(u->fragment_path);
2392 u->fragment_path = NULL;
2396 /* Look for a template */
2397 if (u->load_state == UNIT_STUB && u->instance) {
2400 k = unit_name_template(u->id);
2404 r = load_from_path(u, k);
2410 if (u->load_state == UNIT_STUB)
2411 SET_FOREACH(t, u->names, i) {
2416 k = unit_name_template(t);
2420 r = load_from_path(u, k);
2426 if (u->load_state != UNIT_STUB)
2434 void unit_dump_config_items(FILE *f) {
2435 static const struct {
2436 const ConfigParserCallback callback;
2439 { config_parse_int, "INTEGER" },
2440 { config_parse_unsigned, "UNSIGNED" },
2441 { config_parse_bytes_size, "SIZE" },
2442 { config_parse_bool, "BOOLEAN" },
2443 { config_parse_string, "STRING" },
2444 { config_parse_path, "PATH" },
2445 { config_parse_unit_path_printf, "PATH" },
2446 { config_parse_strv, "STRING [...]" },
2447 { config_parse_exec_nice, "NICE" },
2448 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2449 { config_parse_exec_io_class, "IOCLASS" },
2450 { config_parse_exec_io_priority, "IOPRIORITY" },
2451 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2452 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2453 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2454 { config_parse_mode, "MODE" },
2455 { config_parse_unit_env_file, "FILE" },
2456 { config_parse_output, "OUTPUT" },
2457 { config_parse_input, "INPUT" },
2458 { config_parse_facility, "FACILITY" },
2459 { config_parse_level, "LEVEL" },
2460 { config_parse_exec_capabilities, "CAPABILITIES" },
2461 { config_parse_exec_secure_bits, "SECUREBITS" },
2462 { config_parse_bounding_set, "BOUNDINGSET" },
2463 { config_parse_limit, "LIMIT" },
2464 { config_parse_unit_cgroup, "CGROUP [...]" },
2465 { config_parse_unit_deps, "UNIT [...]" },
2466 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2467 { config_parse_service_type, "SERVICETYPE" },
2468 { config_parse_service_restart, "SERVICERESTART" },
2469 #ifdef HAVE_SYSV_COMPAT
2470 { config_parse_sysv_priority, "SYSVPRIORITY" },
2472 { config_parse_warn_compat, "NOTSUPPORTED" },
2474 { config_parse_kill_mode, "KILLMODE" },
2475 { config_parse_kill_signal, "SIGNAL" },
2476 { config_parse_socket_listen, "SOCKET [...]" },
2477 { config_parse_socket_bind, "SOCKETBIND" },
2478 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
2479 { config_parse_sec, "SECONDS" },
2480 { config_parse_nsec, "NANOSECONDS" },
2481 { config_parse_path_strv, "PATH [...]" },
2482 { config_parse_unit_requires_mounts_for, "PATH [...]" },
2483 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2484 { config_parse_unit_string_printf, "STRING" },
2485 { config_parse_timer, "TIMER" },
2486 { config_parse_timer_unit, "NAME" },
2487 { config_parse_path_spec, "PATH" },
2488 { config_parse_path_unit, "UNIT" },
2489 { config_parse_notify_access, "ACCESS" },
2490 { config_parse_ip_tos, "TOS" },
2491 { config_parse_unit_condition_path, "CONDITION" },
2492 { config_parse_unit_condition_string, "CONDITION" },
2493 { config_parse_unit_condition_null, "CONDITION" },
2496 const char *prev = NULL;
2501 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2502 const char *rvalue = "OTHER", *lvalue;
2506 const ConfigPerfItem *p;
2508 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2510 dot = strchr(i, '.');
2511 lvalue = dot ? dot + 1 : i;
2515 if (!prev || !strneq(prev, i, prefix_len+1)) {
2519 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2522 for (j = 0; j < ELEMENTSOF(table); j++)
2523 if (p->parse == table[j].callback) {
2524 rvalue = table[j].rvalue;
2528 fprintf(f, "%s=%s\n", lvalue, rvalue);