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