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>
41 #include "sd-messages.h"
44 #include "conf-parser.h"
45 #include "load-fragment.h"
48 #include "securebits.h"
50 #include "unit-name.h"
51 #include "unit-printf.h"
53 #include "path-util.h"
57 #include "bus-error.h"
58 #include "errno-list.h"
62 #include "seccomp-util.h"
65 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
66 int config_parse_warn_compat(
71 unsigned section_line,
78 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
79 "Support for option %s= has been disabled at compile time and is ignored",
85 int config_parse_unit_deps(const char* unit,
89 unsigned section_line,
96 UnitDependency d = ltype;
106 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
107 _cleanup_free_ char *t = NULL, *k = NULL;
114 r = unit_name_printf(u, t, &k);
116 log_syntax(unit, LOG_ERR, filename, line, -r,
117 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
121 r = unit_add_dependency_by_name(u, d, k, NULL, true);
123 log_syntax(unit, LOG_ERR, filename, line, -r,
124 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
130 int config_parse_unit_string_printf(const char *unit,
131 const char *filename,
134 unsigned section_line,
142 _cleanup_free_ char *k = NULL;
150 r = unit_full_printf(u, rvalue, &k);
152 log_syntax(unit, LOG_ERR, filename, line, -r,
153 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
155 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
156 k ? k : rvalue, data, userdata);
159 int config_parse_unit_strv_printf(const char *unit,
160 const char *filename,
163 unsigned section_line,
171 _cleanup_free_ char *k = NULL;
179 r = unit_full_printf(u, rvalue, &k);
181 log_syntax(unit, LOG_ERR, filename, line, -r,
182 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
184 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
185 k ? k : rvalue, data, userdata);
188 int config_parse_unit_path_printf(const char *unit,
189 const char *filename,
192 unsigned section_line,
200 _cleanup_free_ char *k = NULL;
208 r = unit_full_printf(u, rvalue, &k);
210 log_syntax(unit, LOG_ERR, filename, line, -r,
211 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
213 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype,
214 k ? k : rvalue, data, userdata);
217 int config_parse_socket_listen(const char *unit,
218 const char *filename,
221 unsigned section_line,
228 SocketPort *p, *tail;
239 if (isempty(rvalue)) {
240 /* An empty assignment removes all ports */
241 socket_free_ports(s);
245 p = new0(SocketPort, 1);
249 if (ltype != SOCKET_SOCKET) {
252 r = unit_full_printf(UNIT(s), rvalue, &p->path);
254 p->path = strdup(rvalue);
259 log_syntax(unit, LOG_ERR, filename, line, -r,
260 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
263 path_kill_slashes(p->path);
265 } else if (streq(lvalue, "ListenNetlink")) {
266 _cleanup_free_ char *k = NULL;
268 p->type = SOCKET_SOCKET;
269 r = unit_full_printf(UNIT(s), rvalue, &k);
271 log_syntax(unit, LOG_ERR, filename, line, -r,
272 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
274 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
276 log_syntax(unit, LOG_ERR, filename, line, -r,
277 "Failed to parse address value, ignoring: %s", rvalue);
283 _cleanup_free_ char *k = NULL;
285 p->type = SOCKET_SOCKET;
286 r = unit_full_printf(UNIT(s), rvalue, &k);
288 log_syntax(unit, LOG_ERR, filename, line, -r,
289 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
291 r = socket_address_parse(&p->address, k ? k : rvalue);
293 log_syntax(unit, LOG_ERR, filename, line, -r,
294 "Failed to parse address value, ignoring: %s", rvalue);
299 if (streq(lvalue, "ListenStream"))
300 p->address.type = SOCK_STREAM;
301 else if (streq(lvalue, "ListenDatagram"))
302 p->address.type = SOCK_DGRAM;
304 assert(streq(lvalue, "ListenSequentialPacket"));
305 p->address.type = SOCK_SEQPACKET;
308 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
309 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
310 "Address family not supported, ignoring: %s", rvalue);
320 LIST_FIND_TAIL(port, s->ports, tail);
321 LIST_INSERT_AFTER(port, s->ports, tail, p);
323 LIST_PREPEND(port, s->ports, p);
328 int config_parse_socket_bind(const char *unit,
329 const char *filename,
332 unsigned section_line,
340 SocketAddressBindIPv6Only b;
349 b = socket_address_bind_ipv6_only_from_string(rvalue);
353 r = parse_boolean(rvalue);
355 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
356 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
360 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
362 s->bind_ipv6_only = b;
367 int config_parse_exec_nice(const char *unit,
368 const char *filename,
371 unsigned section_line,
378 ExecContext *c = data;
386 r = safe_atoi(rvalue, &priority);
388 log_syntax(unit, LOG_ERR, filename, line, -r,
389 "Failed to parse nice priority, ignoring: %s. ", rvalue);
393 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
394 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
395 "Nice priority out of range, ignoring: %s", rvalue);
405 int config_parse_exec_oom_score_adjust(const char* unit,
406 const char *filename,
409 unsigned section_line,
416 ExecContext *c = data;
424 r = safe_atoi(rvalue, &oa);
426 log_syntax(unit, LOG_ERR, filename, line, -r,
427 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
431 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
432 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
433 "OOM score adjust value out of range, ignoring: %s", rvalue);
437 c->oom_score_adjust = oa;
438 c->oom_score_adjust_set = true;
443 int config_parse_exec(const char *unit,
444 const char *filename,
447 unsigned section_line,
454 ExecCommand **e = data, *nce;
466 if (isempty(rvalue)) {
467 /* An empty assignment resets the list */
468 exec_command_free_list(*e);
473 /* We accept an absolute path as first argument, or
474 * alternatively an absolute prefixed with @ to allow
475 * overriding of argv[0]. */
481 bool honour_argv0 = false, ignore = false;
487 rvalue += strspn(rvalue, WHITESPACE);
492 for (i = 0; i < 2; i++) {
493 if (rvalue[0] == '-' && !ignore) {
498 if (rvalue[0] == '@' && !honour_argv0) {
504 if (*rvalue != '/') {
505 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
506 "Executable path is not absolute, ignoring: %s", rvalue);
511 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
512 if (strneq(w, ";", MAX(l, 1U)))
518 n = new(char*, k + !honour_argv0);
523 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
524 if (strneq(w, ";", MAX(l, 1U)))
526 else if (strneq(w, "\\;", MAX(l, 1U)))
529 if (honour_argv0 && w == rvalue) {
532 path = strndup(w, l);
538 if (!utf8_is_valid(path)) {
539 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
547 c = n[k++] = cunescape_length(w, l);
553 if (!utf8_is_valid(c)) {
554 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
564 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
565 "Invalid command line, ignoring: %s", rvalue);
578 assert(path_is_absolute(path));
580 nce = new0(ExecCommand, 1);
588 nce->ignore = ignore;
590 path_kill_slashes(nce->path);
592 exec_command_append_list(e, nce);
608 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
609 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
611 int config_parse_socket_bindtodevice(const char* unit,
612 const char *filename,
615 unsigned section_line,
630 if (rvalue[0] && !streq(rvalue, "*")) {
637 free(s->bind_to_device);
638 s->bind_to_device = n;
643 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
644 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
646 int config_parse_exec_io_class(const char *unit,
647 const char *filename,
650 unsigned section_line,
657 ExecContext *c = data;
665 x = ioprio_class_from_string(rvalue);
667 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
668 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
672 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
673 c->ioprio_set = true;
678 int config_parse_exec_io_priority(const char *unit,
679 const char *filename,
682 unsigned section_line,
689 ExecContext *c = data;
697 r = safe_atoi(rvalue, &i);
698 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
699 log_syntax(unit, LOG_ERR, filename, line, -r,
700 "Failed to parse IO priority, ignoring: %s", rvalue);
704 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
705 c->ioprio_set = true;
710 int config_parse_exec_cpu_sched_policy(const char *unit,
711 const char *filename,
714 unsigned section_line,
722 ExecContext *c = data;
730 x = sched_policy_from_string(rvalue);
732 log_syntax(unit, LOG_ERR, filename, line, -x,
733 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
737 c->cpu_sched_policy = x;
738 /* Moving to or from real-time policy? We need to adjust the priority */
739 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
740 c->cpu_sched_set = true;
745 int config_parse_exec_cpu_sched_prio(const char *unit,
746 const char *filename,
749 unsigned section_line,
756 ExecContext *c = data;
764 r = safe_atoi(rvalue, &i);
766 log_syntax(unit, LOG_ERR, filename, line, -r,
767 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
771 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
772 min = sched_get_priority_min(c->cpu_sched_policy);
773 max = sched_get_priority_max(c->cpu_sched_policy);
775 if (i < min || i > max) {
776 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
777 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
781 c->cpu_sched_priority = i;
782 c->cpu_sched_set = true;
787 int config_parse_exec_cpu_affinity(const char *unit,
788 const char *filename,
791 unsigned section_line,
798 ExecContext *c = data;
808 if (isempty(rvalue)) {
809 /* An empty assignment resets the CPU list */
816 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
817 _cleanup_free_ char *t = NULL;
825 r = safe_atou(t, &cpu);
828 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
833 if (r < 0 || cpu >= c->cpuset_ncpus) {
834 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
835 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
839 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
845 int config_parse_exec_capabilities(const char *unit,
846 const char *filename,
849 unsigned section_line,
856 ExecContext *c = data;
864 cap = cap_from_text(rvalue);
866 log_syntax(unit, LOG_ERR, filename, line, errno,
867 "Failed to parse capabilities, ignoring: %s", rvalue);
872 cap_free(c->capabilities);
873 c->capabilities = cap;
878 int config_parse_exec_secure_bits(const char *unit,
879 const char *filename,
882 unsigned section_line,
889 ExecContext *c = data;
899 if (isempty(rvalue)) {
900 /* An empty assignment resets the field */
905 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
906 if (first_word(w, "keep-caps"))
907 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
908 else if (first_word(w, "keep-caps-locked"))
909 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
910 else if (first_word(w, "no-setuid-fixup"))
911 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
912 else if (first_word(w, "no-setuid-fixup-locked"))
913 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
914 else if (first_word(w, "noroot"))
915 c->secure_bits |= 1<<SECURE_NOROOT;
916 else if (first_word(w, "noroot-locked"))
917 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
919 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
920 "Failed to parse secure bits, ignoring: %s", rvalue);
928 int config_parse_bounding_set(const char *unit,
929 const char *filename,
932 unsigned section_line,
939 uint64_t *capability_bounding_set_drop = data;
951 if (rvalue[0] == '~') {
956 /* Note that we store this inverted internally, since the
957 * kernel wants it like this. But we actually expose it
958 * non-inverted everywhere to have a fully normalized
961 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
962 _cleanup_free_ char *t = NULL;
970 r = cap_from_name(t, &cap);
972 log_syntax(unit, LOG_ERR, filename, line, errno,
973 "Failed to parse capability in bounding set, ignoring: %s", t);
977 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
981 *capability_bounding_set_drop |= sum;
983 *capability_bounding_set_drop |= ~sum;
988 int config_parse_limit(const char *unit,
989 const char *filename,
992 unsigned section_line,
999 struct rlimit **rl = data;
1000 unsigned long long u;
1009 if (streq(rvalue, "infinity"))
1010 u = (unsigned long long) RLIM_INFINITY;
1014 r = safe_atollu(rvalue, &u);
1016 log_syntax(unit, LOG_ERR, filename, line, -r,
1017 "Failed to parse resource value, ignoring: %s", rvalue);
1023 *rl = new(struct rlimit, 1);
1028 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
1032 #ifdef HAVE_SYSV_COMPAT
1033 int config_parse_sysv_priority(const char *unit,
1034 const char *filename,
1036 const char *section,
1037 unsigned section_line,
1044 int *priority = data;
1052 r = safe_atoi(rvalue, &i);
1053 if (r < 0 || i < 0) {
1054 log_syntax(unit, LOG_ERR, filename, line, -r,
1055 "Failed to parse SysV start priority, ignoring: %s", rvalue);
1059 *priority = (int) i;
1064 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1066 int config_parse_kill_signal(const char *unit,
1067 const char *filename,
1069 const char *section,
1070 unsigned section_line,
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,
1100 unsigned section_line,
1107 ExecContext *c = data;
1111 unsigned long flags = 0;
1118 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
1119 _cleanup_free_ char *t;
1125 if (streq(t, "shared"))
1127 else if (streq(t, "slave"))
1129 else if (streq(w, "private"))
1130 flags |= MS_PRIVATE;
1132 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1133 "Failed to parse mount flag %s, ignoring: %s",
1139 c->mount_flags = flags;
1143 int config_parse_exec_selinux_context(
1145 const char *filename,
1147 const char *section,
1148 unsigned section_line,
1155 ExecContext *c = data;
1166 if (isempty(rvalue)) {
1167 free(c->selinux_context);
1168 c->selinux_context = NULL;
1169 c->selinux_context_ignore = false;
1173 if (rvalue[0] == '-') {
1179 r = unit_name_printf(u, rvalue, &k);
1181 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1185 free(c->selinux_context);
1186 c->selinux_context = k;
1187 c->selinux_context_ignore = ignore;
1192 int config_parse_exec_apparmor_profile(
1194 const char *filename,
1196 const char *section,
1197 unsigned section_line,
1204 ExecContext *c = data;
1215 if (isempty(rvalue)) {
1216 free(c->apparmor_profile);
1217 c->apparmor_profile = NULL;
1218 c->apparmor_profile_ignore = false;
1222 if (rvalue[0] == '-') {
1228 r = unit_name_printf(u, rvalue, &k);
1230 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1234 free(c->apparmor_profile);
1235 c->apparmor_profile = k;
1236 c->apparmor_profile_ignore = ignore;
1241 int config_parse_timer(const char *unit,
1242 const char *filename,
1244 const char *section,
1245 unsigned section_line,
1256 CalendarSpec *c = NULL;
1264 if (isempty(rvalue)) {
1265 /* Empty assignment resets list */
1266 timer_free_values(t);
1270 b = timer_base_from_string(lvalue);
1272 log_syntax(unit, LOG_ERR, filename, line, -b,
1273 "Failed to parse timer base, ignoring: %s", lvalue);
1277 if (b == TIMER_CALENDAR) {
1278 if (calendar_spec_from_string(rvalue, &c) < 0) {
1279 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1280 "Failed to parse calendar specification, ignoring: %s",
1285 id = CLOCK_REALTIME;
1287 if (parse_sec(rvalue, &u) < 0) {
1288 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1289 "Failed to parse timer value, ignoring: %s",
1294 id = CLOCK_MONOTONIC;
1297 v = new0(TimerValue, 1);
1304 v->calendar_spec = c;
1306 LIST_PREPEND(value, t->values, v);
1311 int config_parse_trigger_unit(
1313 const char *filename,
1315 const char *section,
1316 unsigned section_line,
1323 _cleanup_free_ char *p = NULL;
1333 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1334 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1335 "Multiple units to trigger specified, ignoring: %s", rvalue);
1339 r = unit_name_printf(u, rvalue, &p);
1341 log_syntax(unit, LOG_ERR, filename, line, -r,
1342 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1344 type = unit_name_to_type(p ?: rvalue);
1346 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1347 "Unit type not valid, ignoring: %s", rvalue);
1351 if (type == u->type) {
1352 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1353 "Trigger cannot be of same type, ignoring: %s", rvalue);
1357 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
1359 log_syntax(unit, LOG_ERR, filename, line, -r,
1360 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
1367 int config_parse_path_spec(const char *unit,
1368 const char *filename,
1370 const char *section,
1371 unsigned section_line,
1381 _cleanup_free_ char *k = NULL;
1389 if (isempty(rvalue)) {
1390 /* Empty assignment clears list */
1395 b = path_type_from_string(lvalue);
1397 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1398 "Failed to parse path type, ignoring: %s", lvalue);
1402 r = unit_full_printf(UNIT(p), rvalue, &k);
1408 log_syntax(unit, LOG_ERR, filename, line, -r,
1409 "Failed to resolve unit specifiers on %s. Ignoring.",
1413 if (!path_is_absolute(k)) {
1414 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1415 "Path is not absolute, ignoring: %s", k);
1419 s = new0(PathSpec, 1);
1424 s->path = path_kill_slashes(k);
1429 LIST_PREPEND(spec, p->specs, s);
1434 int config_parse_socket_service(const char *unit,
1435 const char *filename,
1437 const char *section,
1438 unsigned section_line,
1445 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1449 _cleanup_free_ char *p = NULL;
1456 r = unit_name_printf(UNIT(s), rvalue, &p);
1458 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1462 if (!endswith(p, ".service")) {
1463 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1467 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1469 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1473 unit_ref_set(&s->service, x);
1478 int config_parse_service_sockets(const char *unit,
1479 const char *filename,
1481 const char *section,
1482 unsigned section_line,
1499 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1500 _cleanup_free_ char *t = NULL, *k = NULL;
1506 r = unit_name_printf(UNIT(s), t, &k);
1508 log_syntax(unit, LOG_ERR, filename, line, -r,
1509 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1511 if (!endswith(k ?: t, ".socket")) {
1512 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1513 "Unit must be of type socket, ignoring: %s", k ?: t);
1517 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
1519 log_syntax(unit, LOG_ERR, filename, line, -r,
1520 "Failed to add dependency on %s, ignoring: %s",
1521 k ?: t, strerror(-r));
1523 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
1531 int config_parse_service_timeout(const char *unit,
1532 const char *filename,
1534 const char *section,
1535 unsigned section_line,
1542 Service *s = userdata;
1550 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
1551 rvalue, data, userdata);
1555 if (streq(lvalue, "TimeoutSec")) {
1556 s->start_timeout_defined = true;
1557 s->timeout_stop_usec = s->timeout_start_usec;
1558 } else if (streq(lvalue, "TimeoutStartSec"))
1559 s->start_timeout_defined = true;
1564 int config_parse_busname_service(
1566 const char *filename,
1568 const char *section,
1569 unsigned section_line,
1576 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1580 _cleanup_free_ char *p = NULL;
1587 r = unit_name_printf(UNIT(n), rvalue, &p);
1589 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1593 if (!endswith(p, ".service")) {
1594 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1598 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1600 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1604 unit_ref_set(&n->service, x);
1609 int config_parse_unit_env_file(const char *unit,
1610 const char *filename,
1612 const char *section,
1613 unsigned section_line,
1622 _cleanup_free_ char *n = NULL;
1631 if (isempty(rvalue)) {
1632 /* Empty assignment frees the list */
1638 r = unit_full_printf(u, rvalue, &n);
1640 log_syntax(unit, LOG_ERR, filename, line, r,
1641 "Failed to resolve specifiers, ignoring: %s", rvalue);
1644 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1645 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1646 "Path '%s' is not absolute, ignoring.", s);
1650 r = strv_extend(env, s);
1657 int config_parse_environ(const char *unit,
1658 const char *filename,
1660 const char *section,
1661 unsigned section_line,
1669 char*** env = data, *w, *state;
1671 _cleanup_free_ char *k = NULL;
1679 if (isempty(rvalue)) {
1680 /* Empty assignment resets the list */
1687 r = unit_full_printf(u, rvalue, &k);
1689 log_syntax(unit, LOG_ERR, filename, line, -r,
1690 "Failed to resolve specifiers, ignoring: %s", rvalue);
1698 FOREACH_WORD_QUOTED(w, l, k, state) {
1699 _cleanup_free_ char *n;
1702 n = cunescape_length(w, l);
1706 if (!env_assignment_is_valid(n)) {
1707 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1708 "Invalid environment assignment, ignoring: %s", rvalue);
1712 x = strv_env_set(*env, n);
1723 int config_parse_ip_tos(const char *unit,
1724 const char *filename,
1726 const char *section,
1727 unsigned section_line,
1734 int *ip_tos = data, x;
1741 x = ip_tos_from_string(rvalue);
1743 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1744 "Failed to parse IP TOS value, ignoring: %s", rvalue);
1752 int config_parse_unit_condition_path(const char *unit,
1753 const char *filename,
1755 const char *section,
1756 unsigned section_line,
1763 ConditionType cond = ltype;
1765 bool trigger, negate;
1767 _cleanup_free_ char *p = NULL;
1775 if (isempty(rvalue)) {
1776 /* Empty assignment resets the list */
1777 condition_free_list(u->conditions);
1778 u->conditions = NULL;
1782 trigger = rvalue[0] == '|';
1786 negate = rvalue[0] == '!';
1790 r = unit_full_printf(u, rvalue, &p);
1792 log_syntax(unit, LOG_ERR, filename, line, -r,
1793 "Failed to resolve specifiers, ignoring: %s", rvalue);
1800 if (!path_is_absolute(p)) {
1801 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1802 "Path in condition not absolute, ignoring: %s", p);
1806 c = condition_new(cond, p, trigger, negate);
1810 LIST_PREPEND(conditions, u->conditions, c);
1814 int config_parse_unit_condition_string(const char *unit,
1815 const char *filename,
1817 const char *section,
1818 unsigned section_line,
1825 ConditionType cond = ltype;
1827 bool trigger, negate;
1829 _cleanup_free_ char *s = NULL;
1837 if (isempty(rvalue)) {
1838 /* Empty assignment resets the list */
1839 condition_free_list(u->conditions);
1840 u->conditions = NULL;
1844 trigger = rvalue[0] == '|';
1848 negate = rvalue[0] == '!';
1852 r = unit_full_printf(u, rvalue, &s);
1854 log_syntax(unit, LOG_ERR, filename, line, -r,
1855 "Failed to resolve specifiers, ignoring: %s", rvalue);
1862 c = condition_new(cond, s, trigger, negate);
1866 LIST_PREPEND(conditions, u->conditions, c);
1870 int config_parse_unit_condition_null(const char *unit,
1871 const char *filename,
1873 const char *section,
1874 unsigned section_line,
1883 bool trigger, negate;
1891 if (isempty(rvalue)) {
1892 /* Empty assignment resets the list */
1893 condition_free_list(u->conditions);
1894 u->conditions = NULL;
1898 trigger = rvalue[0] == '|';
1902 negate = rvalue[0] == '!';
1906 b = parse_boolean(rvalue);
1908 log_syntax(unit, LOG_ERR, filename, line, -b,
1909 "Failed to parse boolean value in condition, ignoring: %s",
1917 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1921 LIST_PREPEND(conditions, u->conditions, c);
1925 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1926 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
1928 int config_parse_unit_requires_mounts_for(
1930 const char *filename,
1932 const char *section,
1933 unsigned section_line,
1950 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1952 _cleanup_free_ char *n;
1958 if (!utf8_is_valid(n)) {
1959 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
1963 r = unit_require_mounts_for(u, n);
1965 log_syntax(unit, LOG_ERR, filename, line, r,
1966 "Failed to add required mount for, ignoring: %s", rvalue);
1974 int config_parse_documentation(const char *unit,
1975 const char *filename,
1977 const char *section,
1978 unsigned section_line,
1994 if (isempty(rvalue)) {
1995 /* Empty assignment resets the list */
1996 strv_free(u->documentation);
1997 u->documentation = NULL;
2001 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
2002 rvalue, data, userdata);
2006 for (a = b = u->documentation; a && *a; a++) {
2008 if (is_valid_documentation_url(*a))
2011 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2012 "Invalid URL, ignoring: %s", *a);
2023 int config_parse_syscall_filter(
2025 const char *filename,
2027 const char *section,
2028 unsigned section_line,
2035 static const char default_syscalls[] =
2042 ExecContext *c = data;
2044 bool invert = false;
2054 if (isempty(rvalue)) {
2055 /* Empty assignment resets the list */
2056 set_free(c->syscall_filter);
2057 c->syscall_filter = NULL;
2058 c->syscall_whitelist = false;
2062 if (rvalue[0] == '~') {
2067 if (!c->syscall_filter) {
2068 c->syscall_filter = set_new(trivial_hash_func, trivial_compare_func);
2069 if (!c->syscall_filter)
2073 /* Allow everything but the ones listed */
2074 c->syscall_whitelist = false;
2078 /* Allow nothing but the ones listed */
2079 c->syscall_whitelist = true;
2081 /* Accept default syscalls if we are on a whitelist */
2082 NULSTR_FOREACH(i, default_syscalls) {
2085 id = seccomp_syscall_resolve_name(i);
2089 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2098 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2099 _cleanup_free_ char *t = NULL;
2106 id = seccomp_syscall_resolve_name(t);
2108 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call, ignoring: %s", t);
2112 /* If we previously wanted to forbid a syscall and now
2113 * we want to allow it, then remove it from the list
2115 if (!invert == c->syscall_whitelist) {
2116 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2122 set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
2125 c->no_new_privileges = true;
2130 int config_parse_syscall_archs(
2132 const char *filename,
2134 const char *section,
2135 unsigned section_line,
2147 if (isempty(rvalue)) {
2153 r = set_ensure_allocated(archs, trivial_hash_func, trivial_compare_func);
2157 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2158 _cleanup_free_ char *t = NULL;
2165 r = seccomp_arch_from_string(t, &a);
2167 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call architecture, ignoring: %s", t);
2171 r = set_put(*archs, UINT32_TO_PTR(a + 1));
2181 int config_parse_syscall_errno(
2183 const char *filename,
2185 const char *section,
2186 unsigned section_line,
2193 ExecContext *c = data;
2200 if (isempty(rvalue)) {
2201 /* Empty assignment resets to KILL */
2202 c->syscall_errno = 0;
2206 e = errno_from_name(rvalue);
2208 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse error number, ignoring: %s", rvalue);
2212 c->syscall_errno = e;
2216 int config_parse_address_families(
2218 const char *filename,
2220 const char *section,
2221 unsigned section_line,
2228 ExecContext *c = data;
2230 bool invert = false;
2240 if (isempty(rvalue)) {
2241 /* Empty assignment resets the list */
2242 set_free(c->address_families);
2243 c->address_families = NULL;
2244 c->address_families_whitelist = false;
2248 if (rvalue[0] == '~') {
2253 if (!c->address_families) {
2254 c->address_families = set_new(trivial_hash_func, trivial_compare_func);
2255 if (!c->address_families)
2258 c->address_families_whitelist = !invert;
2261 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2262 _cleanup_free_ char *t = NULL;
2269 af = af_from_name(t);
2271 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse address family, ignoring: %s", t);
2275 /* If we previously wanted to forbid an address family and now
2276 * we want to allow it, then remove it from the list
2278 if (!invert == c->address_families_whitelist) {
2279 r = set_put(c->address_families, INT_TO_PTR(af));
2285 set_remove(c->address_families, INT_TO_PTR(af));
2292 int config_parse_unit_slice(
2294 const char *filename,
2296 const char *section,
2297 unsigned section_line,
2304 _cleanup_free_ char *k = NULL;
2305 Unit *u = userdata, *slice;
2313 r = unit_name_printf(u, rvalue, &k);
2315 log_syntax(unit, LOG_ERR, filename, line, -r,
2316 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2323 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
2325 log_syntax(unit, LOG_ERR, filename, line, -r,
2326 "Failed to load slice unit %s. Ignoring.", k);
2330 if (slice->type != UNIT_SLICE) {
2331 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2332 "Slice unit %s is not a slice. Ignoring.", k);
2336 unit_ref_set(&u->slice, slice);
2340 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2342 int config_parse_cpu_shares(
2344 const char *filename,
2346 const char *section,
2347 unsigned section_line,
2354 CGroupContext *c = data;
2362 if (isempty(rvalue)) {
2363 c->cpu_shares = 1024;
2367 r = safe_atolu(rvalue, &lu);
2368 if (r < 0 || lu <= 0) {
2369 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2370 "CPU shares '%s' invalid. Ignoring.", rvalue);
2378 int config_parse_memory_limit(
2380 const char *filename,
2382 const char *section,
2383 unsigned section_line,
2390 CGroupContext *c = data;
2394 if (isempty(rvalue)) {
2395 c->memory_limit = (uint64_t) -1;
2399 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2401 r = parse_size(rvalue, 1024, &bytes);
2403 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Memory limit '%s' invalid. Ignoring.", rvalue);
2407 c->memory_limit = (uint64_t) bytes;
2411 int config_parse_device_allow(
2413 const char *filename,
2415 const char *section,
2416 unsigned section_line,
2423 _cleanup_free_ char *path = NULL;
2424 CGroupContext *c = data;
2425 CGroupDeviceAllow *a;
2429 if (isempty(rvalue)) {
2430 while (c->device_allow)
2431 cgroup_context_free_device_allow(c, c->device_allow);
2436 n = strcspn(rvalue, WHITESPACE);
2437 path = strndup(rvalue, n);
2441 if (!startswith(path, "/dev/") &&
2442 !startswith(path, "block-") &&
2443 !startswith(path, "char-")) {
2444 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device node path '%s'. Ignoring.", path);
2448 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2452 if (!in_charset(m, "rwm")) {
2453 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device rights '%s'. Ignoring.", m);
2457 a = new0(CGroupDeviceAllow, 1);
2463 a->r = !!strchr(m, 'r');
2464 a->w = !!strchr(m, 'w');
2465 a->m = !!strchr(m, 'm');
2467 LIST_PREPEND(device_allow, c->device_allow, a);
2471 int config_parse_blockio_weight(
2473 const char *filename,
2475 const char *section,
2476 unsigned section_line,
2483 CGroupContext *c = data;
2491 if (isempty(rvalue)) {
2492 c->blockio_weight = 1000;
2496 r = safe_atolu(rvalue, &lu);
2497 if (r < 0 || lu < 10 || lu > 1000) {
2498 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2499 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2503 c->blockio_weight = lu;
2508 int config_parse_blockio_device_weight(
2510 const char *filename,
2512 const char *section,
2513 unsigned section_line,
2520 _cleanup_free_ char *path = NULL;
2521 CGroupBlockIODeviceWeight *w;
2522 CGroupContext *c = data;
2532 if (isempty(rvalue)) {
2533 while (c->blockio_device_weights)
2534 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2539 n = strcspn(rvalue, WHITESPACE);
2540 weight = rvalue + n;
2542 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2543 "Expected block device and device weight. Ignoring.");
2547 path = strndup(rvalue, n);
2551 if (!path_startswith(path, "/dev")) {
2552 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2553 "Invalid device node path '%s'. Ignoring.", path);
2557 weight += strspn(weight, WHITESPACE);
2558 r = safe_atolu(weight, &lu);
2559 if (r < 0 || lu < 10 || lu > 1000) {
2560 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2561 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2566 w = new0(CGroupBlockIODeviceWeight, 1);
2575 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
2579 int config_parse_blockio_bandwidth(
2581 const char *filename,
2583 const char *section,
2584 unsigned section_line,
2591 _cleanup_free_ char *path = NULL;
2592 CGroupBlockIODeviceBandwidth *b;
2593 CGroupContext *c = data;
2594 const char *bandwidth;
2604 read = streq("BlockIOReadBandwidth", lvalue);
2606 if (isempty(rvalue)) {
2607 CGroupBlockIODeviceBandwidth *next;
2609 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2610 if (b->read == read)
2611 cgroup_context_free_blockio_device_bandwidth(c, b);
2616 n = strcspn(rvalue, WHITESPACE);
2617 bandwidth = rvalue + n;
2618 bandwidth += strspn(bandwidth, WHITESPACE);
2621 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2622 "Expected space separated pair of device node and bandwidth. Ignoring.");
2626 path = strndup(rvalue, n);
2630 if (!path_startswith(path, "/dev")) {
2631 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2632 "Invalid device node path '%s'. Ignoring.", path);
2636 r = parse_size(bandwidth, 1000, &bytes);
2637 if (r < 0 || bytes <= 0) {
2638 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2642 b = new0(CGroupBlockIODeviceBandwidth, 1);
2648 b->bandwidth = (uint64_t) bytes;
2651 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
2656 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2658 int config_parse_job_mode_isolate(
2660 const char *filename,
2662 const char *section,
2663 unsigned section_line,
2677 r = parse_boolean(rvalue);
2679 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue);
2683 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2687 int config_parse_personality(
2689 const char *filename,
2691 const char *section,
2692 unsigned section_line,
2699 unsigned long *personality = data, p;
2704 assert(personality);
2706 p = personality_from_string(rvalue);
2707 if (p == 0xffffffffUL) {
2708 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2709 "Failed to parse personality, ignoring: %s", rvalue);
2717 int config_parse_runtime_directory(
2719 const char *filename,
2721 const char *section,
2722 unsigned section_line,
2729 char***rt = data, *w, *state;
2738 if (isempty(rvalue)) {
2739 /* Empty assignment resets the list */
2745 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2746 _cleanup_free_ char *n;
2752 if (!filename_is_safe(n)) {
2753 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
2757 r = strv_push(rt, n);
2767 int config_parse_set_status(
2769 const char *filename,
2771 const char *section,
2772 unsigned section_line,
2783 ExitStatusSet *status_set = data;
2790 if (isempty(rvalue)) {
2791 /* Empty assignment resets the list */
2793 set_free(status_set->signal);
2794 set_free(status_set->code);
2796 status_set->signal = status_set->code = NULL;
2800 FOREACH_WORD(w, l, rvalue, state) {
2801 _cleanup_free_ char *temp;
2804 temp = strndup(w, l);
2808 r = safe_atoi(temp, &val);
2810 val = signal_from_string_try_harder(temp);
2813 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
2817 r = set_put(status_set->signal, INT_TO_PTR(val));
2819 log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w);
2823 log_syntax(unit, LOG_ERR, filename, line, -val, "Failed to parse value, ignoring: %s", w);
2827 if (val < 0 || val > 255)
2828 log_syntax(unit, LOG_ERR, filename, line, ERANGE, "Value %d is outside range 0-255, ignoring", val);
2830 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
2834 r = set_put(status_set->code, INT_TO_PTR(val));
2836 log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w);
2846 int config_parse_namespace_path_strv(
2848 const char *filename,
2850 const char *section,
2851 unsigned section_line,
2858 char*** sv = data, *w, *state;
2867 if (isempty(rvalue)) {
2868 /* Empty assignment resets the list */
2874 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2875 _cleanup_free_ char *n;
2882 if (!utf8_is_valid(n)) {
2883 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
2887 offset = n[0] == '-';
2888 if (!path_is_absolute(n + offset)) {
2889 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
2893 path_kill_slashes(n);
2895 r = strv_push(sv, n);
2905 #define FOLLOW_MAX 8
2907 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2918 /* This will update the filename pointer if the loaded file is
2919 * reached by a symlink. The old string will be freed. */
2922 char *target, *name;
2924 if (c++ >= FOLLOW_MAX)
2927 path_kill_slashes(*filename);
2929 /* Add the file name we are currently looking at to
2930 * the names of this unit, but only if it is a valid
2932 name = basename(*filename);
2934 if (unit_name_is_valid(name, TEMPLATE_VALID)) {
2936 id = set_get(names, name);
2942 r = set_consume(names, id);
2948 /* Try to open the file name, but don't if its a symlink */
2949 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2956 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2957 r = readlink_and_make_absolute(*filename, &target);
2965 f = fdopen(fd, "re");
2968 close_nointr_nofail(fd);
2977 static int merge_by_names(Unit **u, Set *names, const char *id) {
2985 /* Let's try to add in all symlink names we found */
2986 while ((k = set_steal_first(names))) {
2988 /* First try to merge in the other name into our
2990 r = unit_merge_by_name(*u, k);
2994 /* Hmm, we couldn't merge the other unit into
2995 * ours? Then let's try it the other way
2998 other = manager_get_unit((*u)->manager, k);
3002 r = unit_merge(other, *u);
3005 return merge_by_names(u, names, NULL);
3013 unit_choose_id(*u, id);
3021 static int load_from_path(Unit *u, const char *path) {
3023 _cleanup_set_free_free_ Set *symlink_names = NULL;
3024 _cleanup_fclose_ FILE *f = NULL;
3025 _cleanup_free_ char *filename = NULL;
3033 symlink_names = set_new(string_hash_func, string_compare_func);
3037 if (path_is_absolute(path)) {
3039 filename = strdup(path);
3043 r = open_follow(&filename, &f, symlink_names, &id);
3055 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
3057 /* Instead of opening the path right away, we manually
3058 * follow all symlinks and add their name to our unit
3059 * name set while doing so */
3060 filename = path_make_absolute(path, *p);
3064 if (u->manager->unit_path_cache &&
3065 !set_get(u->manager->unit_path_cache, filename))
3068 r = open_follow(&filename, &f, symlink_names, &id);
3077 /* Empty the symlink names for the next run */
3078 set_clear_free(symlink_names);
3087 /* Hmm, no suitable file found? */
3091 r = merge_by_names(&merged, symlink_names, id);
3096 u->load_state = UNIT_MERGED;
3100 if (fstat(fileno(f), &st) < 0)
3103 if (null_or_empty(&st))
3104 u->load_state = UNIT_MASKED;
3106 u->load_state = UNIT_LOADED;
3108 /* Now, parse the file contents */
3109 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
3110 config_item_perf_lookup,
3111 (void*) load_fragment_gperf_lookup, false, true, u);
3116 free(u->fragment_path);
3117 u->fragment_path = filename;
3120 u->fragment_mtime = timespec_load(&st.st_mtim);
3122 if (u->source_path) {
3123 if (stat(u->source_path, &st) >= 0)
3124 u->source_mtime = timespec_load(&st.st_mtim);
3126 u->source_mtime = 0;
3132 int unit_load_fragment(Unit *u) {
3138 assert(u->load_state == UNIT_STUB);
3141 /* First, try to find the unit under its id. We always look
3142 * for unit files in the default directories, to make it easy
3143 * to override things by placing things in /etc/systemd/system */
3144 r = load_from_path(u, u->id);
3148 /* Try to find an alias we can load this with */
3149 if (u->load_state == UNIT_STUB)
3150 SET_FOREACH(t, u->names, i) {
3155 r = load_from_path(u, t);
3159 if (u->load_state != UNIT_STUB)
3163 /* And now, try looking for it under the suggested (originally linked) path */
3164 if (u->load_state == UNIT_STUB && u->fragment_path) {
3166 r = load_from_path(u, u->fragment_path);
3170 if (u->load_state == UNIT_STUB) {
3171 /* Hmm, this didn't work? Then let's get rid
3172 * of the fragment path stored for us, so that
3173 * we don't point to an invalid location. */
3174 free(u->fragment_path);
3175 u->fragment_path = NULL;
3179 /* Look for a template */
3180 if (u->load_state == UNIT_STUB && u->instance) {
3181 _cleanup_free_ char *k;
3183 k = unit_name_template(u->id);
3187 r = load_from_path(u, k);
3191 if (u->load_state == UNIT_STUB)
3192 SET_FOREACH(t, u->names, i) {
3193 _cleanup_free_ char *z = NULL;
3198 z = unit_name_template(t);
3202 r = load_from_path(u, z);
3206 if (u->load_state != UNIT_STUB)
3214 void unit_dump_config_items(FILE *f) {
3215 static const struct {
3216 const ConfigParserCallback callback;
3219 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3220 { config_parse_warn_compat, "NOTSUPPORTED" },
3222 { config_parse_int, "INTEGER" },
3223 { config_parse_unsigned, "UNSIGNED" },
3224 { config_parse_iec_size, "SIZE" },
3225 { config_parse_iec_off, "SIZE" },
3226 { config_parse_si_size, "SIZE" },
3227 { config_parse_bool, "BOOLEAN" },
3228 { config_parse_string, "STRING" },
3229 { config_parse_path, "PATH" },
3230 { config_parse_unit_path_printf, "PATH" },
3231 { config_parse_strv, "STRING [...]" },
3232 { config_parse_exec_nice, "NICE" },
3233 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
3234 { config_parse_exec_io_class, "IOCLASS" },
3235 { config_parse_exec_io_priority, "IOPRIORITY" },
3236 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
3237 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
3238 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
3239 { config_parse_mode, "MODE" },
3240 { config_parse_unit_env_file, "FILE" },
3241 { config_parse_output, "OUTPUT" },
3242 { config_parse_input, "INPUT" },
3243 { config_parse_log_facility, "FACILITY" },
3244 { config_parse_log_level, "LEVEL" },
3245 { config_parse_exec_capabilities, "CAPABILITIES" },
3246 { config_parse_exec_secure_bits, "SECUREBITS" },
3247 { config_parse_bounding_set, "BOUNDINGSET" },
3248 { config_parse_limit, "LIMIT" },
3249 { config_parse_unit_deps, "UNIT [...]" },
3250 { config_parse_exec, "PATH [ARGUMENT [...]]" },
3251 { config_parse_service_type, "SERVICETYPE" },
3252 { config_parse_service_restart, "SERVICERESTART" },
3253 #ifdef HAVE_SYSV_COMPAT
3254 { config_parse_sysv_priority, "SYSVPRIORITY" },
3256 { config_parse_kill_mode, "KILLMODE" },
3257 { config_parse_kill_signal, "SIGNAL" },
3258 { config_parse_socket_listen, "SOCKET [...]" },
3259 { config_parse_socket_bind, "SOCKETBIND" },
3260 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
3261 { config_parse_sec, "SECONDS" },
3262 { config_parse_nsec, "NANOSECONDS" },
3263 { config_parse_namespace_path_strv, "PATH [...]" },
3264 { config_parse_unit_requires_mounts_for, "PATH [...]" },
3265 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
3266 { config_parse_unit_string_printf, "STRING" },
3267 { config_parse_trigger_unit, "UNIT" },
3268 { config_parse_timer, "TIMER" },
3269 { config_parse_path_spec, "PATH" },
3270 { config_parse_notify_access, "ACCESS" },
3271 { config_parse_ip_tos, "TOS" },
3272 { config_parse_unit_condition_path, "CONDITION" },
3273 { config_parse_unit_condition_string, "CONDITION" },
3274 { config_parse_unit_condition_null, "CONDITION" },
3275 { config_parse_unit_slice, "SLICE" },
3276 { config_parse_documentation, "URL" },
3277 { config_parse_service_timeout, "SECONDS" },
3278 { config_parse_start_limit_action, "ACTION" },
3279 { config_parse_set_status, "STATUS" },
3280 { config_parse_service_sockets, "SOCKETS" },
3281 { config_parse_environ, "ENVIRON" },
3283 { config_parse_syscall_filter, "SYSCALLS" },
3284 { config_parse_syscall_archs, "ARCHS" },
3285 { config_parse_syscall_errno, "ERRNO" },
3286 { config_parse_address_families, "FAMILIES" },
3288 { config_parse_cpu_shares, "SHARES" },
3289 { config_parse_memory_limit, "LIMIT" },
3290 { config_parse_device_allow, "DEVICE" },
3291 { config_parse_device_policy, "POLICY" },
3292 { config_parse_blockio_bandwidth, "BANDWIDTH" },
3293 { config_parse_blockio_weight, "WEIGHT" },
3294 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
3295 { config_parse_long, "LONG" },
3296 { config_parse_socket_service, "SERVICE" },
3298 { config_parse_exec_selinux_context, "LABEL" },
3300 { config_parse_job_mode, "MODE" },
3301 { config_parse_job_mode_isolate, "BOOLEAN" },
3302 { config_parse_personality, "PERSONALITY" },
3305 const char *prev = NULL;
3310 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
3311 const char *rvalue = "OTHER", *lvalue;
3315 const ConfigPerfItem *p;
3317 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
3319 dot = strchr(i, '.');
3320 lvalue = dot ? dot + 1 : i;
3324 if (!prev || !strneq(prev, i, prefix_len+1)) {
3328 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
3331 for (j = 0; j < ELEMENTSOF(table); j++)
3332 if (p->parse == table[j].callback) {
3333 rvalue = table[j].rvalue;
3337 fprintf(f, "%s=%s\n", lvalue, rvalue);