]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/load-fragment.c
man: improve wording of systemctl's --after/--before
[thirdparty/systemd.git] / src / core / load-fragment.c
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 #include <sys/types.h>
37 #include <grp.h>
38
39 #ifdef HAVE_SECCOMP
40 #include <seccomp.h>
41 #endif
42
43 #include "sd-messages.h"
44 #include "unit.h"
45 #include "strv.h"
46 #include "conf-parser.h"
47 #include "load-fragment.h"
48 #include "log.h"
49 #include "ioprio.h"
50 #include "securebits.h"
51 #include "missing.h"
52 #include "unit-name.h"
53 #include "unit-printf.h"
54 #include "utf8.h"
55 #include "path-util.h"
56 #include "env-util.h"
57 #include "cgroup.h"
58 #include "bus-util.h"
59 #include "bus-error.h"
60 #include "errno-list.h"
61 #include "af-list.h"
62
63 #ifdef HAVE_SECCOMP
64 #include "seccomp-util.h"
65 #endif
66
67 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
68 int config_parse_warn_compat(
69 const char *unit,
70 const char *filename,
71 unsigned line,
72 const char *section,
73 unsigned section_line,
74 const char *lvalue,
75 int ltype,
76 const char *rvalue,
77 void *data,
78 void *userdata) {
79
80 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
81 "Support for option %s= has been disabled at compile time and is ignored",
82 lvalue);
83 return 0;
84 }
85 #endif
86
87 int config_parse_unit_deps(const char* unit,
88 const char *filename,
89 unsigned line,
90 const char *section,
91 unsigned section_line,
92 const char *lvalue,
93 int ltype,
94 const char *rvalue,
95 void *data,
96 void *userdata) {
97
98 UnitDependency d = ltype;
99 Unit *u = userdata;
100 char *w;
101 size_t l;
102 char *state;
103
104 assert(filename);
105 assert(lvalue);
106 assert(rvalue);
107
108 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
109 _cleanup_free_ char *t = NULL, *k = NULL;
110 int r;
111
112 t = strndup(w, l);
113 if (!t)
114 return log_oom();
115
116 r = unit_name_printf(u, t, &k);
117 if (r < 0) {
118 log_syntax(unit, LOG_ERR, filename, line, -r,
119 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
120 continue;
121 }
122
123 r = unit_add_dependency_by_name(u, d, k, NULL, true);
124 if (r < 0)
125 log_syntax(unit, LOG_ERR, filename, line, -r,
126 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
127 }
128
129 return 0;
130 }
131
132 int config_parse_unit_string_printf(const char *unit,
133 const char *filename,
134 unsigned line,
135 const char *section,
136 unsigned section_line,
137 const char *lvalue,
138 int ltype,
139 const char *rvalue,
140 void *data,
141 void *userdata) {
142
143 Unit *u = userdata;
144 _cleanup_free_ char *k = NULL;
145 int r;
146
147 assert(filename);
148 assert(lvalue);
149 assert(rvalue);
150 assert(u);
151
152 r = unit_full_printf(u, rvalue, &k);
153 if (r < 0)
154 log_syntax(unit, LOG_ERR, filename, line, -r,
155 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
156
157 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
158 k ? k : rvalue, data, userdata);
159 }
160
161 int config_parse_unit_strv_printf(const char *unit,
162 const char *filename,
163 unsigned line,
164 const char *section,
165 unsigned section_line,
166 const char *lvalue,
167 int ltype,
168 const char *rvalue,
169 void *data,
170 void *userdata) {
171
172 Unit *u = userdata;
173 _cleanup_free_ char *k = NULL;
174 int r;
175
176 assert(filename);
177 assert(lvalue);
178 assert(rvalue);
179 assert(u);
180
181 r = unit_full_printf(u, rvalue, &k);
182 if (r < 0)
183 log_syntax(unit, LOG_ERR, filename, line, -r,
184 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
185
186 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
187 k ? k : rvalue, data, userdata);
188 }
189
190 int config_parse_unit_path_printf(const char *unit,
191 const char *filename,
192 unsigned line,
193 const char *section,
194 unsigned section_line,
195 const char *lvalue,
196 int ltype,
197 const char *rvalue,
198 void *data,
199 void *userdata) {
200
201 Unit *u = userdata;
202 _cleanup_free_ char *k = NULL;
203 int r;
204
205 assert(filename);
206 assert(lvalue);
207 assert(rvalue);
208 assert(u);
209
210 r = unit_full_printf(u, rvalue, &k);
211 if (r < 0)
212 log_syntax(unit, LOG_ERR, filename, line, -r,
213 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
214
215 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype,
216 k ? k : rvalue, data, userdata);
217 }
218
219 int config_parse_socket_listen(const char *unit,
220 const char *filename,
221 unsigned line,
222 const char *section,
223 unsigned section_line,
224 const char *lvalue,
225 int ltype,
226 const char *rvalue,
227 void *data,
228 void *userdata) {
229
230 SocketPort *p, *tail;
231 Socket *s;
232 int r;
233
234 assert(filename);
235 assert(lvalue);
236 assert(rvalue);
237 assert(data);
238
239 s = SOCKET(data);
240
241 if (isempty(rvalue)) {
242 /* An empty assignment removes all ports */
243 socket_free_ports(s);
244 return 0;
245 }
246
247 p = new0(SocketPort, 1);
248 if (!p)
249 return log_oom();
250
251 if (ltype != SOCKET_SOCKET) {
252
253 p->type = ltype;
254 r = unit_full_printf(UNIT(s), rvalue, &p->path);
255 if (r < 0) {
256 p->path = strdup(rvalue);
257 if (!p->path) {
258 free(p);
259 return log_oom();
260 } else
261 log_syntax(unit, LOG_ERR, filename, line, -r,
262 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
263 }
264
265 path_kill_slashes(p->path);
266
267 } else if (streq(lvalue, "ListenNetlink")) {
268 _cleanup_free_ char *k = NULL;
269
270 p->type = SOCKET_SOCKET;
271 r = unit_full_printf(UNIT(s), rvalue, &k);
272 if (r < 0)
273 log_syntax(unit, LOG_ERR, filename, line, -r,
274 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
275
276 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
277 if (r < 0) {
278 log_syntax(unit, LOG_ERR, filename, line, -r,
279 "Failed to parse address value, ignoring: %s", rvalue);
280 free(p);
281 return 0;
282 }
283
284 } else {
285 _cleanup_free_ char *k = NULL;
286
287 p->type = SOCKET_SOCKET;
288 r = unit_full_printf(UNIT(s), rvalue, &k);
289 if (r < 0)
290 log_syntax(unit, LOG_ERR, filename, line, -r,
291 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
292
293 r = socket_address_parse(&p->address, k ? k : rvalue);
294 if (r < 0) {
295 log_syntax(unit, LOG_ERR, filename, line, -r,
296 "Failed to parse address value, ignoring: %s", rvalue);
297 free(p);
298 return 0;
299 }
300
301 if (streq(lvalue, "ListenStream"))
302 p->address.type = SOCK_STREAM;
303 else if (streq(lvalue, "ListenDatagram"))
304 p->address.type = SOCK_DGRAM;
305 else {
306 assert(streq(lvalue, "ListenSequentialPacket"));
307 p->address.type = SOCK_SEQPACKET;
308 }
309
310 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
311 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
312 "Address family not supported, ignoring: %s", rvalue);
313 free(p);
314 return 0;
315 }
316 }
317
318 p->fd = -1;
319 p->socket = s;
320
321 if (s->ports) {
322 LIST_FIND_TAIL(port, s->ports, tail);
323 LIST_INSERT_AFTER(port, s->ports, tail, p);
324 } else
325 LIST_PREPEND(port, s->ports, p);
326
327 return 0;
328 }
329
330 int config_parse_socket_bind(const char *unit,
331 const char *filename,
332 unsigned line,
333 const char *section,
334 unsigned section_line,
335 const char *lvalue,
336 int ltype,
337 const char *rvalue,
338 void *data,
339 void *userdata) {
340
341 Socket *s;
342 SocketAddressBindIPv6Only b;
343
344 assert(filename);
345 assert(lvalue);
346 assert(rvalue);
347 assert(data);
348
349 s = SOCKET(data);
350
351 b = socket_address_bind_ipv6_only_from_string(rvalue);
352 if (b < 0) {
353 int r;
354
355 r = parse_boolean(rvalue);
356 if (r < 0) {
357 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
358 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
359 return 0;
360 }
361
362 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
363 } else
364 s->bind_ipv6_only = b;
365
366 return 0;
367 }
368
369 int config_parse_exec_nice(const char *unit,
370 const char *filename,
371 unsigned line,
372 const char *section,
373 unsigned section_line,
374 const char *lvalue,
375 int ltype,
376 const char *rvalue,
377 void *data,
378 void *userdata) {
379
380 ExecContext *c = data;
381 int priority, r;
382
383 assert(filename);
384 assert(lvalue);
385 assert(rvalue);
386 assert(data);
387
388 r = safe_atoi(rvalue, &priority);
389 if (r < 0) {
390 log_syntax(unit, LOG_ERR, filename, line, -r,
391 "Failed to parse nice priority, ignoring: %s. ", rvalue);
392 return 0;
393 }
394
395 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
396 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
397 "Nice priority out of range, ignoring: %s", rvalue);
398 return 0;
399 }
400
401 c->nice = priority;
402 c->nice_set = true;
403
404 return 0;
405 }
406
407 int config_parse_exec_oom_score_adjust(const char* unit,
408 const char *filename,
409 unsigned line,
410 const char *section,
411 unsigned section_line,
412 const char *lvalue,
413 int ltype,
414 const char *rvalue,
415 void *data,
416 void *userdata) {
417
418 ExecContext *c = data;
419 int oa, r;
420
421 assert(filename);
422 assert(lvalue);
423 assert(rvalue);
424 assert(data);
425
426 r = safe_atoi(rvalue, &oa);
427 if (r < 0) {
428 log_syntax(unit, LOG_ERR, filename, line, -r,
429 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
430 return 0;
431 }
432
433 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
434 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
435 "OOM score adjust value out of range, ignoring: %s", rvalue);
436 return 0;
437 }
438
439 c->oom_score_adjust = oa;
440 c->oom_score_adjust_set = true;
441
442 return 0;
443 }
444
445 int config_parse_exec(const char *unit,
446 const char *filename,
447 unsigned line,
448 const char *section,
449 unsigned section_line,
450 const char *lvalue,
451 int ltype,
452 const char *rvalue,
453 void *data,
454 void *userdata) {
455
456 ExecCommand **e = data, *nce;
457 char *path, **n;
458 unsigned k;
459 int r;
460
461 assert(filename);
462 assert(lvalue);
463 assert(rvalue);
464 assert(e);
465
466 e += ltype;
467
468 if (isempty(rvalue)) {
469 /* An empty assignment resets the list */
470 exec_command_free_list(*e);
471 *e = NULL;
472 return 0;
473 }
474
475 /* We accept an absolute path as first argument, or
476 * alternatively an absolute prefixed with @ to allow
477 * overriding of argv[0]. */
478 for (;;) {
479 int i;
480 char *w;
481 size_t l;
482 char *state;
483 bool honour_argv0 = false, ignore = false;
484
485 path = NULL;
486 nce = NULL;
487 n = NULL;
488
489 rvalue += strspn(rvalue, WHITESPACE);
490
491 if (rvalue[0] == 0)
492 break;
493
494 for (i = 0; i < 2; i++) {
495 if (rvalue[0] == '-' && !ignore) {
496 ignore = true;
497 rvalue ++;
498 }
499
500 if (rvalue[0] == '@' && !honour_argv0) {
501 honour_argv0 = true;
502 rvalue ++;
503 }
504 }
505
506 if (*rvalue != '/') {
507 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
508 "Executable path is not absolute, ignoring: %s", rvalue);
509 return 0;
510 }
511
512 k = 0;
513 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
514 if (strneq(w, ";", MAX(l, 1U)))
515 break;
516
517 k++;
518 }
519
520 n = new(char*, k + !honour_argv0);
521 if (!n)
522 return log_oom();
523
524 k = 0;
525 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
526 if (strneq(w, ";", MAX(l, 1U)))
527 break;
528 else if (strneq(w, "\\;", MAX(l, 1U)))
529 w ++;
530
531 if (honour_argv0 && w == rvalue) {
532 assert(!path);
533
534 path = strndup(w, l);
535 if (!path) {
536 r = log_oom();
537 goto fail;
538 }
539
540 if (!utf8_is_valid(path)) {
541 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
542 r = 0;
543 goto fail;
544 }
545
546 } else {
547 char *c;
548
549 c = n[k++] = cunescape_length(w, l);
550 if (!c) {
551 r = log_oom();
552 goto fail;
553 }
554
555 if (!utf8_is_valid(c)) {
556 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
557 r = 0;
558 goto fail;
559 }
560 }
561 }
562
563 n[k] = NULL;
564
565 if (!n[0]) {
566 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
567 "Invalid command line, ignoring: %s", rvalue);
568 r = 0;
569 goto fail;
570 }
571
572 if (!path) {
573 path = strdup(n[0]);
574 if (!path) {
575 r = log_oom();
576 goto fail;
577 }
578 }
579
580 assert(path_is_absolute(path));
581
582 nce = new0(ExecCommand, 1);
583 if (!nce) {
584 r = log_oom();
585 goto fail;
586 }
587
588 nce->argv = n;
589 nce->path = path;
590 nce->ignore = ignore;
591
592 path_kill_slashes(nce->path);
593
594 exec_command_append_list(e, nce);
595
596 rvalue = state;
597 }
598
599 return 0;
600
601 fail:
602 n[k] = NULL;
603 strv_free(n);
604 free(path);
605 free(nce);
606
607 return r;
608 }
609
610 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
611 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
612
613 int config_parse_socket_bindtodevice(const char* unit,
614 const char *filename,
615 unsigned line,
616 const char *section,
617 unsigned section_line,
618 const char *lvalue,
619 int ltype,
620 const char *rvalue,
621 void *data,
622 void *userdata) {
623
624 Socket *s = data;
625 char *n;
626
627 assert(filename);
628 assert(lvalue);
629 assert(rvalue);
630 assert(data);
631
632 if (rvalue[0] && !streq(rvalue, "*")) {
633 n = strdup(rvalue);
634 if (!n)
635 return log_oom();
636 } else
637 n = NULL;
638
639 free(s->bind_to_device);
640 s->bind_to_device = n;
641
642 return 0;
643 }
644
645 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
646 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
647
648 int config_parse_exec_io_class(const char *unit,
649 const char *filename,
650 unsigned line,
651 const char *section,
652 unsigned section_line,
653 const char *lvalue,
654 int ltype,
655 const char *rvalue,
656 void *data,
657 void *userdata) {
658
659 ExecContext *c = data;
660 int x;
661
662 assert(filename);
663 assert(lvalue);
664 assert(rvalue);
665 assert(data);
666
667 x = ioprio_class_from_string(rvalue);
668 if (x < 0) {
669 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
670 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
671 return 0;
672 }
673
674 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
675 c->ioprio_set = true;
676
677 return 0;
678 }
679
680 int config_parse_exec_io_priority(const char *unit,
681 const char *filename,
682 unsigned line,
683 const char *section,
684 unsigned section_line,
685 const char *lvalue,
686 int ltype,
687 const char *rvalue,
688 void *data,
689 void *userdata) {
690
691 ExecContext *c = data;
692 int i, r;
693
694 assert(filename);
695 assert(lvalue);
696 assert(rvalue);
697 assert(data);
698
699 r = safe_atoi(rvalue, &i);
700 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
701 log_syntax(unit, LOG_ERR, filename, line, -r,
702 "Failed to parse IO priority, ignoring: %s", rvalue);
703 return 0;
704 }
705
706 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
707 c->ioprio_set = true;
708
709 return 0;
710 }
711
712 int config_parse_exec_cpu_sched_policy(const char *unit,
713 const char *filename,
714 unsigned line,
715 const char *section,
716 unsigned section_line,
717 const char *lvalue,
718 int ltype,
719 const char *rvalue,
720 void *data,
721 void *userdata) {
722
723
724 ExecContext *c = data;
725 int x;
726
727 assert(filename);
728 assert(lvalue);
729 assert(rvalue);
730 assert(data);
731
732 x = sched_policy_from_string(rvalue);
733 if (x < 0) {
734 log_syntax(unit, LOG_ERR, filename, line, -x,
735 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
736 return 0;
737 }
738
739 c->cpu_sched_policy = x;
740 /* Moving to or from real-time policy? We need to adjust the priority */
741 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
742 c->cpu_sched_set = true;
743
744 return 0;
745 }
746
747 int config_parse_exec_cpu_sched_prio(const char *unit,
748 const char *filename,
749 unsigned line,
750 const char *section,
751 unsigned section_line,
752 const char *lvalue,
753 int ltype,
754 const char *rvalue,
755 void *data,
756 void *userdata) {
757
758 ExecContext *c = data;
759 int i, min, max, r;
760
761 assert(filename);
762 assert(lvalue);
763 assert(rvalue);
764 assert(data);
765
766 r = safe_atoi(rvalue, &i);
767 if (r < 0) {
768 log_syntax(unit, LOG_ERR, filename, line, -r,
769 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
770 return 0;
771 }
772
773 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
774 min = sched_get_priority_min(c->cpu_sched_policy);
775 max = sched_get_priority_max(c->cpu_sched_policy);
776
777 if (i < min || i > max) {
778 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
779 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
780 return 0;
781 }
782
783 c->cpu_sched_priority = i;
784 c->cpu_sched_set = true;
785
786 return 0;
787 }
788
789 int config_parse_exec_cpu_affinity(const char *unit,
790 const char *filename,
791 unsigned line,
792 const char *section,
793 unsigned section_line,
794 const char *lvalue,
795 int ltype,
796 const char *rvalue,
797 void *data,
798 void *userdata) {
799
800 ExecContext *c = data;
801 char *w;
802 size_t l;
803 char *state;
804
805 assert(filename);
806 assert(lvalue);
807 assert(rvalue);
808 assert(data);
809
810 if (isempty(rvalue)) {
811 /* An empty assignment resets the CPU list */
812 if (c->cpuset)
813 CPU_FREE(c->cpuset);
814 c->cpuset = NULL;
815 return 0;
816 }
817
818 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
819 _cleanup_free_ char *t = NULL;
820 int r;
821 unsigned cpu;
822
823 t = strndup(w, l);
824 if (!t)
825 return log_oom();
826
827 r = safe_atou(t, &cpu);
828
829 if (!c->cpuset) {
830 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
831 if (!c->cpuset)
832 return log_oom();
833 }
834
835 if (r < 0 || cpu >= c->cpuset_ncpus) {
836 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
837 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
838 return 0;
839 }
840
841 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
842 }
843
844 return 0;
845 }
846
847 int config_parse_exec_capabilities(const char *unit,
848 const char *filename,
849 unsigned line,
850 const char *section,
851 unsigned section_line,
852 const char *lvalue,
853 int ltype,
854 const char *rvalue,
855 void *data,
856 void *userdata) {
857
858 ExecContext *c = data;
859 cap_t cap;
860
861 assert(filename);
862 assert(lvalue);
863 assert(rvalue);
864 assert(data);
865
866 cap = cap_from_text(rvalue);
867 if (!cap) {
868 log_syntax(unit, LOG_ERR, filename, line, errno,
869 "Failed to parse capabilities, ignoring: %s", rvalue);
870 return 0;
871 }
872
873 if (c->capabilities)
874 cap_free(c->capabilities);
875 c->capabilities = cap;
876
877 return 0;
878 }
879
880 int config_parse_exec_secure_bits(const char *unit,
881 const char *filename,
882 unsigned line,
883 const char *section,
884 unsigned section_line,
885 const char *lvalue,
886 int ltype,
887 const char *rvalue,
888 void *data,
889 void *userdata) {
890
891 ExecContext *c = data;
892 char *w;
893 size_t l;
894 char *state;
895
896 assert(filename);
897 assert(lvalue);
898 assert(rvalue);
899 assert(data);
900
901 if (isempty(rvalue)) {
902 /* An empty assignment resets the field */
903 c->secure_bits = 0;
904 return 0;
905 }
906
907 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
908 if (first_word(w, "keep-caps"))
909 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
910 else if (first_word(w, "keep-caps-locked"))
911 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
912 else if (first_word(w, "no-setuid-fixup"))
913 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
914 else if (first_word(w, "no-setuid-fixup-locked"))
915 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
916 else if (first_word(w, "noroot"))
917 c->secure_bits |= 1<<SECURE_NOROOT;
918 else if (first_word(w, "noroot-locked"))
919 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
920 else {
921 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
922 "Failed to parse secure bits, ignoring: %s", rvalue);
923 return 0;
924 }
925 }
926
927 return 0;
928 }
929
930 int config_parse_bounding_set(const char *unit,
931 const char *filename,
932 unsigned line,
933 const char *section,
934 unsigned section_line,
935 const char *lvalue,
936 int ltype,
937 const char *rvalue,
938 void *data,
939 void *userdata) {
940
941 uint64_t *capability_bounding_set_drop = data;
942 char *w;
943 size_t l;
944 char *state;
945 bool invert = false;
946 uint64_t sum = 0;
947
948 assert(filename);
949 assert(lvalue);
950 assert(rvalue);
951 assert(data);
952
953 if (rvalue[0] == '~') {
954 invert = true;
955 rvalue++;
956 }
957
958 /* Note that we store this inverted internally, since the
959 * kernel wants it like this. But we actually expose it
960 * non-inverted everywhere to have a fully normalized
961 * interface. */
962
963 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
964 _cleanup_free_ char *t = NULL;
965 int r;
966 cap_value_t cap;
967
968 t = strndup(w, l);
969 if (!t)
970 return log_oom();
971
972 r = cap_from_name(t, &cap);
973 if (r < 0) {
974 log_syntax(unit, LOG_ERR, filename, line, errno,
975 "Failed to parse capability in bounding set, ignoring: %s", t);
976 continue;
977 }
978
979 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
980 }
981
982 if (invert)
983 *capability_bounding_set_drop |= sum;
984 else
985 *capability_bounding_set_drop |= ~sum;
986
987 return 0;
988 }
989
990 int config_parse_limit(const char *unit,
991 const char *filename,
992 unsigned line,
993 const char *section,
994 unsigned section_line,
995 const char *lvalue,
996 int ltype,
997 const char *rvalue,
998 void *data,
999 void *userdata) {
1000
1001 struct rlimit **rl = data;
1002 unsigned long long u;
1003
1004 assert(filename);
1005 assert(lvalue);
1006 assert(rvalue);
1007 assert(data);
1008
1009 rl += ltype;
1010
1011 if (streq(rvalue, "infinity"))
1012 u = (unsigned long long) RLIM_INFINITY;
1013 else {
1014 int r;
1015
1016 r = safe_atollu(rvalue, &u);
1017 if (r < 0) {
1018 log_syntax(unit, LOG_ERR, filename, line, -r,
1019 "Failed to parse resource value, ignoring: %s", rvalue);
1020 return 0;
1021 }
1022 }
1023
1024 if (!*rl) {
1025 *rl = new(struct rlimit, 1);
1026 if (!*rl)
1027 return log_oom();
1028 }
1029
1030 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
1031 return 0;
1032 }
1033
1034 #ifdef HAVE_SYSV_COMPAT
1035 int config_parse_sysv_priority(const char *unit,
1036 const char *filename,
1037 unsigned line,
1038 const char *section,
1039 unsigned section_line,
1040 const char *lvalue,
1041 int ltype,
1042 const char *rvalue,
1043 void *data,
1044 void *userdata) {
1045
1046 int *priority = 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 < 0 || i < 0) {
1056 log_syntax(unit, LOG_ERR, filename, line, -r,
1057 "Failed to parse SysV start priority, ignoring: %s", rvalue);
1058 return 0;
1059 }
1060
1061 *priority = (int) i;
1062 return 0;
1063 }
1064 #endif
1065
1066 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1067
1068 int config_parse_kill_signal(const char *unit,
1069 const char *filename,
1070 unsigned line,
1071 const char *section,
1072 unsigned section_line,
1073 const char *lvalue,
1074 int ltype,
1075 const char *rvalue,
1076 void *data,
1077 void *userdata) {
1078
1079 int *sig = data;
1080 int r;
1081
1082 assert(filename);
1083 assert(lvalue);
1084 assert(rvalue);
1085 assert(sig);
1086
1087 r = signal_from_string_try_harder(rvalue);
1088 if (r <= 0) {
1089 log_syntax(unit, LOG_ERR, filename, line, -r,
1090 "Failed to parse kill signal, ignoring: %s", rvalue);
1091 return 0;
1092 }
1093
1094 *sig = r;
1095 return 0;
1096 }
1097
1098 int config_parse_exec_mount_flags(const char *unit,
1099 const char *filename,
1100 unsigned line,
1101 const char *section,
1102 unsigned section_line,
1103 const char *lvalue,
1104 int ltype,
1105 const char *rvalue,
1106 void *data,
1107 void *userdata) {
1108
1109 ExecContext *c = data;
1110 char *w;
1111 size_t l;
1112 char *state;
1113 unsigned long flags = 0;
1114
1115 assert(filename);
1116 assert(lvalue);
1117 assert(rvalue);
1118 assert(data);
1119
1120 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
1121 _cleanup_free_ char *t;
1122
1123 t = strndup(w, l);
1124 if (!t)
1125 return log_oom();
1126
1127 if (streq(t, "shared"))
1128 flags |= MS_SHARED;
1129 else if (streq(t, "slave"))
1130 flags |= MS_SLAVE;
1131 else if (streq(w, "private"))
1132 flags |= MS_PRIVATE;
1133 else {
1134 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1135 "Failed to parse mount flag %s, ignoring: %s",
1136 t, rvalue);
1137 return 0;
1138 }
1139 }
1140
1141 c->mount_flags = flags;
1142 return 0;
1143 }
1144
1145 int config_parse_exec_selinux_context(
1146 const char *unit,
1147 const char *filename,
1148 unsigned line,
1149 const char *section,
1150 unsigned section_line,
1151 const char *lvalue,
1152 int ltype,
1153 const char *rvalue,
1154 void *data,
1155 void *userdata) {
1156
1157 ExecContext *c = data;
1158 Unit *u = userdata;
1159 bool ignore;
1160 char *k;
1161 int r;
1162
1163 assert(filename);
1164 assert(lvalue);
1165 assert(rvalue);
1166 assert(data);
1167
1168 if (isempty(rvalue)) {
1169 free(c->selinux_context);
1170 c->selinux_context = NULL;
1171 c->selinux_context_ignore = false;
1172 return 0;
1173 }
1174
1175 if (rvalue[0] == '-') {
1176 ignore = true;
1177 rvalue++;
1178 } else
1179 ignore = false;
1180
1181 r = unit_name_printf(u, rvalue, &k);
1182 if (r < 0) {
1183 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1184 return 0;
1185 }
1186
1187 free(c->selinux_context);
1188 c->selinux_context = k;
1189 c->selinux_context_ignore = ignore;
1190
1191 return 0;
1192 }
1193
1194 int config_parse_exec_apparmor_profile(
1195 const char *unit,
1196 const char *filename,
1197 unsigned line,
1198 const char *section,
1199 unsigned section_line,
1200 const char *lvalue,
1201 int ltype,
1202 const char *rvalue,
1203 void *data,
1204 void *userdata) {
1205
1206 ExecContext *c = data;
1207 Unit *u = userdata;
1208 bool ignore;
1209 char *k;
1210 int r;
1211
1212 assert(filename);
1213 assert(lvalue);
1214 assert(rvalue);
1215 assert(data);
1216
1217 if (isempty(rvalue)) {
1218 free(c->apparmor_profile);
1219 c->apparmor_profile = NULL;
1220 c->apparmor_profile_ignore = false;
1221 return 0;
1222 }
1223
1224 if (rvalue[0] == '-') {
1225 ignore = true;
1226 rvalue++;
1227 } else
1228 ignore = false;
1229
1230 r = unit_name_printf(u, rvalue, &k);
1231 if (r < 0) {
1232 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1233 return 0;
1234 }
1235
1236 free(c->apparmor_profile);
1237 c->apparmor_profile = k;
1238 c->apparmor_profile_ignore = ignore;
1239
1240 return 0;
1241 }
1242
1243 int config_parse_timer(const char *unit,
1244 const char *filename,
1245 unsigned line,
1246 const char *section,
1247 unsigned section_line,
1248 const char *lvalue,
1249 int ltype,
1250 const char *rvalue,
1251 void *data,
1252 void *userdata) {
1253
1254 Timer *t = data;
1255 usec_t u = 0;
1256 TimerValue *v;
1257 TimerBase b;
1258 CalendarSpec *c = NULL;
1259 clockid_t id;
1260
1261 assert(filename);
1262 assert(lvalue);
1263 assert(rvalue);
1264 assert(data);
1265
1266 if (isempty(rvalue)) {
1267 /* Empty assignment resets list */
1268 timer_free_values(t);
1269 return 0;
1270 }
1271
1272 b = timer_base_from_string(lvalue);
1273 if (b < 0) {
1274 log_syntax(unit, LOG_ERR, filename, line, -b,
1275 "Failed to parse timer base, ignoring: %s", lvalue);
1276 return 0;
1277 }
1278
1279 if (b == TIMER_CALENDAR) {
1280 if (calendar_spec_from_string(rvalue, &c) < 0) {
1281 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1282 "Failed to parse calendar specification, ignoring: %s",
1283 rvalue);
1284 return 0;
1285 }
1286
1287 id = CLOCK_REALTIME;
1288 } else {
1289 if (parse_sec(rvalue, &u) < 0) {
1290 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1291 "Failed to parse timer value, ignoring: %s",
1292 rvalue);
1293 return 0;
1294 }
1295
1296 id = CLOCK_MONOTONIC;
1297 }
1298
1299 v = new0(TimerValue, 1);
1300 if (!v)
1301 return log_oom();
1302
1303 v->base = b;
1304 v->clock_id = id;
1305 v->value = u;
1306 v->calendar_spec = c;
1307
1308 LIST_PREPEND(value, t->values, v);
1309
1310 return 0;
1311 }
1312
1313 int config_parse_trigger_unit(
1314 const char *unit,
1315 const char *filename,
1316 unsigned line,
1317 const char *section,
1318 unsigned section_line,
1319 const char *lvalue,
1320 int ltype,
1321 const char *rvalue,
1322 void *data,
1323 void *userdata) {
1324
1325 _cleanup_free_ char *p = NULL;
1326 Unit *u = data;
1327 UnitType type;
1328 int r;
1329
1330 assert(filename);
1331 assert(lvalue);
1332 assert(rvalue);
1333 assert(data);
1334
1335 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1336 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1337 "Multiple units to trigger specified, ignoring: %s", rvalue);
1338 return 0;
1339 }
1340
1341 r = unit_name_printf(u, rvalue, &p);
1342 if (r < 0)
1343 log_syntax(unit, LOG_ERR, filename, line, -r,
1344 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1345
1346 type = unit_name_to_type(p ?: rvalue);
1347 if (type < 0) {
1348 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1349 "Unit type not valid, ignoring: %s", rvalue);
1350 return 0;
1351 }
1352
1353 if (type == u->type) {
1354 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1355 "Trigger cannot be of same type, ignoring: %s", rvalue);
1356 return 0;
1357 }
1358
1359 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
1360 if (r < 0) {
1361 log_syntax(unit, LOG_ERR, filename, line, -r,
1362 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
1363 return 0;
1364 }
1365
1366 return 0;
1367 }
1368
1369 int config_parse_path_spec(const char *unit,
1370 const char *filename,
1371 unsigned line,
1372 const char *section,
1373 unsigned section_line,
1374 const char *lvalue,
1375 int ltype,
1376 const char *rvalue,
1377 void *data,
1378 void *userdata) {
1379
1380 Path *p = data;
1381 PathSpec *s;
1382 PathType b;
1383 _cleanup_free_ char *k = NULL;
1384 int r;
1385
1386 assert(filename);
1387 assert(lvalue);
1388 assert(rvalue);
1389 assert(data);
1390
1391 if (isempty(rvalue)) {
1392 /* Empty assignment clears list */
1393 path_free_specs(p);
1394 return 0;
1395 }
1396
1397 b = path_type_from_string(lvalue);
1398 if (b < 0) {
1399 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1400 "Failed to parse path type, ignoring: %s", lvalue);
1401 return 0;
1402 }
1403
1404 r = unit_full_printf(UNIT(p), rvalue, &k);
1405 if (r < 0) {
1406 k = strdup(rvalue);
1407 if (!k)
1408 return log_oom();
1409 else
1410 log_syntax(unit, LOG_ERR, filename, line, -r,
1411 "Failed to resolve unit specifiers on %s. Ignoring.",
1412 rvalue);
1413 }
1414
1415 if (!path_is_absolute(k)) {
1416 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1417 "Path is not absolute, ignoring: %s", k);
1418 return 0;
1419 }
1420
1421 s = new0(PathSpec, 1);
1422 if (!s)
1423 return log_oom();
1424
1425 s->unit = UNIT(p);
1426 s->path = path_kill_slashes(k);
1427 k = NULL;
1428 s->type = b;
1429 s->inotify_fd = -1;
1430
1431 LIST_PREPEND(spec, p->specs, s);
1432
1433 return 0;
1434 }
1435
1436 int config_parse_socket_service(const char *unit,
1437 const char *filename,
1438 unsigned line,
1439 const char *section,
1440 unsigned section_line,
1441 const char *lvalue,
1442 int ltype,
1443 const char *rvalue,
1444 void *data,
1445 void *userdata) {
1446
1447 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1448 Socket *s = data;
1449 int r;
1450 Unit *x;
1451 _cleanup_free_ char *p = NULL;
1452
1453 assert(filename);
1454 assert(lvalue);
1455 assert(rvalue);
1456 assert(data);
1457
1458 r = unit_name_printf(UNIT(s), rvalue, &p);
1459 if (r < 0) {
1460 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1461 return 0;
1462 }
1463
1464 if (!endswith(p, ".service")) {
1465 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1466 return 0;
1467 }
1468
1469 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1470 if (r < 0) {
1471 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1472 return 0;
1473 }
1474
1475 unit_ref_set(&s->service, x);
1476
1477 return 0;
1478 }
1479
1480 int config_parse_service_sockets(const char *unit,
1481 const char *filename,
1482 unsigned line,
1483 const char *section,
1484 unsigned section_line,
1485 const char *lvalue,
1486 int ltype,
1487 const char *rvalue,
1488 void *data,
1489 void *userdata) {
1490
1491 Service *s = data;
1492 int r;
1493 char *state, *w;
1494 size_t l;
1495
1496 assert(filename);
1497 assert(lvalue);
1498 assert(rvalue);
1499 assert(data);
1500
1501 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1502 _cleanup_free_ char *t = NULL, *k = NULL;
1503
1504 t = strndup(w, l);
1505 if (!t)
1506 return log_oom();
1507
1508 r = unit_name_printf(UNIT(s), t, &k);
1509 if (r < 0)
1510 log_syntax(unit, LOG_ERR, filename, line, -r,
1511 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1512
1513 if (!endswith(k ?: t, ".socket")) {
1514 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1515 "Unit must be of type socket, ignoring: %s", k ?: t);
1516 continue;
1517 }
1518
1519 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
1520 if (r < 0)
1521 log_syntax(unit, LOG_ERR, filename, line, -r,
1522 "Failed to add dependency on %s, ignoring: %s",
1523 k ?: t, strerror(-r));
1524
1525 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
1526 if (r < 0)
1527 return r;
1528 }
1529
1530 return 0;
1531 }
1532
1533 int config_parse_service_timeout(const char *unit,
1534 const char *filename,
1535 unsigned line,
1536 const char *section,
1537 unsigned section_line,
1538 const char *lvalue,
1539 int ltype,
1540 const char *rvalue,
1541 void *data,
1542 void *userdata) {
1543
1544 Service *s = userdata;
1545 int r;
1546
1547 assert(filename);
1548 assert(lvalue);
1549 assert(rvalue);
1550 assert(s);
1551
1552 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
1553 rvalue, data, userdata);
1554 if (r < 0)
1555 return r;
1556
1557 if (streq(lvalue, "TimeoutSec")) {
1558 s->start_timeout_defined = true;
1559 s->timeout_stop_usec = s->timeout_start_usec;
1560 } else if (streq(lvalue, "TimeoutStartSec"))
1561 s->start_timeout_defined = true;
1562
1563 return 0;
1564 }
1565
1566 int config_parse_busname_service(
1567 const char *unit,
1568 const char *filename,
1569 unsigned line,
1570 const char *section,
1571 unsigned section_line,
1572 const char *lvalue,
1573 int ltype,
1574 const char *rvalue,
1575 void *data,
1576 void *userdata) {
1577
1578 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1579 BusName *n = data;
1580 int r;
1581 Unit *x;
1582 _cleanup_free_ char *p = NULL;
1583
1584 assert(filename);
1585 assert(lvalue);
1586 assert(rvalue);
1587 assert(data);
1588
1589 r = unit_name_printf(UNIT(n), rvalue, &p);
1590 if (r < 0) {
1591 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1592 return 0;
1593 }
1594
1595 if (!endswith(p, ".service")) {
1596 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1597 return 0;
1598 }
1599
1600 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1601 if (r < 0) {
1602 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1603 return 0;
1604 }
1605
1606 unit_ref_set(&n->service, x);
1607
1608 return 0;
1609 }
1610
1611 int config_parse_bus_policy(
1612 const char *unit,
1613 const char *filename,
1614 unsigned line,
1615 const char *section,
1616 unsigned section_line,
1617 const char *lvalue,
1618 int ltype,
1619 const char *rvalue,
1620 void *data,
1621 void *userdata) {
1622
1623 _cleanup_free_ BusNamePolicy *p = NULL;
1624 _cleanup_free_ char *id_str = NULL;
1625 BusName *busname = data;
1626 char *access_str;
1627 int r;
1628
1629 assert(filename);
1630 assert(lvalue);
1631 assert(rvalue);
1632 assert(data);
1633
1634 p = new0(BusNamePolicy, 1);
1635 if (!p)
1636 return log_oom();
1637
1638 if (streq(lvalue, "AllowUser"))
1639 p->type = BUSNAME_POLICY_TYPE_USER;
1640 else if (streq(lvalue, "AllowGroup"))
1641 p->type = BUSNAME_POLICY_TYPE_GROUP;
1642 else if (streq(lvalue, "AllowWorld"))
1643 p->type = BUSNAME_POLICY_TYPE_WORLD;
1644 else
1645 assert_not_reached("Unknown lvalue");
1646
1647 id_str = strdup(rvalue);
1648 if (!id_str)
1649 return log_oom();
1650
1651 if (p->type != BUSNAME_POLICY_TYPE_WORLD) {
1652 access_str = strchr(id_str, ' ');
1653 if (!access_str) {
1654 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid busname policy value '%s'", rvalue);
1655 return 0;
1656 }
1657
1658 *access_str = '\0';
1659 access_str++;
1660
1661 if (p->type == BUSNAME_POLICY_TYPE_USER) {
1662 const char *user = id_str;
1663
1664 r = get_user_creds(&user, &p->uid, NULL, NULL, NULL);
1665 if (r < 0) {
1666 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to parse uid from '%s'", id_str);
1667 return 0;
1668 }
1669 } else {
1670 const char *group = id_str;
1671
1672 r = get_group_creds(&group, &p->gid);
1673 if (r < 0) {
1674 log_syntax(unit, LOG_ERR, filename, line, -errno, "Unable to parse gid from '%s'", id_str);
1675 return 0;
1676 }
1677 }
1678 } else {
1679 access_str = id_str;
1680 }
1681
1682 p->access = busname_policy_access_from_string(access_str);
1683 if (p->access < 0) {
1684 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid busname policy access type '%s'", access_str);
1685 return 0;
1686 }
1687
1688 LIST_PREPEND(policy, busname->policy, p);
1689 p = NULL;
1690
1691 return 0;
1692 }
1693
1694 int config_parse_unit_env_file(const char *unit,
1695 const char *filename,
1696 unsigned line,
1697 const char *section,
1698 unsigned section_line,
1699 const char *lvalue,
1700 int ltype,
1701 const char *rvalue,
1702 void *data,
1703 void *userdata) {
1704
1705 char ***env = data;
1706 Unit *u = userdata;
1707 _cleanup_free_ char *n = NULL;
1708 const char *s;
1709 int r;
1710
1711 assert(filename);
1712 assert(lvalue);
1713 assert(rvalue);
1714 assert(data);
1715
1716 if (isempty(rvalue)) {
1717 /* Empty assignment frees the list */
1718 strv_free(*env);
1719 *env = NULL;
1720 return 0;
1721 }
1722
1723 r = unit_full_printf(u, rvalue, &n);
1724 if (r < 0)
1725 log_syntax(unit, LOG_ERR, filename, line, r,
1726 "Failed to resolve specifiers, ignoring: %s", rvalue);
1727
1728 s = n ?: rvalue;
1729 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1730 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1731 "Path '%s' is not absolute, ignoring.", s);
1732 return 0;
1733 }
1734
1735 r = strv_extend(env, s);
1736 if (r < 0)
1737 return log_oom();
1738
1739 return 0;
1740 }
1741
1742 int config_parse_environ(const char *unit,
1743 const char *filename,
1744 unsigned line,
1745 const char *section,
1746 unsigned section_line,
1747 const char *lvalue,
1748 int ltype,
1749 const char *rvalue,
1750 void *data,
1751 void *userdata) {
1752
1753 Unit *u = userdata;
1754 char*** env = data, *w, *state;
1755 size_t l;
1756 _cleanup_free_ char *k = NULL;
1757 int r;
1758
1759 assert(filename);
1760 assert(lvalue);
1761 assert(rvalue);
1762 assert(data);
1763
1764 if (isempty(rvalue)) {
1765 /* Empty assignment resets the list */
1766 strv_free(*env);
1767 *env = NULL;
1768 return 0;
1769 }
1770
1771 if (u) {
1772 r = unit_full_printf(u, rvalue, &k);
1773 if (r < 0)
1774 log_syntax(unit, LOG_ERR, filename, line, -r,
1775 "Failed to resolve specifiers, ignoring: %s", rvalue);
1776 }
1777
1778 if (!k)
1779 k = strdup(rvalue);
1780 if (!k)
1781 return log_oom();
1782
1783 FOREACH_WORD_QUOTED(w, l, k, state) {
1784 _cleanup_free_ char *n;
1785 char **x;
1786
1787 n = cunescape_length(w, l);
1788 if (!n)
1789 return log_oom();
1790
1791 if (!env_assignment_is_valid(n)) {
1792 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1793 "Invalid environment assignment, ignoring: %s", rvalue);
1794 continue;
1795 }
1796
1797 x = strv_env_set(*env, n);
1798 if (!x)
1799 return log_oom();
1800
1801 strv_free(*env);
1802 *env = x;
1803 }
1804
1805 return 0;
1806 }
1807
1808 int config_parse_ip_tos(const char *unit,
1809 const char *filename,
1810 unsigned line,
1811 const char *section,
1812 unsigned section_line,
1813 const char *lvalue,
1814 int ltype,
1815 const char *rvalue,
1816 void *data,
1817 void *userdata) {
1818
1819 int *ip_tos = data, x;
1820
1821 assert(filename);
1822 assert(lvalue);
1823 assert(rvalue);
1824 assert(data);
1825
1826 x = ip_tos_from_string(rvalue);
1827 if (x < 0) {
1828 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1829 "Failed to parse IP TOS value, ignoring: %s", rvalue);
1830 return 0;
1831 }
1832
1833 *ip_tos = x;
1834 return 0;
1835 }
1836
1837 int config_parse_unit_condition_path(const char *unit,
1838 const char *filename,
1839 unsigned line,
1840 const char *section,
1841 unsigned section_line,
1842 const char *lvalue,
1843 int ltype,
1844 const char *rvalue,
1845 void *data,
1846 void *userdata) {
1847
1848 ConditionType cond = ltype;
1849 Unit *u = data;
1850 bool trigger, negate;
1851 Condition *c;
1852 _cleanup_free_ char *p = NULL;
1853 int r;
1854
1855 assert(filename);
1856 assert(lvalue);
1857 assert(rvalue);
1858 assert(data);
1859
1860 if (isempty(rvalue)) {
1861 /* Empty assignment resets the list */
1862 condition_free_list(u->conditions);
1863 u->conditions = NULL;
1864 return 0;
1865 }
1866
1867 trigger = rvalue[0] == '|';
1868 if (trigger)
1869 rvalue++;
1870
1871 negate = rvalue[0] == '!';
1872 if (negate)
1873 rvalue++;
1874
1875 r = unit_full_printf(u, rvalue, &p);
1876 if (r < 0)
1877 log_syntax(unit, LOG_ERR, filename, line, -r,
1878 "Failed to resolve specifiers, ignoring: %s", rvalue);
1879 if (!p) {
1880 p = strdup(rvalue);
1881 if (!p)
1882 return log_oom();
1883 }
1884
1885 if (!path_is_absolute(p)) {
1886 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1887 "Path in condition not absolute, ignoring: %s", p);
1888 return 0;
1889 }
1890
1891 c = condition_new(cond, p, trigger, negate);
1892 if (!c)
1893 return log_oom();
1894
1895 LIST_PREPEND(conditions, u->conditions, c);
1896 return 0;
1897 }
1898
1899 int config_parse_unit_condition_string(const char *unit,
1900 const char *filename,
1901 unsigned line,
1902 const char *section,
1903 unsigned section_line,
1904 const char *lvalue,
1905 int ltype,
1906 const char *rvalue,
1907 void *data,
1908 void *userdata) {
1909
1910 ConditionType cond = ltype;
1911 Unit *u = data;
1912 bool trigger, negate;
1913 Condition *c;
1914 _cleanup_free_ char *s = NULL;
1915 int r;
1916
1917 assert(filename);
1918 assert(lvalue);
1919 assert(rvalue);
1920 assert(data);
1921
1922 if (isempty(rvalue)) {
1923 /* Empty assignment resets the list */
1924 condition_free_list(u->conditions);
1925 u->conditions = NULL;
1926 return 0;
1927 }
1928
1929 trigger = rvalue[0] == '|';
1930 if (trigger)
1931 rvalue++;
1932
1933 negate = rvalue[0] == '!';
1934 if (negate)
1935 rvalue++;
1936
1937 r = unit_full_printf(u, rvalue, &s);
1938 if (r < 0)
1939 log_syntax(unit, LOG_ERR, filename, line, -r,
1940 "Failed to resolve specifiers, ignoring: %s", rvalue);
1941 if (!s) {
1942 s = strdup(rvalue);
1943 if (!s)
1944 return log_oom();
1945 }
1946
1947 c = condition_new(cond, s, trigger, negate);
1948 if (!c)
1949 return log_oom();
1950
1951 LIST_PREPEND(conditions, u->conditions, c);
1952 return 0;
1953 }
1954
1955 int config_parse_unit_condition_null(const char *unit,
1956 const char *filename,
1957 unsigned line,
1958 const char *section,
1959 unsigned section_line,
1960 const char *lvalue,
1961 int ltype,
1962 const char *rvalue,
1963 void *data,
1964 void *userdata) {
1965
1966 Unit *u = data;
1967 Condition *c;
1968 bool trigger, negate;
1969 int b;
1970
1971 assert(filename);
1972 assert(lvalue);
1973 assert(rvalue);
1974 assert(data);
1975
1976 if (isempty(rvalue)) {
1977 /* Empty assignment resets the list */
1978 condition_free_list(u->conditions);
1979 u->conditions = NULL;
1980 return 0;
1981 }
1982
1983 trigger = rvalue[0] == '|';
1984 if (trigger)
1985 rvalue++;
1986
1987 negate = rvalue[0] == '!';
1988 if (negate)
1989 rvalue++;
1990
1991 b = parse_boolean(rvalue);
1992 if (b < 0) {
1993 log_syntax(unit, LOG_ERR, filename, line, -b,
1994 "Failed to parse boolean value in condition, ignoring: %s",
1995 rvalue);
1996 return 0;
1997 }
1998
1999 if (!b)
2000 negate = !negate;
2001
2002 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2003 if (!c)
2004 return log_oom();
2005
2006 LIST_PREPEND(conditions, u->conditions, c);
2007 return 0;
2008 }
2009
2010 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
2011 DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
2012
2013 int config_parse_unit_requires_mounts_for(
2014 const char *unit,
2015 const char *filename,
2016 unsigned line,
2017 const char *section,
2018 unsigned section_line,
2019 const char *lvalue,
2020 int ltype,
2021 const char *rvalue,
2022 void *data,
2023 void *userdata) {
2024
2025 Unit *u = userdata;
2026 char *state;
2027 size_t l;
2028 char *w;
2029
2030 assert(filename);
2031 assert(lvalue);
2032 assert(rvalue);
2033 assert(data);
2034
2035 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2036 int r;
2037 _cleanup_free_ char *n;
2038
2039 n = strndup(w, l);
2040 if (!n)
2041 return log_oom();
2042
2043 if (!utf8_is_valid(n)) {
2044 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
2045 continue;
2046 }
2047
2048 r = unit_require_mounts_for(u, n);
2049 if (r < 0) {
2050 log_syntax(unit, LOG_ERR, filename, line, r,
2051 "Failed to add required mount for, ignoring: %s", rvalue);
2052 continue;
2053 }
2054 }
2055
2056 return 0;
2057 }
2058
2059 int config_parse_documentation(const char *unit,
2060 const char *filename,
2061 unsigned line,
2062 const char *section,
2063 unsigned section_line,
2064 const char *lvalue,
2065 int ltype,
2066 const char *rvalue,
2067 void *data,
2068 void *userdata) {
2069
2070 Unit *u = userdata;
2071 int r;
2072 char **a, **b;
2073
2074 assert(filename);
2075 assert(lvalue);
2076 assert(rvalue);
2077 assert(u);
2078
2079 if (isempty(rvalue)) {
2080 /* Empty assignment resets the list */
2081 strv_free(u->documentation);
2082 u->documentation = NULL;
2083 return 0;
2084 }
2085
2086 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
2087 rvalue, data, userdata);
2088 if (r < 0)
2089 return r;
2090
2091 for (a = b = u->documentation; a && *a; a++) {
2092
2093 if (is_valid_documentation_url(*a))
2094 *(b++) = *a;
2095 else {
2096 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2097 "Invalid URL, ignoring: %s", *a);
2098 free(*a);
2099 }
2100 }
2101 if (b)
2102 *b = NULL;
2103
2104 return r;
2105 }
2106
2107 #ifdef HAVE_SECCOMP
2108 int config_parse_syscall_filter(
2109 const char *unit,
2110 const char *filename,
2111 unsigned line,
2112 const char *section,
2113 unsigned section_line,
2114 const char *lvalue,
2115 int ltype,
2116 const char *rvalue,
2117 void *data,
2118 void *userdata) {
2119
2120 static const char default_syscalls[] =
2121 "execve\0"
2122 "exit\0"
2123 "exit_group\0"
2124 "rt_sigreturn\0"
2125 "sigreturn\0";
2126
2127 ExecContext *c = data;
2128 Unit *u = userdata;
2129 bool invert = false;
2130 char *w, *state;
2131 size_t l;
2132 int r;
2133
2134 assert(filename);
2135 assert(lvalue);
2136 assert(rvalue);
2137 assert(u);
2138
2139 if (isempty(rvalue)) {
2140 /* Empty assignment resets the list */
2141 set_free(c->syscall_filter);
2142 c->syscall_filter = NULL;
2143 c->syscall_whitelist = false;
2144 return 0;
2145 }
2146
2147 if (rvalue[0] == '~') {
2148 invert = true;
2149 rvalue++;
2150 }
2151
2152 if (!c->syscall_filter) {
2153 c->syscall_filter = set_new(trivial_hash_func, trivial_compare_func);
2154 if (!c->syscall_filter)
2155 return log_oom();
2156
2157 if (invert)
2158 /* Allow everything but the ones listed */
2159 c->syscall_whitelist = false;
2160 else {
2161 const char *i;
2162
2163 /* Allow nothing but the ones listed */
2164 c->syscall_whitelist = true;
2165
2166 /* Accept default syscalls if we are on a whitelist */
2167 NULSTR_FOREACH(i, default_syscalls) {
2168 int id;
2169
2170 id = seccomp_syscall_resolve_name(i);
2171 if (id < 0)
2172 continue;
2173
2174 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2175 if (r == -EEXIST)
2176 continue;
2177 if (r < 0)
2178 return log_oom();
2179 }
2180 }
2181 }
2182
2183 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2184 _cleanup_free_ char *t = NULL;
2185 int id;
2186
2187 t = strndup(w, l);
2188 if (!t)
2189 return log_oom();
2190
2191 id = seccomp_syscall_resolve_name(t);
2192 if (id < 0) {
2193 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call, ignoring: %s", t);
2194 continue;
2195 }
2196
2197 /* If we previously wanted to forbid a syscall and now
2198 * we want to allow it, then remove it from the list
2199 */
2200 if (!invert == c->syscall_whitelist) {
2201 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2202 if (r == -EEXIST)
2203 continue;
2204 if (r < 0)
2205 return log_oom();
2206 } else
2207 set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
2208 }
2209
2210 /* Turn on NNP, but only if it wasn't configured explicitly
2211 * before, and only if we are in user mode. */
2212 if (!c->no_new_privileges_set && u->manager->running_as == SYSTEMD_USER)
2213 c->no_new_privileges = true;
2214
2215 return 0;
2216 }
2217
2218 int config_parse_syscall_archs(
2219 const char *unit,
2220 const char *filename,
2221 unsigned line,
2222 const char *section,
2223 unsigned section_line,
2224 const char *lvalue,
2225 int ltype,
2226 const char *rvalue,
2227 void *data,
2228 void *userdata) {
2229
2230 Set **archs = data;
2231 char *w, *state;
2232 size_t l;
2233 int r;
2234
2235 if (isempty(rvalue)) {
2236 set_free(*archs);
2237 *archs = NULL;
2238 return 0;
2239 }
2240
2241 r = set_ensure_allocated(archs, trivial_hash_func, trivial_compare_func);
2242 if (r < 0)
2243 return log_oom();
2244
2245 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2246 _cleanup_free_ char *t = NULL;
2247 uint32_t a;
2248
2249 t = strndup(w, l);
2250 if (!t)
2251 return log_oom();
2252
2253 r = seccomp_arch_from_string(t, &a);
2254 if (r < 0) {
2255 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call architecture, ignoring: %s", t);
2256 continue;
2257 }
2258
2259 r = set_put(*archs, UINT32_TO_PTR(a + 1));
2260 if (r == -EEXIST)
2261 continue;
2262 if (r < 0)
2263 return log_oom();
2264 }
2265
2266 return 0;
2267 }
2268
2269 int config_parse_syscall_errno(
2270 const char *unit,
2271 const char *filename,
2272 unsigned line,
2273 const char *section,
2274 unsigned section_line,
2275 const char *lvalue,
2276 int ltype,
2277 const char *rvalue,
2278 void *data,
2279 void *userdata) {
2280
2281 ExecContext *c = data;
2282 int e;
2283
2284 assert(filename);
2285 assert(lvalue);
2286 assert(rvalue);
2287
2288 if (isempty(rvalue)) {
2289 /* Empty assignment resets to KILL */
2290 c->syscall_errno = 0;
2291 return 0;
2292 }
2293
2294 e = errno_from_name(rvalue);
2295 if (e < 0) {
2296 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse error number, ignoring: %s", rvalue);
2297 return 0;
2298 }
2299
2300 c->syscall_errno = e;
2301 return 0;
2302 }
2303
2304 int config_parse_address_families(
2305 const char *unit,
2306 const char *filename,
2307 unsigned line,
2308 const char *section,
2309 unsigned section_line,
2310 const char *lvalue,
2311 int ltype,
2312 const char *rvalue,
2313 void *data,
2314 void *userdata) {
2315
2316 ExecContext *c = data;
2317 Unit *u = userdata;
2318 bool invert = false;
2319 char *w, *state;
2320 size_t l;
2321 int r;
2322
2323 assert(filename);
2324 assert(lvalue);
2325 assert(rvalue);
2326 assert(u);
2327
2328 if (isempty(rvalue)) {
2329 /* Empty assignment resets the list */
2330 set_free(c->address_families);
2331 c->address_families = NULL;
2332 c->address_families_whitelist = false;
2333 return 0;
2334 }
2335
2336 if (rvalue[0] == '~') {
2337 invert = true;
2338 rvalue++;
2339 }
2340
2341 if (!c->address_families) {
2342 c->address_families = set_new(trivial_hash_func, trivial_compare_func);
2343 if (!c->address_families)
2344 return log_oom();
2345
2346 c->address_families_whitelist = !invert;
2347 }
2348
2349 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2350 _cleanup_free_ char *t = NULL;
2351 int af;
2352
2353 t = strndup(w, l);
2354 if (!t)
2355 return log_oom();
2356
2357 af = af_from_name(t);
2358 if (af <= 0) {
2359 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse address family, ignoring: %s", t);
2360 continue;
2361 }
2362
2363 /* If we previously wanted to forbid an address family and now
2364 * we want to allow it, then remove it from the list
2365 */
2366 if (!invert == c->address_families_whitelist) {
2367 r = set_put(c->address_families, INT_TO_PTR(af));
2368 if (r == -EEXIST)
2369 continue;
2370 if (r < 0)
2371 return log_oom();
2372 } else
2373 set_remove(c->address_families, INT_TO_PTR(af));
2374 }
2375
2376 return 0;
2377 }
2378 #endif
2379
2380 int config_parse_unit_slice(
2381 const char *unit,
2382 const char *filename,
2383 unsigned line,
2384 const char *section,
2385 unsigned section_line,
2386 const char *lvalue,
2387 int ltype,
2388 const char *rvalue,
2389 void *data,
2390 void *userdata) {
2391
2392 _cleanup_free_ char *k = NULL;
2393 Unit *u = userdata, *slice;
2394 int r;
2395
2396 assert(filename);
2397 assert(lvalue);
2398 assert(rvalue);
2399 assert(u);
2400
2401 r = unit_name_printf(u, rvalue, &k);
2402 if (r < 0)
2403 log_syntax(unit, LOG_ERR, filename, line, -r,
2404 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2405 if (!k) {
2406 k = strdup(rvalue);
2407 if (!k)
2408 return log_oom();
2409 }
2410
2411 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
2412 if (r < 0) {
2413 log_syntax(unit, LOG_ERR, filename, line, -r,
2414 "Failed to load slice unit %s. Ignoring.", k);
2415 return 0;
2416 }
2417
2418 if (slice->type != UNIT_SLICE) {
2419 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2420 "Slice unit %s is not a slice. Ignoring.", k);
2421 return 0;
2422 }
2423
2424 unit_ref_set(&u->slice, slice);
2425 return 0;
2426 }
2427
2428 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2429
2430 int config_parse_cpu_shares(
2431 const char *unit,
2432 const char *filename,
2433 unsigned line,
2434 const char *section,
2435 unsigned section_line,
2436 const char *lvalue,
2437 int ltype,
2438 const char *rvalue,
2439 void *data,
2440 void *userdata) {
2441
2442 CGroupContext *c = data;
2443 unsigned long lu;
2444 int r;
2445
2446 assert(filename);
2447 assert(lvalue);
2448 assert(rvalue);
2449
2450 if (isempty(rvalue)) {
2451 c->cpu_shares = 1024;
2452 return 0;
2453 }
2454
2455 r = safe_atolu(rvalue, &lu);
2456 if (r < 0 || lu <= 0) {
2457 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2458 "CPU shares '%s' invalid. Ignoring.", rvalue);
2459 return 0;
2460 }
2461
2462 c->cpu_shares = lu;
2463 return 0;
2464 }
2465
2466 int config_parse_memory_limit(
2467 const char *unit,
2468 const char *filename,
2469 unsigned line,
2470 const char *section,
2471 unsigned section_line,
2472 const char *lvalue,
2473 int ltype,
2474 const char *rvalue,
2475 void *data,
2476 void *userdata) {
2477
2478 CGroupContext *c = data;
2479 off_t bytes;
2480 int r;
2481
2482 if (isempty(rvalue)) {
2483 c->memory_limit = (uint64_t) -1;
2484 return 0;
2485 }
2486
2487 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2488
2489 r = parse_size(rvalue, 1024, &bytes);
2490 if (r < 0) {
2491 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Memory limit '%s' invalid. Ignoring.", rvalue);
2492 return 0;
2493 }
2494
2495 c->memory_limit = (uint64_t) bytes;
2496 return 0;
2497 }
2498
2499 int config_parse_device_allow(
2500 const char *unit,
2501 const char *filename,
2502 unsigned line,
2503 const char *section,
2504 unsigned section_line,
2505 const char *lvalue,
2506 int ltype,
2507 const char *rvalue,
2508 void *data,
2509 void *userdata) {
2510
2511 _cleanup_free_ char *path = NULL;
2512 CGroupContext *c = data;
2513 CGroupDeviceAllow *a;
2514 const char *m;
2515 size_t n;
2516
2517 if (isempty(rvalue)) {
2518 while (c->device_allow)
2519 cgroup_context_free_device_allow(c, c->device_allow);
2520
2521 return 0;
2522 }
2523
2524 n = strcspn(rvalue, WHITESPACE);
2525 path = strndup(rvalue, n);
2526 if (!path)
2527 return log_oom();
2528
2529 if (!startswith(path, "/dev/") &&
2530 !startswith(path, "block-") &&
2531 !startswith(path, "char-")) {
2532 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device node path '%s'. Ignoring.", path);
2533 return 0;
2534 }
2535
2536 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2537 if (isempty(m))
2538 m = "rwm";
2539
2540 if (!in_charset(m, "rwm")) {
2541 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device rights '%s'. Ignoring.", m);
2542 return 0;
2543 }
2544
2545 a = new0(CGroupDeviceAllow, 1);
2546 if (!a)
2547 return log_oom();
2548
2549 a->path = path;
2550 path = NULL;
2551 a->r = !!strchr(m, 'r');
2552 a->w = !!strchr(m, 'w');
2553 a->m = !!strchr(m, 'm');
2554
2555 LIST_PREPEND(device_allow, c->device_allow, a);
2556 return 0;
2557 }
2558
2559 int config_parse_blockio_weight(
2560 const char *unit,
2561 const char *filename,
2562 unsigned line,
2563 const char *section,
2564 unsigned section_line,
2565 const char *lvalue,
2566 int ltype,
2567 const char *rvalue,
2568 void *data,
2569 void *userdata) {
2570
2571 CGroupContext *c = data;
2572 unsigned long lu;
2573 int r;
2574
2575 assert(filename);
2576 assert(lvalue);
2577 assert(rvalue);
2578
2579 if (isempty(rvalue)) {
2580 c->blockio_weight = 1000;
2581 return 0;
2582 }
2583
2584 r = safe_atolu(rvalue, &lu);
2585 if (r < 0 || lu < 10 || lu > 1000) {
2586 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2587 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2588 return 0;
2589 }
2590
2591 c->blockio_weight = lu;
2592
2593 return 0;
2594 }
2595
2596 int config_parse_blockio_device_weight(
2597 const char *unit,
2598 const char *filename,
2599 unsigned line,
2600 const char *section,
2601 unsigned section_line,
2602 const char *lvalue,
2603 int ltype,
2604 const char *rvalue,
2605 void *data,
2606 void *userdata) {
2607
2608 _cleanup_free_ char *path = NULL;
2609 CGroupBlockIODeviceWeight *w;
2610 CGroupContext *c = data;
2611 unsigned long lu;
2612 const char *weight;
2613 size_t n;
2614 int r;
2615
2616 assert(filename);
2617 assert(lvalue);
2618 assert(rvalue);
2619
2620 if (isempty(rvalue)) {
2621 while (c->blockio_device_weights)
2622 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2623
2624 return 0;
2625 }
2626
2627 n = strcspn(rvalue, WHITESPACE);
2628 weight = rvalue + n;
2629 if (!*weight) {
2630 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2631 "Expected block device and device weight. Ignoring.");
2632 return 0;
2633 }
2634
2635 path = strndup(rvalue, n);
2636 if (!path)
2637 return log_oom();
2638
2639 if (!path_startswith(path, "/dev")) {
2640 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2641 "Invalid device node path '%s'. Ignoring.", path);
2642 return 0;
2643 }
2644
2645 weight += strspn(weight, WHITESPACE);
2646 r = safe_atolu(weight, &lu);
2647 if (r < 0 || lu < 10 || lu > 1000) {
2648 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2649 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2650 return 0;
2651 }
2652
2653
2654 w = new0(CGroupBlockIODeviceWeight, 1);
2655 if (!w)
2656 return log_oom();
2657
2658 w->path = path;
2659 path = NULL;
2660
2661 w->weight = lu;
2662
2663 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
2664 return 0;
2665 }
2666
2667 int config_parse_blockio_bandwidth(
2668 const char *unit,
2669 const char *filename,
2670 unsigned line,
2671 const char *section,
2672 unsigned section_line,
2673 const char *lvalue,
2674 int ltype,
2675 const char *rvalue,
2676 void *data,
2677 void *userdata) {
2678
2679 _cleanup_free_ char *path = NULL;
2680 CGroupBlockIODeviceBandwidth *b;
2681 CGroupContext *c = data;
2682 const char *bandwidth;
2683 off_t bytes;
2684 bool read;
2685 size_t n;
2686 int r;
2687
2688 assert(filename);
2689 assert(lvalue);
2690 assert(rvalue);
2691
2692 read = streq("BlockIOReadBandwidth", lvalue);
2693
2694 if (isempty(rvalue)) {
2695 CGroupBlockIODeviceBandwidth *next;
2696
2697 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2698 if (b->read == read)
2699 cgroup_context_free_blockio_device_bandwidth(c, b);
2700
2701 return 0;
2702 }
2703
2704 n = strcspn(rvalue, WHITESPACE);
2705 bandwidth = rvalue + n;
2706 bandwidth += strspn(bandwidth, WHITESPACE);
2707
2708 if (!*bandwidth) {
2709 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2710 "Expected space separated pair of device node and bandwidth. Ignoring.");
2711 return 0;
2712 }
2713
2714 path = strndup(rvalue, n);
2715 if (!path)
2716 return log_oom();
2717
2718 if (!path_startswith(path, "/dev")) {
2719 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2720 "Invalid device node path '%s'. Ignoring.", path);
2721 return 0;
2722 }
2723
2724 r = parse_size(bandwidth, 1000, &bytes);
2725 if (r < 0 || bytes <= 0) {
2726 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2727 return 0;
2728 }
2729
2730 b = new0(CGroupBlockIODeviceBandwidth, 1);
2731 if (!b)
2732 return log_oom();
2733
2734 b->path = path;
2735 path = NULL;
2736 b->bandwidth = (uint64_t) bytes;
2737 b->read = read;
2738
2739 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
2740
2741 return 0;
2742 }
2743
2744 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2745
2746 int config_parse_job_mode_isolate(
2747 const char *unit,
2748 const char *filename,
2749 unsigned line,
2750 const char *section,
2751 unsigned section_line,
2752 const char *lvalue,
2753 int ltype,
2754 const char *rvalue,
2755 void *data,
2756 void *userdata) {
2757
2758 JobMode *m = data;
2759 int r;
2760
2761 assert(filename);
2762 assert(lvalue);
2763 assert(rvalue);
2764
2765 r = parse_boolean(rvalue);
2766 if (r < 0) {
2767 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue);
2768 return 0;
2769 }
2770
2771 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2772 return 0;
2773 }
2774
2775 int config_parse_personality(
2776 const char *unit,
2777 const char *filename,
2778 unsigned line,
2779 const char *section,
2780 unsigned section_line,
2781 const char *lvalue,
2782 int ltype,
2783 const char *rvalue,
2784 void *data,
2785 void *userdata) {
2786
2787 unsigned long *personality = data, p;
2788
2789 assert(filename);
2790 assert(lvalue);
2791 assert(rvalue);
2792 assert(personality);
2793
2794 p = personality_from_string(rvalue);
2795 if (p == 0xffffffffUL) {
2796 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2797 "Failed to parse personality, ignoring: %s", rvalue);
2798 return 0;
2799 }
2800
2801 *personality = p;
2802 return 0;
2803 }
2804
2805 int config_parse_runtime_directory(
2806 const char *unit,
2807 const char *filename,
2808 unsigned line,
2809 const char *section,
2810 unsigned section_line,
2811 const char *lvalue,
2812 int ltype,
2813 const char *rvalue,
2814 void *data,
2815 void *userdata) {
2816
2817 char***rt = data, *w, *state;
2818 size_t l;
2819 int r;
2820
2821 assert(filename);
2822 assert(lvalue);
2823 assert(rvalue);
2824 assert(data);
2825
2826 if (isempty(rvalue)) {
2827 /* Empty assignment resets the list */
2828 strv_free(*rt);
2829 *rt = NULL;
2830 return 0;
2831 }
2832
2833 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2834 _cleanup_free_ char *n;
2835
2836 n = strndup(w, l);
2837 if (!n)
2838 return log_oom();
2839
2840 if (!filename_is_safe(n)) {
2841 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
2842 continue;
2843 }
2844
2845 r = strv_push(rt, n);
2846 if (r < 0)
2847 return log_oom();
2848
2849 n = NULL;
2850 }
2851
2852 return 0;
2853 }
2854
2855 int config_parse_set_status(
2856 const char *unit,
2857 const char *filename,
2858 unsigned line,
2859 const char *section,
2860 unsigned section_line,
2861 const char *lvalue,
2862 int ltype,
2863 const char *rvalue,
2864 void *data,
2865 void *userdata) {
2866
2867 char *w;
2868 size_t l;
2869 char *state;
2870 int r;
2871 ExitStatusSet *status_set = data;
2872
2873 assert(filename);
2874 assert(lvalue);
2875 assert(rvalue);
2876 assert(data);
2877
2878 if (isempty(rvalue)) {
2879 /* Empty assignment resets the list */
2880
2881 set_free(status_set->signal);
2882 set_free(status_set->code);
2883
2884 status_set->signal = status_set->code = NULL;
2885 return 0;
2886 }
2887
2888 FOREACH_WORD(w, l, rvalue, state) {
2889 _cleanup_free_ char *temp;
2890 int val;
2891
2892 temp = strndup(w, l);
2893 if (!temp)
2894 return log_oom();
2895
2896 r = safe_atoi(temp, &val);
2897 if (r < 0) {
2898 val = signal_from_string_try_harder(temp);
2899
2900 if (val > 0) {
2901 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
2902 if (r < 0)
2903 return log_oom();
2904
2905 r = set_put(status_set->signal, INT_TO_PTR(val));
2906 if (r < 0) {
2907 log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w);
2908 return r;
2909 }
2910 } else {
2911 log_syntax(unit, LOG_ERR, filename, line, -val, "Failed to parse value, ignoring: %s", w);
2912 return 0;
2913 }
2914 } else {
2915 if (val < 0 || val > 255)
2916 log_syntax(unit, LOG_ERR, filename, line, ERANGE, "Value %d is outside range 0-255, ignoring", val);
2917 else {
2918 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
2919 if (r < 0)
2920 return log_oom();
2921
2922 r = set_put(status_set->code, INT_TO_PTR(val));
2923 if (r < 0) {
2924 log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w);
2925 return r;
2926 }
2927 }
2928 }
2929 }
2930
2931 return 0;
2932 }
2933
2934 int config_parse_namespace_path_strv(
2935 const char *unit,
2936 const char *filename,
2937 unsigned line,
2938 const char *section,
2939 unsigned section_line,
2940 const char *lvalue,
2941 int ltype,
2942 const char *rvalue,
2943 void *data,
2944 void *userdata) {
2945
2946 char*** sv = data, *w, *state;
2947 size_t l;
2948 int r;
2949
2950 assert(filename);
2951 assert(lvalue);
2952 assert(rvalue);
2953 assert(data);
2954
2955 if (isempty(rvalue)) {
2956 /* Empty assignment resets the list */
2957 strv_free(*sv);
2958 *sv = NULL;
2959 return 0;
2960 }
2961
2962 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2963 _cleanup_free_ char *n;
2964 int offset;
2965
2966 n = strndup(w, l);
2967 if (!n)
2968 return log_oom();
2969
2970 if (!utf8_is_valid(n)) {
2971 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
2972 continue;
2973 }
2974
2975 offset = n[0] == '-';
2976 if (!path_is_absolute(n + offset)) {
2977 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
2978 continue;
2979 }
2980
2981 path_kill_slashes(n);
2982
2983 r = strv_push(sv, n);
2984 if (r < 0)
2985 return log_oom();
2986
2987 n = NULL;
2988 }
2989
2990 return 0;
2991 }
2992
2993 int config_parse_no_new_priviliges(
2994 const char* unit,
2995 const char *filename,
2996 unsigned line,
2997 const char *section,
2998 unsigned section_line,
2999 const char *lvalue,
3000 int ltype,
3001 const char *rvalue,
3002 void *data,
3003 void *userdata) {
3004
3005 ExecContext *c = data;
3006 int k;
3007
3008 assert(filename);
3009 assert(lvalue);
3010 assert(rvalue);
3011 assert(data);
3012
3013 k = parse_boolean(rvalue);
3014 if (k < 0) {
3015 log_syntax(unit, LOG_ERR, filename, line, -k, "Failed to parse boolean value, ignoring: %s", rvalue);
3016 return 0;
3017 }
3018
3019 c->no_new_privileges = !!k;
3020 c->no_new_privileges_set = true;
3021
3022 return 0;
3023 }
3024
3025 #define FOLLOW_MAX 8
3026
3027 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
3028 unsigned c = 0;
3029 int fd, r;
3030 FILE *f;
3031 char *id = NULL;
3032
3033 assert(filename);
3034 assert(*filename);
3035 assert(_f);
3036 assert(names);
3037
3038 /* This will update the filename pointer if the loaded file is
3039 * reached by a symlink. The old string will be freed. */
3040
3041 for (;;) {
3042 char *target, *name;
3043
3044 if (c++ >= FOLLOW_MAX)
3045 return -ELOOP;
3046
3047 path_kill_slashes(*filename);
3048
3049 /* Add the file name we are currently looking at to
3050 * the names of this unit, but only if it is a valid
3051 * unit name. */
3052 name = basename(*filename);
3053
3054 if (unit_name_is_valid(name, TEMPLATE_VALID)) {
3055
3056 id = set_get(names, name);
3057 if (!id) {
3058 id = strdup(name);
3059 if (!id)
3060 return -ENOMEM;
3061
3062 r = set_consume(names, id);
3063 if (r < 0)
3064 return r;
3065 }
3066 }
3067
3068 /* Try to open the file name, but don't if its a symlink */
3069 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
3070 if (fd >= 0)
3071 break;
3072
3073 if (errno != ELOOP)
3074 return -errno;
3075
3076 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3077 r = readlink_and_make_absolute(*filename, &target);
3078 if (r < 0)
3079 return r;
3080
3081 free(*filename);
3082 *filename = target;
3083 }
3084
3085 f = fdopen(fd, "re");
3086 if (!f) {
3087 r = -errno;
3088 close_nointr_nofail(fd);
3089 return r;
3090 }
3091
3092 *_f = f;
3093 *_final = id;
3094 return 0;
3095 }
3096
3097 static int merge_by_names(Unit **u, Set *names, const char *id) {
3098 char *k;
3099 int r;
3100
3101 assert(u);
3102 assert(*u);
3103 assert(names);
3104
3105 /* Let's try to add in all symlink names we found */
3106 while ((k = set_steal_first(names))) {
3107
3108 /* First try to merge in the other name into our
3109 * unit */
3110 r = unit_merge_by_name(*u, k);
3111 if (r < 0) {
3112 Unit *other;
3113
3114 /* Hmm, we couldn't merge the other unit into
3115 * ours? Then let's try it the other way
3116 * round */
3117
3118 other = manager_get_unit((*u)->manager, k);
3119 free(k);
3120
3121 if (other) {
3122 r = unit_merge(other, *u);
3123 if (r >= 0) {
3124 *u = other;
3125 return merge_by_names(u, names, NULL);
3126 }
3127 }
3128
3129 return r;
3130 }
3131
3132 if (id == k)
3133 unit_choose_id(*u, id);
3134
3135 free(k);
3136 }
3137
3138 return 0;
3139 }
3140
3141 static int load_from_path(Unit *u, const char *path) {
3142 int r;
3143 _cleanup_set_free_free_ Set *symlink_names = NULL;
3144 _cleanup_fclose_ FILE *f = NULL;
3145 _cleanup_free_ char *filename = NULL;
3146 char *id = NULL;
3147 Unit *merged;
3148 struct stat st;
3149
3150 assert(u);
3151 assert(path);
3152
3153 symlink_names = set_new(string_hash_func, string_compare_func);
3154 if (!symlink_names)
3155 return -ENOMEM;
3156
3157 if (path_is_absolute(path)) {
3158
3159 filename = strdup(path);
3160 if (!filename)
3161 return -ENOMEM;
3162
3163 r = open_follow(&filename, &f, symlink_names, &id);
3164 if (r < 0) {
3165 free(filename);
3166 filename = NULL;
3167
3168 if (r != -ENOENT)
3169 return r;
3170 }
3171
3172 } else {
3173 char **p;
3174
3175 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
3176
3177 /* Instead of opening the path right away, we manually
3178 * follow all symlinks and add their name to our unit
3179 * name set while doing so */
3180 filename = path_make_absolute(path, *p);
3181 if (!filename)
3182 return -ENOMEM;
3183
3184 if (u->manager->unit_path_cache &&
3185 !set_get(u->manager->unit_path_cache, filename))
3186 r = -ENOENT;
3187 else
3188 r = open_follow(&filename, &f, symlink_names, &id);
3189
3190 if (r < 0) {
3191 free(filename);
3192 filename = NULL;
3193
3194 if (r != -ENOENT)
3195 return r;
3196
3197 /* Empty the symlink names for the next run */
3198 set_clear_free(symlink_names);
3199 continue;
3200 }
3201
3202 break;
3203 }
3204 }
3205
3206 if (!filename)
3207 /* Hmm, no suitable file found? */
3208 return 0;
3209
3210 merged = u;
3211 r = merge_by_names(&merged, symlink_names, id);
3212 if (r < 0)
3213 return r;
3214
3215 if (merged != u) {
3216 u->load_state = UNIT_MERGED;
3217 return 0;
3218 }
3219
3220 if (fstat(fileno(f), &st) < 0)
3221 return -errno;
3222
3223 if (null_or_empty(&st))
3224 u->load_state = UNIT_MASKED;
3225 else {
3226 u->load_state = UNIT_LOADED;
3227
3228 /* Now, parse the file contents */
3229 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
3230 config_item_perf_lookup,
3231 (void*) load_fragment_gperf_lookup, false, true, u);
3232 if (r < 0)
3233 return r;
3234 }
3235
3236 free(u->fragment_path);
3237 u->fragment_path = filename;
3238 filename = NULL;
3239
3240 u->fragment_mtime = timespec_load(&st.st_mtim);
3241
3242 if (u->source_path) {
3243 if (stat(u->source_path, &st) >= 0)
3244 u->source_mtime = timespec_load(&st.st_mtim);
3245 else
3246 u->source_mtime = 0;
3247 }
3248
3249 return 0;
3250 }
3251
3252 int unit_load_fragment(Unit *u) {
3253 int r;
3254 Iterator i;
3255 const char *t;
3256
3257 assert(u);
3258 assert(u->load_state == UNIT_STUB);
3259 assert(u->id);
3260
3261 /* First, try to find the unit under its id. We always look
3262 * for unit files in the default directories, to make it easy
3263 * to override things by placing things in /etc/systemd/system */
3264 r = load_from_path(u, u->id);
3265 if (r < 0)
3266 return r;
3267
3268 /* Try to find an alias we can load this with */
3269 if (u->load_state == UNIT_STUB)
3270 SET_FOREACH(t, u->names, i) {
3271
3272 if (t == u->id)
3273 continue;
3274
3275 r = load_from_path(u, t);
3276 if (r < 0)
3277 return r;
3278
3279 if (u->load_state != UNIT_STUB)
3280 break;
3281 }
3282
3283 /* And now, try looking for it under the suggested (originally linked) path */
3284 if (u->load_state == UNIT_STUB && u->fragment_path) {
3285
3286 r = load_from_path(u, u->fragment_path);
3287 if (r < 0)
3288 return r;
3289
3290 if (u->load_state == UNIT_STUB) {
3291 /* Hmm, this didn't work? Then let's get rid
3292 * of the fragment path stored for us, so that
3293 * we don't point to an invalid location. */
3294 free(u->fragment_path);
3295 u->fragment_path = NULL;
3296 }
3297 }
3298
3299 /* Look for a template */
3300 if (u->load_state == UNIT_STUB && u->instance) {
3301 _cleanup_free_ char *k;
3302
3303 k = unit_name_template(u->id);
3304 if (!k)
3305 return -ENOMEM;
3306
3307 r = load_from_path(u, k);
3308 if (r < 0)
3309 return r;
3310
3311 if (u->load_state == UNIT_STUB)
3312 SET_FOREACH(t, u->names, i) {
3313 _cleanup_free_ char *z = NULL;
3314
3315 if (t == u->id)
3316 continue;
3317
3318 z = unit_name_template(t);
3319 if (!z)
3320 return -ENOMEM;
3321
3322 r = load_from_path(u, z);
3323 if (r < 0)
3324 return r;
3325
3326 if (u->load_state != UNIT_STUB)
3327 break;
3328 }
3329 }
3330
3331 return 0;
3332 }
3333
3334 void unit_dump_config_items(FILE *f) {
3335 static const struct {
3336 const ConfigParserCallback callback;
3337 const char *rvalue;
3338 } table[] = {
3339 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_LIBWRAP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3340 { config_parse_warn_compat, "NOTSUPPORTED" },
3341 #endif
3342 { config_parse_int, "INTEGER" },
3343 { config_parse_unsigned, "UNSIGNED" },
3344 { config_parse_iec_size, "SIZE" },
3345 { config_parse_iec_off, "SIZE" },
3346 { config_parse_si_size, "SIZE" },
3347 { config_parse_bool, "BOOLEAN" },
3348 { config_parse_string, "STRING" },
3349 { config_parse_path, "PATH" },
3350 { config_parse_unit_path_printf, "PATH" },
3351 { config_parse_strv, "STRING [...]" },
3352 { config_parse_exec_nice, "NICE" },
3353 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
3354 { config_parse_exec_io_class, "IOCLASS" },
3355 { config_parse_exec_io_priority, "IOPRIORITY" },
3356 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
3357 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
3358 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
3359 { config_parse_mode, "MODE" },
3360 { config_parse_unit_env_file, "FILE" },
3361 { config_parse_output, "OUTPUT" },
3362 { config_parse_input, "INPUT" },
3363 { config_parse_log_facility, "FACILITY" },
3364 { config_parse_log_level, "LEVEL" },
3365 { config_parse_exec_capabilities, "CAPABILITIES" },
3366 { config_parse_exec_secure_bits, "SECUREBITS" },
3367 { config_parse_bounding_set, "BOUNDINGSET" },
3368 { config_parse_limit, "LIMIT" },
3369 { config_parse_unit_deps, "UNIT [...]" },
3370 { config_parse_exec, "PATH [ARGUMENT [...]]" },
3371 { config_parse_service_type, "SERVICETYPE" },
3372 { config_parse_service_restart, "SERVICERESTART" },
3373 #ifdef HAVE_SYSV_COMPAT
3374 { config_parse_sysv_priority, "SYSVPRIORITY" },
3375 #endif
3376 { config_parse_kill_mode, "KILLMODE" },
3377 { config_parse_kill_signal, "SIGNAL" },
3378 { config_parse_socket_listen, "SOCKET [...]" },
3379 { config_parse_socket_bind, "SOCKETBIND" },
3380 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
3381 { config_parse_sec, "SECONDS" },
3382 { config_parse_nsec, "NANOSECONDS" },
3383 { config_parse_namespace_path_strv, "PATH [...]" },
3384 { config_parse_unit_requires_mounts_for, "PATH [...]" },
3385 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
3386 { config_parse_unit_string_printf, "STRING" },
3387 { config_parse_trigger_unit, "UNIT" },
3388 { config_parse_timer, "TIMER" },
3389 { config_parse_path_spec, "PATH" },
3390 { config_parse_notify_access, "ACCESS" },
3391 { config_parse_ip_tos, "TOS" },
3392 { config_parse_unit_condition_path, "CONDITION" },
3393 { config_parse_unit_condition_string, "CONDITION" },
3394 { config_parse_unit_condition_null, "CONDITION" },
3395 { config_parse_unit_slice, "SLICE" },
3396 { config_parse_documentation, "URL" },
3397 { config_parse_service_timeout, "SECONDS" },
3398 { config_parse_start_limit_action, "ACTION" },
3399 { config_parse_set_status, "STATUS" },
3400 { config_parse_service_sockets, "SOCKETS" },
3401 { config_parse_environ, "ENVIRON" },
3402 #ifdef HAVE_SECCOMP
3403 { config_parse_syscall_filter, "SYSCALLS" },
3404 { config_parse_syscall_archs, "ARCHS" },
3405 { config_parse_syscall_errno, "ERRNO" },
3406 { config_parse_address_families, "FAMILIES" },
3407 #endif
3408 { config_parse_cpu_shares, "SHARES" },
3409 { config_parse_memory_limit, "LIMIT" },
3410 { config_parse_device_allow, "DEVICE" },
3411 { config_parse_device_policy, "POLICY" },
3412 { config_parse_blockio_bandwidth, "BANDWIDTH" },
3413 { config_parse_blockio_weight, "WEIGHT" },
3414 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
3415 { config_parse_long, "LONG" },
3416 { config_parse_socket_service, "SERVICE" },
3417 #ifdef HAVE_SELINUX
3418 { config_parse_exec_selinux_context, "LABEL" },
3419 #endif
3420 { config_parse_job_mode, "MODE" },
3421 { config_parse_job_mode_isolate, "BOOLEAN" },
3422 { config_parse_personality, "PERSONALITY" },
3423 };
3424
3425 const char *prev = NULL;
3426 const char *i;
3427
3428 assert(f);
3429
3430 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
3431 const char *rvalue = "OTHER", *lvalue;
3432 unsigned j;
3433 size_t prefix_len;
3434 const char *dot;
3435 const ConfigPerfItem *p;
3436
3437 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
3438
3439 dot = strchr(i, '.');
3440 lvalue = dot ? dot + 1 : i;
3441 prefix_len = dot-i;
3442
3443 if (dot)
3444 if (!prev || !strneq(prev, i, prefix_len+1)) {
3445 if (prev)
3446 fputc('\n', f);
3447
3448 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
3449 }
3450
3451 for (j = 0; j < ELEMENTSOF(table); j++)
3452 if (p->parse == table[j].callback) {
3453 rvalue = table[j].rvalue;
3454 break;
3455 }
3456
3457 fprintf(f, "%s=%s\n", lvalue, rvalue);
3458 prev = i;
3459 }
3460 }