]> git.proxmox.com Git - systemd.git/blame - src/core/load-fragment.c
Imported Upstream version 208
[systemd.git] / src / core / load-fragment.c
CommitLineData
663996b3
MS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7 Copyright 2012 Holger Hans Peter Freyther
8
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.
13
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.
18
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/>.
21***/
22
23#include <linux/oom.h>
24#include <assert.h>
25#include <errno.h>
26#include <string.h>
27#include <unistd.h>
28#include <fcntl.h>
29#include <sched.h>
30#include <sys/prctl.h>
31#include <sys/mount.h>
32#include <linux/fs.h>
33#include <sys/stat.h>
34#include <sys/time.h>
35#include <sys/resource.h>
36
37#include <systemd/sd-messages.h>
38
39#include "unit.h"
40#include "strv.h"
41#include "conf-parser.h"
42#include "load-fragment.h"
43#include "log.h"
44#include "ioprio.h"
45#include "securebits.h"
46#include "missing.h"
47#include "unit-name.h"
48#include "unit-printf.h"
49#include "dbus-common.h"
50#include "utf8.h"
51#include "path-util.h"
52#include "syscall-list.h"
53#include "env-util.h"
14228c0d 54#include "cgroup.h"
663996b3
MS
55
56#ifndef HAVE_SYSV_COMPAT
57int config_parse_warn_compat(const char *unit,
58 const char *filename,
59 unsigned line,
60 const char *section,
61 const char *lvalue,
62 int ltype,
63 const char *rvalue,
64 void *data,
65 void *userdata) {
66
67 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
68 "Support for option %s= has been disabled at compile time and is ignored",
69 lvalue);
70 return 0;
71}
72#endif
73
74int config_parse_unit_deps(const char* unit,
75 const char *filename,
76 unsigned line,
77 const char *section,
78 const char *lvalue,
79 int ltype,
80 const char *rvalue,
81 void *data,
82 void *userdata) {
83
84 UnitDependency d = ltype;
85 Unit *u = userdata;
86 char *w;
87 size_t l;
88 char *state;
89
90 assert(filename);
91 assert(lvalue);
92 assert(rvalue);
93
94 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
95 _cleanup_free_ char *t = NULL, *k = NULL;
96 int r;
97
98 t = strndup(w, l);
99 if (!t)
100 return log_oom();
101
14228c0d
MB
102 r = unit_name_printf(u, t, &k);
103 if (r < 0) {
104 log_syntax(unit, LOG_ERR, filename, line, -r,
105 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
106 continue;
107 }
663996b3
MS
108
109 r = unit_add_dependency_by_name(u, d, k, NULL, true);
110 if (r < 0)
111 log_syntax(unit, LOG_ERR, filename, line, -r,
112 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
113 }
114
115 return 0;
116}
117
118int config_parse_unit_string_printf(const char *unit,
119 const char *filename,
120 unsigned line,
121 const char *section,
122 const char *lvalue,
123 int ltype,
124 const char *rvalue,
125 void *data,
126 void *userdata) {
127
128 Unit *u = userdata;
129 _cleanup_free_ char *k = NULL;
14228c0d 130 int r;
663996b3
MS
131
132 assert(filename);
133 assert(lvalue);
134 assert(rvalue);
135 assert(u);
136
14228c0d
MB
137 r = unit_full_printf(u, rvalue, &k);
138 if (r < 0)
139 log_syntax(unit, LOG_ERR, filename, line, -r,
140 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
663996b3
MS
141
142 return config_parse_string(unit, filename, line, section, lvalue, ltype,
143 k ? k : rvalue, data, userdata);
144}
145
146int config_parse_unit_strv_printf(const char *unit,
147 const char *filename,
148 unsigned line,
149 const char *section,
150 const char *lvalue,
151 int ltype,
152 const char *rvalue,
153 void *data,
154 void *userdata) {
155
156 Unit *u = userdata;
157 _cleanup_free_ char *k = NULL;
14228c0d 158 int r;
663996b3
MS
159
160 assert(filename);
161 assert(lvalue);
162 assert(rvalue);
163 assert(u);
164
14228c0d
MB
165 r = unit_full_printf(u, rvalue, &k);
166 if (r < 0)
167 log_syntax(unit, LOG_ERR, filename, line, -r,
168 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
663996b3
MS
169
170 return config_parse_strv(unit, filename, line, section, lvalue, ltype,
171 k ? k : rvalue, data, userdata);
172}
173
174int config_parse_unit_path_printf(const char *unit,
175 const char *filename,
176 unsigned line,
177 const char *section,
178 const char *lvalue,
179 int ltype,
180 const char *rvalue,
181 void *data,
182 void *userdata) {
183
184 Unit *u = userdata;
185 _cleanup_free_ char *k = NULL;
14228c0d 186 int r;
663996b3
MS
187
188 assert(filename);
189 assert(lvalue);
190 assert(rvalue);
191 assert(u);
192
14228c0d
MB
193 r = unit_full_printf(u, rvalue, &k);
194 if (r < 0)
195 log_syntax(unit, LOG_ERR, filename, line, -r,
196 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
663996b3
MS
197
198 return config_parse_path(unit, filename, line, section, lvalue, ltype,
199 k ? k : rvalue, data, userdata);
200}
201
202int config_parse_socket_listen(const char *unit,
203 const char *filename,
204 unsigned line,
205 const char *section,
206 const char *lvalue,
207 int ltype,
208 const char *rvalue,
209 void *data,
210 void *userdata) {
211
212 SocketPort *p, *tail;
213 Socket *s;
14228c0d 214 int r;
663996b3
MS
215
216 assert(filename);
217 assert(lvalue);
218 assert(rvalue);
219 assert(data);
220
221 s = SOCKET(data);
222
223 if (isempty(rvalue)) {
224 /* An empty assignment removes all ports */
225 socket_free_ports(s);
226 return 0;
227 }
228
229 p = new0(SocketPort, 1);
230 if (!p)
231 return log_oom();
232
233 if (ltype != SOCKET_SOCKET) {
234
235 p->type = ltype;
14228c0d
MB
236 r = unit_full_printf(UNIT(s), rvalue, &p->path);
237 if (r < 0) {
663996b3
MS
238 p->path = strdup(rvalue);
239 if (!p->path) {
240 free(p);
241 return log_oom();
242 } else
14228c0d
MB
243 log_syntax(unit, LOG_ERR, filename, line, -r,
244 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
663996b3
MS
245 }
246
247 path_kill_slashes(p->path);
248
249 } else if (streq(lvalue, "ListenNetlink")) {
250 _cleanup_free_ char *k = NULL;
663996b3
MS
251
252 p->type = SOCKET_SOCKET;
14228c0d
MB
253 r = unit_full_printf(UNIT(s), rvalue, &k);
254 if (r < 0)
255 log_syntax(unit, LOG_ERR, filename, line, -r,
256 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
663996b3
MS
257
258 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
259 if (r < 0) {
14228c0d 260 log_syntax(unit, LOG_ERR, filename, line, -r,
663996b3
MS
261 "Failed to parse address value, ignoring: %s", rvalue);
262 free(p);
263 return 0;
264 }
265
266 } else {
267 _cleanup_free_ char *k = NULL;
663996b3
MS
268
269 p->type = SOCKET_SOCKET;
14228c0d
MB
270 r = unit_full_printf(UNIT(s), rvalue, &k);
271 if (r < 0)
272 log_syntax(unit, LOG_ERR, filename, line, -r,
273 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
663996b3
MS
274
275 r = socket_address_parse(&p->address, k ? k : rvalue);
276 if (r < 0) {
14228c0d 277 log_syntax(unit, LOG_ERR, filename, line, -r,
663996b3
MS
278 "Failed to parse address value, ignoring: %s", rvalue);
279 free(p);
280 return 0;
281 }
282
283 if (streq(lvalue, "ListenStream"))
284 p->address.type = SOCK_STREAM;
285 else if (streq(lvalue, "ListenDatagram"))
286 p->address.type = SOCK_DGRAM;
287 else {
288 assert(streq(lvalue, "ListenSequentialPacket"));
289 p->address.type = SOCK_SEQPACKET;
290 }
291
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);
295 free(p);
296 return 0;
297 }
298 }
299
300 p->fd = -1;
301
302 if (s->ports) {
303 LIST_FIND_TAIL(SocketPort, port, s->ports, tail);
304 LIST_INSERT_AFTER(SocketPort, port, s->ports, tail, p);
305 } else
306 LIST_PREPEND(SocketPort, port, s->ports, p);
307
308 return 0;
309}
310
311int config_parse_socket_bind(const char *unit,
312 const char *filename,
313 unsigned line,
314 const char *section,
315 const char *lvalue,
316 int ltype,
317 const char *rvalue,
318 void *data,
319 void *userdata) {
320
321 Socket *s;
322 SocketAddressBindIPv6Only b;
323
324 assert(filename);
325 assert(lvalue);
326 assert(rvalue);
327 assert(data);
328
329 s = SOCKET(data);
330
331 b = socket_address_bind_ipv6_only_from_string(rvalue);
332 if (b < 0) {
333 int r;
334
335 r = parse_boolean(rvalue);
336 if (r < 0) {
337 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
338 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
339 return 0;
340 }
341
342 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
343 } else
344 s->bind_ipv6_only = b;
345
346 return 0;
347}
348
349int config_parse_exec_nice(const char *unit,
350 const char *filename,
351 unsigned line,
352 const char *section,
353 const char *lvalue,
354 int ltype,
355 const char *rvalue,
356 void *data,
357 void *userdata) {
358
359 ExecContext *c = data;
360 int priority, r;
361
362 assert(filename);
363 assert(lvalue);
364 assert(rvalue);
365 assert(data);
366
367 r = safe_atoi(rvalue, &priority);
368 if (r < 0) {
369 log_syntax(unit, LOG_ERR, filename, line, -r,
370 "Failed to parse nice priority, ignoring: %s. ", rvalue);
371 return 0;
372 }
373
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);
377 return 0;
378 }
379
380 c->nice = priority;
381 c->nice_set = true;
382
383 return 0;
384}
385
386int config_parse_exec_oom_score_adjust(const char* unit,
387 const char *filename,
388 unsigned line,
389 const char *section,
390 const char *lvalue,
391 int ltype,
392 const char *rvalue,
393 void *data,
394 void *userdata) {
395
396 ExecContext *c = data;
397 int oa, r;
398
399 assert(filename);
400 assert(lvalue);
401 assert(rvalue);
402 assert(data);
403
404 r = safe_atoi(rvalue, &oa);
405 if (r < 0) {
406 log_syntax(unit, LOG_ERR, filename, line, -r,
407 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
408 return 0;
409 }
410
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);
414 return 0;
415 }
416
417 c->oom_score_adjust = oa;
418 c->oom_score_adjust_set = true;
419
420 return 0;
421}
422
423int config_parse_exec(const char *unit,
424 const char *filename,
425 unsigned line,
426 const char *section,
427 const char *lvalue,
428 int ltype,
429 const char *rvalue,
430 void *data,
431 void *userdata) {
432
433 ExecCommand **e = data, *nce;
434 char *path, **n;
435 unsigned k;
436 int r;
437
438 assert(filename);
439 assert(lvalue);
440 assert(rvalue);
441 assert(e);
442
443 e += ltype;
444
445 if (isempty(rvalue)) {
446 /* An empty assignment resets the list */
447 exec_command_free_list(*e);
448 *e = NULL;
449 return 0;
450 }
451
452 /* We accept an absolute path as first argument, or
453 * alternatively an absolute prefixed with @ to allow
454 * overriding of argv[0]. */
455 for (;;) {
456 int i;
457 char *w;
458 size_t l;
459 char *state;
460 bool honour_argv0 = false, ignore = false;
461
462 path = NULL;
463 nce = NULL;
464 n = NULL;
465
466 rvalue += strspn(rvalue, WHITESPACE);
467
468 if (rvalue[0] == 0)
469 break;
470
471 for (i = 0; i < 2; i++) {
472 if (rvalue[0] == '-' && !ignore) {
473 ignore = true;
474 rvalue ++;
475 }
476
477 if (rvalue[0] == '@' && !honour_argv0) {
478 honour_argv0 = true;
479 rvalue ++;
480 }
481 }
482
483 if (*rvalue != '/') {
484 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
485 "Executable path is not absolute, ignoring: %s", rvalue);
486 return 0;
487 }
488
489 k = 0;
490 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
491 if (strneq(w, ";", MAX(l, 1U)))
492 break;
493
494 k++;
495 }
496
497 n = new(char*, k + !honour_argv0);
498 if (!n)
499 return log_oom();
500
501 k = 0;
502 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
503 if (strneq(w, ";", MAX(l, 1U)))
504 break;
505 else if (strneq(w, "\\;", MAX(l, 1U)))
506 w ++;
507
508 if (honour_argv0 && w == rvalue) {
509 assert(!path);
510
511 path = strndup(w, l);
512 if (!path) {
513 r = log_oom();
514 goto fail;
515 }
516
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",
520 rvalue);
521 r = 0;
522 goto fail;
523 }
524
525 } else {
526 char *c;
527
528 c = n[k++] = cunescape_length(w, l);
529 if (!c) {
530 r = log_oom();
531 goto fail;
532 }
533
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",
537 rvalue);
538 r = 0;
539 goto fail;
540 }
541 }
542 }
543
544 n[k] = NULL;
545
546 if (!n[0]) {
547 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
548 "Invalid command line, ignoring: %s", rvalue);
549 r = 0;
550 goto fail;
551 }
552
553 if (!path) {
554 path = strdup(n[0]);
555 if (!path) {
556 r = log_oom();
557 goto fail;
558 }
559 }
560
561 assert(path_is_absolute(path));
562
563 nce = new0(ExecCommand, 1);
564 if (!nce) {
565 r = log_oom();
566 goto fail;
567 }
568
569 nce->argv = n;
570 nce->path = path;
571 nce->ignore = ignore;
572
573 path_kill_slashes(nce->path);
574
575 exec_command_append_list(e, nce);
576
577 rvalue = state;
578 }
579
580 return 0;
581
582fail:
583 n[k] = NULL;
584 strv_free(n);
585 free(path);
586 free(nce);
587
588 return r;
589}
590
591DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
592DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
593
594int config_parse_socket_bindtodevice(const char* unit,
595 const char *filename,
596 unsigned line,
597 const char *section,
598 const char *lvalue,
599 int ltype,
600 const char *rvalue,
601 void *data,
602 void *userdata) {
603
604 Socket *s = data;
605 char *n;
606
607 assert(filename);
608 assert(lvalue);
609 assert(rvalue);
610 assert(data);
611
612 if (rvalue[0] && !streq(rvalue, "*")) {
613 n = strdup(rvalue);
614 if (!n)
615 return log_oom();
616 } else
617 n = NULL;
618
619 free(s->bind_to_device);
620 s->bind_to_device = n;
621
622 return 0;
623}
624
625DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
626DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
627
628int config_parse_exec_io_class(const char *unit,
629 const char *filename,
630 unsigned line,
631 const char *section,
632 const char *lvalue,
633 int ltype,
634 const char *rvalue,
635 void *data,
636 void *userdata) {
637
638 ExecContext *c = data;
639 int x;
640
641 assert(filename);
642 assert(lvalue);
643 assert(rvalue);
644 assert(data);
645
646 x = ioprio_class_from_string(rvalue);
647 if (x < 0) {
648 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
649 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
650 return 0;
651 }
652
653 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
654 c->ioprio_set = true;
655
656 return 0;
657}
658
659int config_parse_exec_io_priority(const char *unit,
660 const char *filename,
661 unsigned line,
662 const char *section,
663 const char *lvalue,
664 int ltype,
665 const char *rvalue,
666 void *data,
667 void *userdata) {
668
669 ExecContext *c = data;
670 int i, r;
671
672 assert(filename);
673 assert(lvalue);
674 assert(rvalue);
675 assert(data);
676
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);
681 return 0;
682 }
683
684 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
685 c->ioprio_set = true;
686
687 return 0;
688}
689
690int config_parse_exec_cpu_sched_policy(const char *unit,
691 const char *filename,
692 unsigned line,
693 const char *section,
694 const char *lvalue,
695 int ltype,
696 const char *rvalue,
697 void *data,
698 void *userdata) {
699
700
701 ExecContext *c = data;
702 int x;
703
704 assert(filename);
705 assert(lvalue);
706 assert(rvalue);
707 assert(data);
708
709 x = sched_policy_from_string(rvalue);
710 if (x < 0) {
711 log_syntax(unit, LOG_ERR, filename, line, -x,
712 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
713 return 0;
714 }
715
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;
720
721 return 0;
722}
723
724int config_parse_exec_cpu_sched_prio(const char *unit,
725 const char *filename,
726 unsigned line,
727 const char *section,
728 const char *lvalue,
729 int ltype,
730 const char *rvalue,
731 void *data,
732 void *userdata) {
733
734 ExecContext *c = data;
735 int i, min, max, r;
736
737 assert(filename);
738 assert(lvalue);
739 assert(rvalue);
740 assert(data);
741
742 r = safe_atoi(rvalue, &i);
743 if (r < 0) {
744 log_syntax(unit, LOG_ERR, filename, line, -r,
745 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
746 return 0;
747 }
748
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);
752
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);
756 return 0;
757 }
758
759 c->cpu_sched_priority = i;
760 c->cpu_sched_set = true;
761
762 return 0;
763}
764
765int config_parse_exec_cpu_affinity(const char *unit,
766 const char *filename,
767 unsigned line,
768 const char *section,
769 const char *lvalue,
770 int ltype,
771 const char *rvalue,
772 void *data,
773 void *userdata) {
774
775 ExecContext *c = data;
776 char *w;
777 size_t l;
778 char *state;
779
780 assert(filename);
781 assert(lvalue);
782 assert(rvalue);
783 assert(data);
784
785 if (isempty(rvalue)) {
786 /* An empty assignment resets the CPU list */
787 if (c->cpuset)
788 CPU_FREE(c->cpuset);
789 c->cpuset = NULL;
790 return 0;
791 }
792
793 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
794 _cleanup_free_ char *t = NULL;
795 int r;
796 unsigned cpu;
797
798 t = strndup(w, l);
799 if (!t)
800 return log_oom();
801
802 r = safe_atou(t, &cpu);
803
804 if (!c->cpuset) {
805 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
806 if (!c->cpuset)
807 return log_oom();
808 }
809
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);
813 return 0;
814 }
815
816 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
817 }
818
819 return 0;
820}
821
822int config_parse_exec_capabilities(const char *unit,
823 const char *filename,
824 unsigned line,
825 const char *section,
826 const char *lvalue,
827 int ltype,
828 const char *rvalue,
829 void *data,
830 void *userdata) {
831
832 ExecContext *c = data;
833 cap_t cap;
834
835 assert(filename);
836 assert(lvalue);
837 assert(rvalue);
838 assert(data);
839
840 cap = cap_from_text(rvalue);
841 if (!cap) {
842 log_syntax(unit, LOG_ERR, filename, line, errno,
843 "Failed to parse capabilities, ignoring: %s", rvalue);
844 return 0;
845 }
846
847 if (c->capabilities)
848 cap_free(c->capabilities);
849 c->capabilities = cap;
850
851 return 0;
852}
853
854int config_parse_exec_secure_bits(const char *unit,
855 const char *filename,
856 unsigned line,
857 const char *section,
858 const char *lvalue,
859 int ltype,
860 const char *rvalue,
861 void *data,
862 void *userdata) {
863
864 ExecContext *c = data;
865 char *w;
866 size_t l;
867 char *state;
868
869 assert(filename);
870 assert(lvalue);
871 assert(rvalue);
872 assert(data);
873
874 if (isempty(rvalue)) {
875 /* An empty assignment resets the field */
876 c->secure_bits = 0;
877 return 0;
878 }
879
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;
893 else {
894 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
895 "Failed to parse secure bits, ignoring: %s", rvalue);
896 return 0;
897 }
898 }
899
900 return 0;
901}
902
903int config_parse_bounding_set(const char *unit,
904 const char *filename,
905 unsigned line,
906 const char *section,
907 const char *lvalue,
908 int ltype,
909 const char *rvalue,
910 void *data,
911 void *userdata) {
912
913 uint64_t *capability_bounding_set_drop = data;
914 char *w;
915 size_t l;
916 char *state;
917 bool invert = false;
918 uint64_t sum = 0;
919
920 assert(filename);
921 assert(lvalue);
922 assert(rvalue);
923 assert(data);
924
925 if (rvalue[0] == '~') {
926 invert = true;
927 rvalue++;
928 }
929
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
933 * interface. */
934
935 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
936 _cleanup_free_ char *t = NULL;
937 int r;
938 cap_value_t cap;
939
940 t = strndup(w, l);
941 if (!t)
942 return log_oom();
943
944 r = cap_from_name(t, &cap);
945 if (r < 0) {
946 log_syntax(unit, LOG_ERR, filename, line, errno,
947 "Failed to parse capability in bounding set, ignoring: %s", t);
948 continue;
949 }
950
951 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
952 }
953
954 if (invert)
955 *capability_bounding_set_drop |= sum;
956 else
957 *capability_bounding_set_drop |= ~sum;
958
959 return 0;
960}
961
962int config_parse_limit(const char *unit,
963 const char *filename,
964 unsigned line,
965 const char *section,
966 const char *lvalue,
967 int ltype,
968 const char *rvalue,
969 void *data,
970 void *userdata) {
971
972 struct rlimit **rl = data;
973 unsigned long long u;
974
975 assert(filename);
976 assert(lvalue);
977 assert(rvalue);
978 assert(data);
979
980 rl += ltype;
981
982 if (streq(rvalue, "infinity"))
983 u = (unsigned long long) RLIM_INFINITY;
984 else {
985 int r;
986
987 r = safe_atollu(rvalue, &u);
988 if (r < 0) {
989 log_syntax(unit, LOG_ERR, filename, line, -r,
990 "Failed to parse resource value, ignoring: %s", rvalue);
991 return 0;
992 }
993 }
994
995 if (!*rl) {
996 *rl = new(struct rlimit, 1);
997 if (!*rl)
998 return log_oom();
999 }
1000
1001 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
1002 return 0;
1003}
1004
663996b3
MS
1005#ifdef HAVE_SYSV_COMPAT
1006int config_parse_sysv_priority(const char *unit,
1007 const char *filename,
1008 unsigned line,
1009 const char *section,
1010 const char *lvalue,
1011 int ltype,
1012 const char *rvalue,
1013 void *data,
1014 void *userdata) {
1015
1016 int *priority = data;
1017 int i, r;
1018
1019 assert(filename);
1020 assert(lvalue);
1021 assert(rvalue);
1022 assert(data);
1023
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);
1028 return 0;
1029 }
1030
1031 *priority = (int) i;
1032 return 0;
1033}
1034#endif
1035
1036int config_parse_fsck_passno(const char *unit,
1037 const char *filename,
1038 unsigned line,
1039 const char *section,
1040 const char *lvalue,
1041 int ltype,
1042 const char *rvalue,
1043 void *data,
1044 void *userdata) {
1045
1046 int *passno = data;
1047 int i, r;
1048
1049 assert(filename);
1050 assert(lvalue);
1051 assert(rvalue);
1052 assert(data);
1053
1054 r = safe_atoi(rvalue, &i);
1055 if (r || i < 0) {
1056 log_syntax(unit, LOG_ERR, filename, line, -r,
1057 "Failed to parse fsck pass number, ignoring: %s", rvalue);
1058 return 0;
1059 }
1060
1061 *passno = (int) i;
1062 return 0;
1063}
1064
1065DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1066
1067int config_parse_kill_signal(const char *unit,
1068 const char *filename,
1069 unsigned line,
1070 const char *section,
1071 const char *lvalue,
1072 int ltype,
1073 const char *rvalue,
1074 void *data,
1075 void *userdata) {
1076
1077 int *sig = data;
1078 int r;
1079
1080 assert(filename);
1081 assert(lvalue);
1082 assert(rvalue);
1083 assert(sig);
1084
1085 r = signal_from_string_try_harder(rvalue);
1086 if (r <= 0) {
1087 log_syntax(unit, LOG_ERR, filename, line, -r,
1088 "Failed to parse kill signal, ignoring: %s", rvalue);
1089 return 0;
1090 }
1091
1092 *sig = r;
1093 return 0;
1094}
1095
1096int config_parse_exec_mount_flags(const char *unit,
1097 const char *filename,
1098 unsigned line,
1099 const char *section,
1100 const char *lvalue,
1101 int ltype,
1102 const char *rvalue,
1103 void *data,
1104 void *userdata) {
1105
1106 ExecContext *c = data;
1107 char *w;
1108 size_t l;
1109 char *state;
1110 unsigned long flags = 0;
1111
1112 assert(filename);
1113 assert(lvalue);
1114 assert(rvalue);
1115 assert(data);
1116
1117 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
1118 _cleanup_free_ char *t;
1119
1120 t = strndup(w, l);
1121 if (!t)
1122 return log_oom();
1123
1124 if (streq(t, "shared"))
1125 flags |= MS_SHARED;
1126 else if (streq(t, "slave"))
1127 flags |= MS_SLAVE;
1128 else if (streq(w, "private"))
1129 flags |= MS_PRIVATE;
1130 else {
1131 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1132 "Failed to parse mount flag %s, ignoring: %s",
1133 t, rvalue);
1134 return 0;
1135 }
1136 }
1137
1138 c->mount_flags = flags;
1139 return 0;
1140}
1141
1142int config_parse_timer(const char *unit,
1143 const char *filename,
1144 unsigned line,
1145 const char *section,
1146 const char *lvalue,
1147 int ltype,
1148 const char *rvalue,
1149 void *data,
1150 void *userdata) {
1151
1152 Timer *t = data;
1153 usec_t u = 0;
1154 TimerValue *v;
1155 TimerBase b;
1156 CalendarSpec *c = NULL;
1157 clockid_t id;
1158
1159 assert(filename);
1160 assert(lvalue);
1161 assert(rvalue);
1162 assert(data);
1163
1164 if (isempty(rvalue)) {
1165 /* Empty assignment resets list */
1166 timer_free_values(t);
1167 return 0;
1168 }
1169
1170 b = timer_base_from_string(lvalue);
1171 if (b < 0) {
1172 log_syntax(unit, LOG_ERR, filename, line, -b,
1173 "Failed to parse timer base, ignoring: %s", lvalue);
1174 return 0;
1175 }
1176
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",
1181 rvalue);
1182 return 0;
1183 }
1184
1185 id = CLOCK_REALTIME;
1186 } else {
1187 if (parse_sec(rvalue, &u) < 0) {
1188 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1189 "Failed to parse timer value, ignoring: %s",
1190 rvalue);
1191 return 0;
1192 }
1193
1194 id = CLOCK_MONOTONIC;
1195 }
1196
1197 v = new0(TimerValue, 1);
1198 if (!v)
1199 return log_oom();
1200
1201 v->base = b;
1202 v->clock_id = id;
1203 v->value = u;
1204 v->calendar_spec = c;
1205
1206 LIST_PREPEND(TimerValue, value, t->values, v);
1207
1208 return 0;
1209}
1210
1211int config_parse_trigger_unit(
1212 const char *unit,
1213 const char *filename,
1214 unsigned line,
1215 const char *section,
1216 const char *lvalue,
1217 int ltype,
1218 const char *rvalue,
1219 void *data,
1220 void *userdata) {
1221
1222 _cleanup_free_ char *p = NULL;
1223 Unit *u = data;
1224 UnitType type;
1225 int r;
1226
1227 assert(filename);
1228 assert(lvalue);
1229 assert(rvalue);
1230 assert(data);
1231
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);
1235 return 0;
1236 }
1237
14228c0d
MB
1238 r = unit_name_printf(u, rvalue, &p);
1239 if (r < 0)
1240 log_syntax(unit, LOG_ERR, filename, line, -r,
1241 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
663996b3 1242
14228c0d 1243 type = unit_name_to_type(p ?: rvalue);
663996b3
MS
1244 if (type < 0) {
1245 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1246 "Unit type not valid, ignoring: %s", rvalue);
1247 return 0;
1248 }
1249
1250 if (type == u->type) {
1251 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1252 "Trigger cannot be of same type, ignoring: %s", rvalue);
1253 return 0;
1254 }
1255
14228c0d 1256 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
663996b3
MS
1257 if (r < 0) {
1258 log_syntax(unit, LOG_ERR, filename, line, -r,
14228c0d 1259 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
663996b3
MS
1260 return 0;
1261 }
1262
1263 return 0;
1264}
1265
1266int config_parse_path_spec(const char *unit,
1267 const char *filename,
1268 unsigned line,
1269 const char *section,
1270 const char *lvalue,
1271 int ltype,
1272 const char *rvalue,
1273 void *data,
1274 void *userdata) {
1275
1276 Path *p = data;
1277 PathSpec *s;
1278 PathType b;
1279 _cleanup_free_ char *k = NULL;
14228c0d 1280 int r;
663996b3
MS
1281
1282 assert(filename);
1283 assert(lvalue);
1284 assert(rvalue);
1285 assert(data);
1286
1287 if (isempty(rvalue)) {
1288 /* Empty assignment clears list */
1289 path_free_specs(p);
1290 return 0;
1291 }
1292
1293 b = path_type_from_string(lvalue);
1294 if (b < 0) {
1295 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1296 "Failed to parse path type, ignoring: %s", lvalue);
1297 return 0;
1298 }
1299
14228c0d
MB
1300 r = unit_full_printf(UNIT(p), rvalue, &k);
1301 if (r < 0) {
663996b3
MS
1302 k = strdup(rvalue);
1303 if (!k)
1304 return log_oom();
1305 else
14228c0d 1306 log_syntax(unit, LOG_ERR, filename, line, -r,
663996b3
MS
1307 "Failed to resolve unit specifiers on %s. Ignoring.",
1308 rvalue);
1309 }
1310
1311 if (!path_is_absolute(k)) {
1312 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1313 "Path is not absolute, ignoring: %s", k);
1314 return 0;
1315 }
1316
1317 s = new0(PathSpec, 1);
1318 if (!s)
1319 return log_oom();
1320
1321 s->path = path_kill_slashes(k);
1322 k = NULL;
1323 s->type = b;
1324 s->inotify_fd = -1;
1325
1326 LIST_PREPEND(PathSpec, spec, p->specs, s);
1327
1328 return 0;
1329}
1330
1331int config_parse_socket_service(const char *unit,
1332 const char *filename,
1333 unsigned line,
1334 const char *section,
1335 const char *lvalue,
1336 int ltype,
1337 const char *rvalue,
1338 void *data,
1339 void *userdata) {
1340
1341 Socket *s = data;
1342 int r;
1343 DBusError error;
1344 Unit *x;
1345 _cleanup_free_ char *p = NULL;
1346
1347 assert(filename);
1348 assert(lvalue);
1349 assert(rvalue);
1350 assert(data);
1351
1352 dbus_error_init(&error);
1353
14228c0d
MB
1354 r = unit_name_printf(UNIT(s), rvalue, &p);
1355 if (r < 0)
1356 log_syntax(unit, LOG_ERR, filename, line, -r,
1357 "Failed to resolve specifiers, ignoring: %s", rvalue);
663996b3 1358
14228c0d 1359 if (!endswith(p ?: rvalue, ".service")) {
663996b3
MS
1360 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1361 "Unit must be of type service, ignoring: %s", rvalue);
1362 return 0;
1363 }
1364
14228c0d 1365 r = manager_load_unit(UNIT(s)->manager, p ?: rvalue, NULL, &error, &x);
663996b3 1366 if (r < 0) {
14228c0d 1367 log_syntax(unit, LOG_ERR, filename, line, r,
663996b3
MS
1368 "Failed to load unit %s, ignoring: %s",
1369 rvalue, bus_error(&error, r));
1370 dbus_error_free(&error);
1371 return 0;
1372 }
1373
1374 unit_ref_set(&s->service, x);
1375
1376 return 0;
1377}
1378
1379int config_parse_service_sockets(const char *unit,
1380 const char *filename,
1381 unsigned line,
1382 const char *section,
1383 const char *lvalue,
1384 int ltype,
1385 const char *rvalue,
1386 void *data,
1387 void *userdata) {
1388
1389 Service *s = data;
1390 int r;
1391 char *state, *w;
1392 size_t l;
1393
1394 assert(filename);
1395 assert(lvalue);
1396 assert(rvalue);
1397 assert(data);
1398
1399 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1400 _cleanup_free_ char *t = NULL, *k = NULL;
1401
1402 t = strndup(w, l);
1403 if (!t)
1404 return log_oom();
1405
14228c0d
MB
1406 r = unit_name_printf(UNIT(s), t, &k);
1407 if (r < 0)
1408 log_syntax(unit, LOG_ERR, filename, line, -r,
1409 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
663996b3 1410
14228c0d 1411 if (!endswith(k ?: t, ".socket")) {
663996b3 1412 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
14228c0d 1413 "Unit must be of type socket, ignoring: %s", k ?: t);
663996b3
MS
1414 continue;
1415 }
1416
14228c0d 1417 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
663996b3
MS
1418 if (r < 0)
1419 log_syntax(unit, LOG_ERR, filename, line, -r,
1420 "Failed to add dependency on %s, ignoring: %s",
14228c0d 1421 k ?: t, strerror(-r));
663996b3 1422
14228c0d 1423 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
663996b3
MS
1424 if (r < 0)
1425 return r;
1426 }
1427
1428 return 0;
1429}
1430
1431int config_parse_service_timeout(const char *unit,
1432 const char *filename,
1433 unsigned line,
1434 const char *section,
1435 const char *lvalue,
1436 int ltype,
1437 const char *rvalue,
1438 void *data,
1439 void *userdata) {
1440
1441 Service *s = userdata;
1442 int r;
1443
1444 assert(filename);
1445 assert(lvalue);
1446 assert(rvalue);
1447 assert(s);
1448
1449 r = config_parse_sec(unit, filename, line, section, lvalue, ltype,
1450 rvalue, data, userdata);
1451 if (r < 0)
1452 return r;
1453
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;
1459
1460 return 0;
1461}
1462
1463int config_parse_unit_env_file(const char *unit,
1464 const char *filename,
1465 unsigned line,
1466 const char *section,
1467 const char *lvalue,
1468 int ltype,
1469 const char *rvalue,
1470 void *data,
1471 void *userdata) {
1472
1473 char ***env = data;
1474 Unit *u = userdata;
14228c0d
MB
1475 _cleanup_free_ char *n = NULL;
1476 const char *s;
663996b3
MS
1477 int r;
1478
1479 assert(filename);
1480 assert(lvalue);
1481 assert(rvalue);
1482 assert(data);
1483
1484 if (isempty(rvalue)) {
1485 /* Empty assignment frees the list */
1486 strv_free(*env);
1487 *env = NULL;
1488 return 0;
1489 }
1490
14228c0d
MB
1491 r = unit_full_printf(u, rvalue, &n);
1492 if (r < 0)
1493 log_syntax(unit, LOG_ERR, filename, line, r,
1494 "Failed to resolve specifiers, ignoring: %s", rvalue);
663996b3 1495
14228c0d 1496 s = n ?: rvalue;
663996b3
MS
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);
1500 return 0;
1501 }
1502
1503 r = strv_extend(env, s);
1504 if (r < 0)
1505 return log_oom();
1506
1507 return 0;
1508}
1509
1510int config_parse_environ(const char *unit,
1511 const char *filename,
1512 unsigned line,
1513 const char *section,
1514 const char *lvalue,
1515 int ltype,
1516 const char *rvalue,
1517 void *data,
1518 void *userdata) {
1519
1520 Unit *u = userdata;
1521 char*** env = data, *w, *state;
1522 size_t l;
1523 _cleanup_free_ char *k = NULL;
14228c0d 1524 int r;
663996b3
MS
1525
1526 assert(filename);
1527 assert(lvalue);
1528 assert(rvalue);
14228c0d 1529 assert(data);
663996b3
MS
1530
1531 if (isempty(rvalue)) {
1532 /* Empty assignment resets the list */
1533 strv_free(*env);
1534 *env = NULL;
1535 return 0;
1536 }
1537
14228c0d
MB
1538 if (u) {
1539 r = unit_full_printf(u, rvalue, &k);
1540 if (r < 0)
1541 log_syntax(unit, LOG_ERR, filename, line, -r,
1542 "Failed to resolve specifiers, ignoring: %s", rvalue);
1543 }
1544
1545 if (!k)
1546 k = strdup(rvalue);
663996b3
MS
1547 if (!k)
1548 return log_oom();
1549
1550 FOREACH_WORD_QUOTED(w, l, k, state) {
1551 _cleanup_free_ char *n;
1552 char **x;
1553
1554 n = cunescape_length(w, l);
1555 if (!n)
1556 return log_oom();
1557
1558 if (!env_assignment_is_valid(n)) {
1559 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1560 "Invalid environment assignment, ignoring: %s", rvalue);
1561 continue;
1562 }
1563
1564 x = strv_env_set(*env, n);
1565 if (!x)
1566 return log_oom();
1567
1568 strv_free(*env);
1569 *env = x;
1570 }
1571
1572 return 0;
1573}
1574
1575int config_parse_ip_tos(const char *unit,
1576 const char *filename,
1577 unsigned line,
1578 const char *section,
1579 const char *lvalue,
1580 int ltype,
1581 const char *rvalue,
1582 void *data,
1583 void *userdata) {
1584
1585 int *ip_tos = data, x;
1586
1587 assert(filename);
1588 assert(lvalue);
1589 assert(rvalue);
1590 assert(data);
1591
1592 x = ip_tos_from_string(rvalue);
1593 if (x < 0) {
1594 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1595 "Failed to parse IP TOS value, ignoring: %s", rvalue);
1596 return 0;
1597 }
1598
1599 *ip_tos = x;
1600 return 0;
1601}
1602
1603int config_parse_unit_condition_path(const char *unit,
1604 const char *filename,
1605 unsigned line,
1606 const char *section,
1607 const char *lvalue,
1608 int ltype,
1609 const char *rvalue,
1610 void *data,
1611 void *userdata) {
1612
1613 ConditionType cond = ltype;
1614 Unit *u = data;
1615 bool trigger, negate;
1616 Condition *c;
1617 _cleanup_free_ char *p = NULL;
14228c0d 1618 int r;
663996b3
MS
1619
1620 assert(filename);
1621 assert(lvalue);
1622 assert(rvalue);
1623 assert(data);
1624
1625 if (isempty(rvalue)) {
1626 /* Empty assignment resets the list */
1627 condition_free_list(u->conditions);
1628 u->conditions = NULL;
1629 return 0;
1630 }
1631
1632 trigger = rvalue[0] == '|';
1633 if (trigger)
1634 rvalue++;
1635
1636 negate = rvalue[0] == '!';
1637 if (negate)
1638 rvalue++;
1639
14228c0d
MB
1640 r = unit_full_printf(u, rvalue, &p);
1641 if (r < 0)
1642 log_syntax(unit, LOG_ERR, filename, line, -r,
1643 "Failed to resolve specifiers, ignoring: %s", rvalue);
1644 if (!p) {
1645 p = strdup(rvalue);
1646 if (!p)
1647 return log_oom();
1648 }
663996b3
MS
1649
1650 if (!path_is_absolute(p)) {
1651 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1652 "Path in condition not absolute, ignoring: %s", p);
1653 return 0;
1654 }
1655
1656 c = condition_new(cond, p, trigger, negate);
1657 if (!c)
1658 return log_oom();
1659
1660 LIST_PREPEND(Condition, conditions, u->conditions, c);
1661 return 0;
1662}
1663
1664int config_parse_unit_condition_string(const char *unit,
1665 const char *filename,
1666 unsigned line,
1667 const char *section,
1668 const char *lvalue,
1669 int ltype,
1670 const char *rvalue,
1671 void *data,
1672 void *userdata) {
1673
1674 ConditionType cond = ltype;
1675 Unit *u = data;
1676 bool trigger, negate;
1677 Condition *c;
1678 _cleanup_free_ char *s = NULL;
14228c0d 1679 int r;
663996b3
MS
1680
1681 assert(filename);
1682 assert(lvalue);
1683 assert(rvalue);
1684 assert(data);
1685
1686 if (isempty(rvalue)) {
1687 /* Empty assignment resets the list */
1688 condition_free_list(u->conditions);
1689 u->conditions = NULL;
1690 return 0;
1691 }
1692
1693 trigger = rvalue[0] == '|';
1694 if (trigger)
1695 rvalue++;
1696
1697 negate = rvalue[0] == '!';
1698 if (negate)
1699 rvalue++;
1700
14228c0d
MB
1701 r = unit_full_printf(u, rvalue, &s);
1702 if (r < 0)
1703 log_syntax(unit, LOG_ERR, filename, line, -r,
1704 "Failed to resolve specifiers, ignoring: %s", rvalue);
1705 if (!s) {
1706 s = strdup(rvalue);
1707 if (!s)
1708 return log_oom();
1709 }
663996b3
MS
1710
1711 c = condition_new(cond, s, trigger, negate);
1712 if (!c)
1713 return log_oom();
1714
1715 LIST_PREPEND(Condition, conditions, u->conditions, c);
1716 return 0;
1717}
1718
1719int config_parse_unit_condition_null(const char *unit,
1720 const char *filename,
1721 unsigned line,
1722 const char *section,
1723 const char *lvalue,
1724 int ltype,
1725 const char *rvalue,
1726 void *data,
1727 void *userdata) {
1728
1729 Unit *u = data;
1730 Condition *c;
1731 bool trigger, negate;
1732 int b;
1733
1734 assert(filename);
1735 assert(lvalue);
1736 assert(rvalue);
1737 assert(data);
1738
1739 if (isempty(rvalue)) {
1740 /* Empty assignment resets the list */
1741 condition_free_list(u->conditions);
1742 u->conditions = NULL;
1743 return 0;
1744 }
1745
1746 trigger = rvalue[0] == '|';
1747 if (trigger)
1748 rvalue++;
1749
1750 negate = rvalue[0] == '!';
1751 if (negate)
1752 rvalue++;
1753
1754 b = parse_boolean(rvalue);
1755 if (b < 0) {
1756 log_syntax(unit, LOG_ERR, filename, line, -b,
1757 "Failed to parse boolean value in condition, ignoring: %s",
1758 rvalue);
1759 return 0;
1760 }
1761
1762 if (!b)
1763 negate = !negate;
1764
1765 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1766 if (!c)
1767 return log_oom();
1768
1769 LIST_PREPEND(Condition, conditions, u->conditions, c);
1770 return 0;
1771}
1772
1773DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1774DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
1775
14228c0d
MB
1776int config_parse_unit_requires_mounts_for(
1777 const char *unit,
1778 const char *filename,
1779 unsigned line,
1780 const char *section,
1781 const char *lvalue,
1782 int ltype,
1783 const char *rvalue,
1784 void *data,
1785 void *userdata) {
663996b3 1786
14228c0d
MB
1787 Unit *u = userdata;
1788 char *state;
1789 size_t l;
1790 char *w;
663996b3
MS
1791
1792 assert(filename);
1793 assert(lvalue);
1794 assert(rvalue);
1795 assert(data);
1796
14228c0d
MB
1797 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1798 int r;
1799 _cleanup_free_ char *n;
663996b3 1800
14228c0d
MB
1801 n = strndup(w, l);
1802 if (!n)
1803 return log_oom();
663996b3 1804
14228c0d
MB
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);
1808 continue;
1809 }
663996b3 1810
14228c0d
MB
1811 r = unit_require_mounts_for(u, n);
1812 if (r < 0) {
1813 log_syntax(unit, LOG_ERR, filename, line, r,
1814 "Failed to add required mount for, ignoring: %s", rvalue);
1815 continue;
1816 }
663996b3
MS
1817 }
1818
1819 return 0;
1820}
1821
663996b3
MS
1822int config_parse_documentation(const char *unit,
1823 const char *filename,
1824 unsigned line,
1825 const char *section,
1826 const char *lvalue,
1827 int ltype,
1828 const char *rvalue,
1829 void *data,
1830 void *userdata) {
1831
1832 Unit *u = userdata;
1833 int r;
1834 char **a, **b;
1835
1836 assert(filename);
1837 assert(lvalue);
1838 assert(rvalue);
1839 assert(u);
1840
1841 if (isempty(rvalue)) {
1842 /* Empty assignment resets the list */
1843 strv_free(u->documentation);
1844 u->documentation = NULL;
1845 return 0;
1846 }
1847
1848 r = config_parse_unit_strv_printf(unit, filename, line, section, lvalue, ltype,
1849 rvalue, data, userdata);
1850 if (r < 0)
1851 return r;
1852
1853 for (a = b = u->documentation; a && *a; a++) {
1854
1855 if (is_valid_documentation_url(*a))
1856 *(b++) = *a;
1857 else {
1858 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1859 "Invalid URL, ignoring: %s", *a);
1860 free(*a);
1861 }
1862 }
1863 *b = NULL;
1864
1865 return r;
1866}
1867
1868static void syscall_set(uint32_t *p, int nr) {
1869 nr = SYSCALL_TO_INDEX(nr);
1870 p[nr >> 4] |= 1 << (nr & 31);
1871}
1872
1873static void syscall_unset(uint32_t *p, int nr) {
1874 nr = SYSCALL_TO_INDEX(nr);
1875 p[nr >> 4] &= ~(1 << (nr & 31));
1876}
1877
1878int config_parse_syscall_filter(const char *unit,
1879 const char *filename,
1880 unsigned line,
1881 const char *section,
1882 const char *lvalue,
1883 int ltype,
1884 const char *rvalue,
1885 void *data,
1886 void *userdata) {
1887
1888 ExecContext *c = data;
1889 Unit *u = userdata;
1890 bool invert = false;
1891 char *w;
1892 size_t l;
1893 char *state;
1894
1895 assert(filename);
1896 assert(lvalue);
1897 assert(rvalue);
1898 assert(u);
1899
1900 if (isempty(rvalue)) {
1901 /* Empty assignment resets the list */
1902 free(c->syscall_filter);
1903 c->syscall_filter = NULL;
1904 return 0;
1905 }
1906
1907 if (rvalue[0] == '~') {
1908 invert = true;
1909 rvalue++;
1910 }
1911
1912 if (!c->syscall_filter) {
1913 size_t n;
1914
1915 n = (syscall_max() + 31) >> 4;
1916 c->syscall_filter = new(uint32_t, n);
1917 if (!c->syscall_filter)
1918 return log_oom();
1919
1920 memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
1921
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);
1927#endif
1928 syscall_set(c->syscall_filter, __NR_exit_group);
1929 syscall_set(c->syscall_filter, __NR_exit);
1930 }
1931
1932 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1933 int id;
1934 _cleanup_free_ char *t = NULL;
1935
1936 t = strndup(w, l);
1937 if (!t)
1938 return log_oom();
1939
1940 id = syscall_from_name(t);
1941 if (id < 0) {
1942 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1943 "Failed to parse syscall, ignoring: %s", t);
1944 continue;
1945 }
1946
1947 if (invert)
1948 syscall_unset(c->syscall_filter, id);
1949 else
1950 syscall_set(c->syscall_filter, id);
1951 }
1952
1953 c->no_new_privileges = true;
1954
1955 return 0;
1956}
1957
14228c0d
MB
1958int config_parse_unit_slice(
1959 const char *unit,
1960 const char *filename,
1961 unsigned line,
1962 const char *section,
1963 const char *lvalue,
1964 int ltype,
1965 const char *rvalue,
1966 void *data,
1967 void *userdata) {
1968
1969 _cleanup_free_ char *k = NULL;
1970 Unit *u = userdata, *slice;
1971 int r;
1972
1973 assert(filename);
1974 assert(lvalue);
1975 assert(rvalue);
1976 assert(u);
1977
1978 r = unit_name_printf(u, rvalue, &k);
1979 if (r < 0)
1980 log_syntax(unit, LOG_ERR, filename, line, -r,
1981 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
1982 if (!k) {
1983 k = strdup(rvalue);
1984 if (!k)
1985 return log_oom();
1986 }
1987
1988 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
1989 if (r < 0) {
1990 log_syntax(unit, LOG_ERR, filename, line, -r,
1991 "Failed to load slice unit %s. Ignoring.", k);
1992 return 0;
1993 }
1994
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);
1998 return 0;
1999 }
2000
2001 unit_ref_set(&u->slice, slice);
2002 return 0;
2003}
2004
2005DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2006
2007int config_parse_cpu_shares(
2008 const char *unit,
2009 const char *filename,
2010 unsigned line,
2011 const char *section,
2012 const char *lvalue,
2013 int ltype,
2014 const char *rvalue,
2015 void *data,
2016 void *userdata) {
2017
2018 CGroupContext *c = data;
2019 unsigned long lu;
2020 int r;
2021
2022 assert(filename);
2023 assert(lvalue);
2024 assert(rvalue);
2025
2026 if (isempty(rvalue)) {
2027 c->cpu_shares = 1024;
2028 return 0;
2029 }
2030
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);
2035 return 0;
2036 }
2037
2038 c->cpu_shares = lu;
2039 return 0;
2040}
2041
2042int config_parse_memory_limit(
2043 const char *unit,
2044 const char *filename,
2045 unsigned line,
2046 const char *section,
2047 const char *lvalue,
2048 int ltype,
2049 const char *rvalue,
2050 void *data,
2051 void *userdata) {
2052
2053 CGroupContext *c = data;
2054 off_t bytes;
2055 int r;
2056
2057 if (isempty(rvalue)) {
2058 c->memory_limit = (uint64_t) -1;
2059 return 0;
2060 }
2061
2062 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2063
2064 r = parse_bytes(rvalue, &bytes);
2065 if (r < 0) {
2066 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2067 "Memory limit '%s' invalid. Ignoring.", rvalue);
2068 return 0;
2069 }
2070
2071 c->memory_limit = (uint64_t) bytes;
2072 return 0;
2073}
2074
2075int config_parse_device_allow(
2076 const char *unit,
2077 const char *filename,
2078 unsigned line,
2079 const char *section,
2080 const char *lvalue,
2081 int ltype,
2082 const char *rvalue,
2083 void *data,
2084 void *userdata) {
2085
2086 _cleanup_free_ char *path = NULL;
2087 CGroupContext *c = data;
2088 CGroupDeviceAllow *a;
2089 const char *m;
2090 size_t n;
2091
2092 if (isempty(rvalue)) {
2093 while (c->device_allow)
2094 cgroup_context_free_device_allow(c, c->device_allow);
2095
2096 return 0;
2097 }
2098
2099 n = strcspn(rvalue, WHITESPACE);
2100 path = strndup(rvalue, n);
2101 if (!path)
2102 return log_oom();
2103
2104 if (!path_startswith(path, "/dev")) {
2105 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2106 "Invalid device node path '%s'. Ignoring.", path);
2107 return 0;
2108 }
2109
2110 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2111 if (isempty(m))
2112 m = "rwm";
2113
2114 if (!in_charset(m, "rwm")) {
2115 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2116 "Invalid device rights '%s'. Ignoring.", m);
2117 return 0;
2118 }
2119
2120 a = new0(CGroupDeviceAllow, 1);
2121 if (!a)
2122 return log_oom();
2123
2124 a->path = path;
2125 path = NULL;
2126 a->r = !!strchr(m, 'r');
2127 a->w = !!strchr(m, 'w');
2128 a->m = !!strchr(m, 'm');
2129
2130 LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
2131 return 0;
2132}
2133
2134int config_parse_blockio_weight(
2135 const char *unit,
2136 const char *filename,
2137 unsigned line,
2138 const char *section,
2139 const char *lvalue,
2140 int ltype,
2141 const char *rvalue,
2142 void *data,
2143 void *userdata) {
2144
2145 CGroupContext *c = data;
2146 unsigned long lu;
2147 int r;
2148
2149 assert(filename);
2150 assert(lvalue);
2151 assert(rvalue);
2152
2153 if (isempty(rvalue)) {
2154 c->blockio_weight = 1000;
2155 return 0;
2156 }
2157
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);
2162 return 0;
2163 }
2164
2165 c->blockio_weight = lu;
2166
2167 return 0;
2168}
2169
2170int config_parse_blockio_device_weight(
2171 const char *unit,
2172 const char *filename,
2173 unsigned line,
2174 const char *section,
2175 const char *lvalue,
2176 int ltype,
2177 const char *rvalue,
2178 void *data,
2179 void *userdata) {
2180
2181 _cleanup_free_ char *path = NULL;
2182 CGroupBlockIODeviceWeight *w;
2183 CGroupContext *c = data;
2184 unsigned long lu;
2185 const char *weight;
2186 size_t n;
2187 int r;
2188
2189 assert(filename);
2190 assert(lvalue);
2191 assert(rvalue);
2192
2193 if (isempty(rvalue)) {
2194 while (c->blockio_device_weights)
2195 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2196
2197 return 0;
2198 }
2199
2200 n = strcspn(rvalue, WHITESPACE);
2201 weight = rvalue + n;
2202 if (!*weight) {
2203 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2204 "Expected block device and device weight. Ignoring.");
2205 return 0;
2206 }
2207
2208 path = strndup(rvalue, n);
2209 if (!path)
2210 return log_oom();
2211
2212 if (!path_startswith(path, "/dev")) {
2213 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2214 "Invalid device node path '%s'. Ignoring.", path);
2215 return 0;
2216 }
2217
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);
2223 return 0;
2224 }
2225
2226
2227 w = new0(CGroupBlockIODeviceWeight, 1);
2228 if (!w)
2229 return log_oom();
2230
2231 w->path = path;
2232 path = NULL;
2233
2234 w->weight = lu;
2235
2236 LIST_PREPEND(CGroupBlockIODeviceWeight, device_weights, c->blockio_device_weights, w);
2237 return 0;
2238}
2239
2240int config_parse_blockio_bandwidth(
2241 const char *unit,
2242 const char *filename,
2243 unsigned line,
2244 const char *section,
2245 const char *lvalue,
2246 int ltype,
2247 const char *rvalue,
2248 void *data,
2249 void *userdata) {
2250
2251 _cleanup_free_ char *path = NULL;
2252 CGroupBlockIODeviceBandwidth *b;
2253 CGroupContext *c = data;
2254 const char *bandwidth;
2255 off_t bytes;
2256 bool read;
2257 size_t n;
2258 int r;
2259
2260 assert(filename);
2261 assert(lvalue);
2262 assert(rvalue);
2263
2264 read = streq("BlockIOReadBandwidth", lvalue);
2265
2266 if (isempty(rvalue)) {
2267 CGroupBlockIODeviceBandwidth *next;
2268
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);
2272
2273 return 0;
2274 }
2275
2276 n = strcspn(rvalue, WHITESPACE);
2277 bandwidth = rvalue + n;
2278 bandwidth += strspn(bandwidth, WHITESPACE);
2279
2280 if (!*bandwidth) {
2281 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2282 "Expected space separated pair of device node and bandwidth. Ignoring.");
2283 return 0;
2284 }
2285
2286 path = strndup(rvalue, n);
2287 if (!path)
2288 return log_oom();
2289
2290 if (!path_startswith(path, "/dev")) {
2291 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2292 "Invalid device node path '%s'. Ignoring.", path);
2293 return 0;
2294 }
2295
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);
2300 return 0;
2301 }
2302
2303 b = new0(CGroupBlockIODeviceBandwidth, 1);
2304 if (!b)
2305 return log_oom();
2306
2307 b->path = path;
2308 path = NULL;
2309 b->bandwidth = (uint64_t) bytes;
2310 b->read = read;
2311
2312 LIST_PREPEND(CGroupBlockIODeviceBandwidth, device_bandwidths, c->blockio_device_bandwidths, b);
2313
2314 return 0;
2315}
2316
663996b3
MS
2317#define FOLLOW_MAX 8
2318
2319static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
2320 unsigned c = 0;
2321 int fd, r;
2322 FILE *f;
2323 char *id = NULL;
2324
2325 assert(filename);
2326 assert(*filename);
2327 assert(_f);
2328 assert(names);
2329
2330 /* This will update the filename pointer if the loaded file is
2331 * reached by a symlink. The old string will be freed. */
2332
2333 for (;;) {
2334 char *target, *name;
2335
2336 if (c++ >= FOLLOW_MAX)
2337 return -ELOOP;
2338
2339 path_kill_slashes(*filename);
2340
2341 /* Add the file name we are currently looking at to
2342 * the names of this unit, but only if it is a valid
2343 * unit name. */
2344 name = path_get_file_name(*filename);
2345
2346 if (unit_name_is_valid(name, true)) {
2347
2348 id = set_get(names, name);
2349 if (!id) {
2350 id = strdup(name);
2351 if (!id)
2352 return -ENOMEM;
2353
2354 r = set_consume(names, id);
2355 if (r < 0)
2356 return r;
2357 }
2358 }
2359
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);
2362 if (fd >= 0)
2363 break;
2364
2365 if (errno != ELOOP)
2366 return -errno;
2367
2368 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2369 r = readlink_and_make_absolute(*filename, &target);
2370 if (r < 0)
2371 return r;
2372
2373 free(*filename);
2374 *filename = target;
2375 }
2376
2377 f = fdopen(fd, "re");
2378 if (!f) {
2379 r = -errno;
2380 close_nointr_nofail(fd);
2381 return r;
2382 }
2383
2384 *_f = f;
2385 *_final = id;
2386 return 0;
2387}
2388
2389static int merge_by_names(Unit **u, Set *names, const char *id) {
2390 char *k;
2391 int r;
2392
2393 assert(u);
2394 assert(*u);
2395 assert(names);
2396
2397 /* Let's try to add in all symlink names we found */
2398 while ((k = set_steal_first(names))) {
2399
2400 /* First try to merge in the other name into our
2401 * unit */
2402 r = unit_merge_by_name(*u, k);
2403 if (r < 0) {
2404 Unit *other;
2405
2406 /* Hmm, we couldn't merge the other unit into
2407 * ours? Then let's try it the other way
2408 * round */
2409
2410 other = manager_get_unit((*u)->manager, k);
2411 free(k);
2412
2413 if (other) {
2414 r = unit_merge(other, *u);
2415 if (r >= 0) {
2416 *u = other;
2417 return merge_by_names(u, names, NULL);
2418 }
2419 }
2420
2421 return r;
2422 }
2423
2424 if (id == k)
2425 unit_choose_id(*u, id);
2426
2427 free(k);
2428 }
2429
2430 return 0;
2431}
2432
2433static int load_from_path(Unit *u, const char *path) {
2434 int r;
2435 Set *symlink_names;
2436 FILE *f = NULL;
2437 char *filename = NULL, *id = NULL;
2438 Unit *merged;
2439 struct stat st;
2440
2441 assert(u);
2442 assert(path);
2443
2444 symlink_names = set_new(string_hash_func, string_compare_func);
2445 if (!symlink_names)
2446 return -ENOMEM;
2447
2448 if (path_is_absolute(path)) {
2449
2450 filename = strdup(path);
2451 if (!filename) {
2452 r = -ENOMEM;
2453 goto finish;
2454 }
2455
2456 r = open_follow(&filename, &f, symlink_names, &id);
2457 if (r < 0) {
2458 free(filename);
2459 filename = NULL;
2460
2461 if (r != -ENOENT)
2462 goto finish;
2463 }
2464
2465 } else {
2466 char **p;
2467
2468 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
2469
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);
2474 if (!filename) {
2475 r = -ENOMEM;
2476 goto finish;
2477 }
2478
2479 if (u->manager->unit_path_cache &&
2480 !set_get(u->manager->unit_path_cache, filename))
2481 r = -ENOENT;
2482 else
2483 r = open_follow(&filename, &f, symlink_names, &id);
2484
2485 if (r < 0) {
2486 free(filename);
2487 filename = NULL;
2488
2489 if (r != -ENOENT)
2490 goto finish;
2491
2492 /* Empty the symlink names for the next run */
2493 set_clear_free(symlink_names);
2494 continue;
2495 }
2496
2497 break;
2498 }
2499 }
2500
2501 if (!filename) {
2502 /* Hmm, no suitable file found? */
2503 r = 0;
2504 goto finish;
2505 }
2506
2507 merged = u;
2508 r = merge_by_names(&merged, symlink_names, id);
2509 if (r < 0)
2510 goto finish;
2511
2512 if (merged != u) {
2513 u->load_state = UNIT_MERGED;
2514 r = 0;
2515 goto finish;
2516 }
2517
2518 if (fstat(fileno(f), &st) < 0) {
2519 r = -errno;
2520 goto finish;
2521 }
2522
2523 if (null_or_empty(&st))
2524 u->load_state = UNIT_MASKED;
2525 else {
14228c0d
MB
2526 u->load_state = UNIT_LOADED;
2527
663996b3
MS
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);
2532 if (r < 0)
2533 goto finish;
663996b3
MS
2534 }
2535
2536 free(u->fragment_path);
2537 u->fragment_path = filename;
2538 filename = NULL;
2539
2540 u->fragment_mtime = timespec_load(&st.st_mtim);
2541
2542 if (u->source_path) {
2543 if (stat(u->source_path, &st) >= 0)
2544 u->source_mtime = timespec_load(&st.st_mtim);
2545 else
2546 u->source_mtime = 0;
2547 }
2548
2549 r = 0;
2550
2551finish:
2552 set_free_free(symlink_names);
2553 free(filename);
2554
2555 if (f)
2556 fclose(f);
2557
2558 return r;
2559}
2560
2561int unit_load_fragment(Unit *u) {
2562 int r;
2563 Iterator i;
2564 const char *t;
2565
2566 assert(u);
2567 assert(u->load_state == UNIT_STUB);
2568 assert(u->id);
2569
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);
2574 if (r < 0)
2575 return r;
2576
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) {
2580
2581 if (t == u->id)
2582 continue;
2583
2584 r = load_from_path(u, t);
2585 if (r < 0)
2586 return r;
2587
2588 if (u->load_state != UNIT_STUB)
2589 break;
2590 }
2591
2592 /* And now, try looking for it under the suggested (originally linked) path */
2593 if (u->load_state == UNIT_STUB && u->fragment_path) {
2594
2595 r = load_from_path(u, u->fragment_path);
2596 if (r < 0)
2597 return r;
2598
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;
2605 }
2606 }
2607
2608 /* Look for a template */
2609 if (u->load_state == UNIT_STUB && u->instance) {
2610 char *k;
2611
2612 k = unit_name_template(u->id);
2613 if (!k)
2614 return -ENOMEM;
2615
2616 r = load_from_path(u, k);
2617 free(k);
2618
2619 if (r < 0)
2620 return r;
2621
2622 if (u->load_state == UNIT_STUB)
2623 SET_FOREACH(t, u->names, i) {
2624
2625 if (t == u->id)
2626 continue;
2627
2628 k = unit_name_template(t);
2629 if (!k)
2630 return -ENOMEM;
2631
2632 r = load_from_path(u, k);
2633 free(k);
2634
2635 if (r < 0)
2636 return r;
2637
2638 if (u->load_state != UNIT_STUB)
2639 break;
2640 }
2641 }
2642
2643 return 0;
2644}
2645
2646void unit_dump_config_items(FILE *f) {
2647 static const struct {
2648 const ConfigParserCallback callback;
2649 const char *rvalue;
2650 } table[] = {
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" },
663996b3
MS
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" },
2682#else
2683 { config_parse_warn_compat, "NOTSUPPORTED" },
2684#endif
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" },
14228c0d
MB
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" },
663996b3
MS
2722 };
2723
2724 const char *prev = NULL;
2725 const char *i;
2726
2727 assert(f);
2728
2729 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2730 const char *rvalue = "OTHER", *lvalue;
2731 unsigned j;
2732 size_t prefix_len;
2733 const char *dot;
2734 const ConfigPerfItem *p;
2735
2736 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2737
2738 dot = strchr(i, '.');
2739 lvalue = dot ? dot + 1 : i;
2740 prefix_len = dot-i;
2741
2742 if (dot)
2743 if (!prev || !strneq(prev, i, prefix_len+1)) {
2744 if (prev)
2745 fputc('\n', f);
2746
2747 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2748 }
2749
2750 for (j = 0; j < ELEMENTSOF(table); j++)
2751 if (p->parse == table[j].callback) {
2752 rvalue = table[j].rvalue;
2753 break;
2754 }
2755
2756 fprintf(f, "%s=%s\n", lvalue, rvalue);
2757 prev = i;
2758 }
2759}