]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/load-fragment.c
fix spelling of privilege
[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 CGroupContext *c = data;
2434 unsigned long lu;
2435 int r;
2436
2437 assert(filename);
2438 assert(lvalue);
2439 assert(rvalue);
2440
2441 if (isempty(rvalue)) {
2442 c->cpu_shares = 1024;
2443 return 0;
2444 }
2445
2446 r = safe_atolu(rvalue, &lu);
2447 if (r < 0 || lu <= 0) {
2448 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2449 "CPU shares '%s' invalid. Ignoring.", rvalue);
2450 return 0;
2451 }
2452
2453 c->cpu_shares = lu;
2454 return 0;
2455 }
2456
2457 int config_parse_cpu_quota(
2458 const char *unit,
2459 const char *filename,
2460 unsigned line,
2461 const char *section,
2462 unsigned section_line,
2463 const char *lvalue,
2464 int ltype,
2465 const char *rvalue,
2466 void *data,
2467 void *userdata) {
2468
2469 CGroupContext *c = data;
2470 int r;
2471
2472 assert(filename);
2473 assert(lvalue);
2474 assert(rvalue);
2475
2476 if (isempty(rvalue)) {
2477 c->cpu_quota_per_sec_usec = (usec_t) -1;
2478 c->cpu_quota_usec = (usec_t) -1;
2479 return 0;
2480 }
2481
2482 if (endswith(rvalue, "%")) {
2483 double percent;
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 c->cpu_quota_usec = (usec_t) -1;
2492 } else {
2493 r = parse_sec(rvalue, &c->cpu_quota_usec);
2494 if (r < 0) {
2495 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "CPU quota '%s' invalid. Ignoring.", rvalue);
2496 return 0;
2497 }
2498
2499 c->cpu_quota_per_sec_usec = (usec_t) -1;
2500 }
2501
2502 return 0;
2503 }
2504
2505 int config_parse_memory_limit(
2506 const char *unit,
2507 const char *filename,
2508 unsigned line,
2509 const char *section,
2510 unsigned section_line,
2511 const char *lvalue,
2512 int ltype,
2513 const char *rvalue,
2514 void *data,
2515 void *userdata) {
2516
2517 CGroupContext *c = data;
2518 off_t bytes;
2519 int r;
2520
2521 if (isempty(rvalue)) {
2522 c->memory_limit = (uint64_t) -1;
2523 return 0;
2524 }
2525
2526 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2527
2528 r = parse_size(rvalue, 1024, &bytes);
2529 if (r < 0) {
2530 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Memory limit '%s' invalid. Ignoring.", rvalue);
2531 return 0;
2532 }
2533
2534 c->memory_limit = (uint64_t) bytes;
2535 return 0;
2536 }
2537
2538 int config_parse_device_allow(
2539 const char *unit,
2540 const char *filename,
2541 unsigned line,
2542 const char *section,
2543 unsigned section_line,
2544 const char *lvalue,
2545 int ltype,
2546 const char *rvalue,
2547 void *data,
2548 void *userdata) {
2549
2550 _cleanup_free_ char *path = NULL;
2551 CGroupContext *c = data;
2552 CGroupDeviceAllow *a;
2553 const char *m;
2554 size_t n;
2555
2556 if (isempty(rvalue)) {
2557 while (c->device_allow)
2558 cgroup_context_free_device_allow(c, c->device_allow);
2559
2560 return 0;
2561 }
2562
2563 n = strcspn(rvalue, WHITESPACE);
2564 path = strndup(rvalue, n);
2565 if (!path)
2566 return log_oom();
2567
2568 if (!startswith(path, "/dev/") &&
2569 !startswith(path, "block-") &&
2570 !startswith(path, "char-")) {
2571 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device node path '%s'. Ignoring.", path);
2572 return 0;
2573 }
2574
2575 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2576 if (isempty(m))
2577 m = "rwm";
2578
2579 if (!in_charset(m, "rwm")) {
2580 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device rights '%s'. Ignoring.", m);
2581 return 0;
2582 }
2583
2584 a = new0(CGroupDeviceAllow, 1);
2585 if (!a)
2586 return log_oom();
2587
2588 a->path = path;
2589 path = NULL;
2590 a->r = !!strchr(m, 'r');
2591 a->w = !!strchr(m, 'w');
2592 a->m = !!strchr(m, 'm');
2593
2594 LIST_PREPEND(device_allow, c->device_allow, a);
2595 return 0;
2596 }
2597
2598 int config_parse_blockio_weight(
2599 const char *unit,
2600 const char *filename,
2601 unsigned line,
2602 const char *section,
2603 unsigned section_line,
2604 const char *lvalue,
2605 int ltype,
2606 const char *rvalue,
2607 void *data,
2608 void *userdata) {
2609
2610 CGroupContext *c = data;
2611 unsigned long lu;
2612 int r;
2613
2614 assert(filename);
2615 assert(lvalue);
2616 assert(rvalue);
2617
2618 if (isempty(rvalue)) {
2619 c->blockio_weight = 1000;
2620 return 0;
2621 }
2622
2623 r = safe_atolu(rvalue, &lu);
2624 if (r < 0 || lu < 10 || lu > 1000) {
2625 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2626 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2627 return 0;
2628 }
2629
2630 c->blockio_weight = lu;
2631
2632 return 0;
2633 }
2634
2635 int config_parse_blockio_device_weight(
2636 const char *unit,
2637 const char *filename,
2638 unsigned line,
2639 const char *section,
2640 unsigned section_line,
2641 const char *lvalue,
2642 int ltype,
2643 const char *rvalue,
2644 void *data,
2645 void *userdata) {
2646
2647 _cleanup_free_ char *path = NULL;
2648 CGroupBlockIODeviceWeight *w;
2649 CGroupContext *c = data;
2650 unsigned long lu;
2651 const char *weight;
2652 size_t n;
2653 int r;
2654
2655 assert(filename);
2656 assert(lvalue);
2657 assert(rvalue);
2658
2659 if (isempty(rvalue)) {
2660 while (c->blockio_device_weights)
2661 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2662
2663 return 0;
2664 }
2665
2666 n = strcspn(rvalue, WHITESPACE);
2667 weight = rvalue + n;
2668 if (!*weight) {
2669 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2670 "Expected block device and device weight. Ignoring.");
2671 return 0;
2672 }
2673
2674 path = strndup(rvalue, n);
2675 if (!path)
2676 return log_oom();
2677
2678 if (!path_startswith(path, "/dev")) {
2679 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2680 "Invalid device node path '%s'. Ignoring.", path);
2681 return 0;
2682 }
2683
2684 weight += strspn(weight, WHITESPACE);
2685 r = safe_atolu(weight, &lu);
2686 if (r < 0 || lu < 10 || lu > 1000) {
2687 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2688 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2689 return 0;
2690 }
2691
2692
2693 w = new0(CGroupBlockIODeviceWeight, 1);
2694 if (!w)
2695 return log_oom();
2696
2697 w->path = path;
2698 path = NULL;
2699
2700 w->weight = lu;
2701
2702 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
2703 return 0;
2704 }
2705
2706 int config_parse_blockio_bandwidth(
2707 const char *unit,
2708 const char *filename,
2709 unsigned line,
2710 const char *section,
2711 unsigned section_line,
2712 const char *lvalue,
2713 int ltype,
2714 const char *rvalue,
2715 void *data,
2716 void *userdata) {
2717
2718 _cleanup_free_ char *path = NULL;
2719 CGroupBlockIODeviceBandwidth *b;
2720 CGroupContext *c = data;
2721 const char *bandwidth;
2722 off_t bytes;
2723 bool read;
2724 size_t n;
2725 int r;
2726
2727 assert(filename);
2728 assert(lvalue);
2729 assert(rvalue);
2730
2731 read = streq("BlockIOReadBandwidth", lvalue);
2732
2733 if (isempty(rvalue)) {
2734 CGroupBlockIODeviceBandwidth *next;
2735
2736 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2737 if (b->read == read)
2738 cgroup_context_free_blockio_device_bandwidth(c, b);
2739
2740 return 0;
2741 }
2742
2743 n = strcspn(rvalue, WHITESPACE);
2744 bandwidth = rvalue + n;
2745 bandwidth += strspn(bandwidth, WHITESPACE);
2746
2747 if (!*bandwidth) {
2748 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2749 "Expected space separated pair of device node and bandwidth. Ignoring.");
2750 return 0;
2751 }
2752
2753 path = strndup(rvalue, n);
2754 if (!path)
2755 return log_oom();
2756
2757 if (!path_startswith(path, "/dev")) {
2758 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2759 "Invalid device node path '%s'. Ignoring.", path);
2760 return 0;
2761 }
2762
2763 r = parse_size(bandwidth, 1000, &bytes);
2764 if (r < 0 || bytes <= 0) {
2765 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2766 return 0;
2767 }
2768
2769 b = new0(CGroupBlockIODeviceBandwidth, 1);
2770 if (!b)
2771 return log_oom();
2772
2773 b->path = path;
2774 path = NULL;
2775 b->bandwidth = (uint64_t) bytes;
2776 b->read = read;
2777
2778 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
2779
2780 return 0;
2781 }
2782
2783 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2784
2785 int config_parse_job_mode_isolate(
2786 const char *unit,
2787 const char *filename,
2788 unsigned line,
2789 const char *section,
2790 unsigned section_line,
2791 const char *lvalue,
2792 int ltype,
2793 const char *rvalue,
2794 void *data,
2795 void *userdata) {
2796
2797 JobMode *m = data;
2798 int r;
2799
2800 assert(filename);
2801 assert(lvalue);
2802 assert(rvalue);
2803
2804 r = parse_boolean(rvalue);
2805 if (r < 0) {
2806 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse boolean, ignoring: %s", rvalue);
2807 return 0;
2808 }
2809
2810 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2811 return 0;
2812 }
2813
2814 int config_parse_personality(
2815 const char *unit,
2816 const char *filename,
2817 unsigned line,
2818 const char *section,
2819 unsigned section_line,
2820 const char *lvalue,
2821 int ltype,
2822 const char *rvalue,
2823 void *data,
2824 void *userdata) {
2825
2826 unsigned long *personality = data, p;
2827
2828 assert(filename);
2829 assert(lvalue);
2830 assert(rvalue);
2831 assert(personality);
2832
2833 p = personality_from_string(rvalue);
2834 if (p == 0xffffffffUL) {
2835 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2836 "Failed to parse personality, ignoring: %s", rvalue);
2837 return 0;
2838 }
2839
2840 *personality = p;
2841 return 0;
2842 }
2843
2844 int config_parse_runtime_directory(
2845 const char *unit,
2846 const char *filename,
2847 unsigned line,
2848 const char *section,
2849 unsigned section_line,
2850 const char *lvalue,
2851 int ltype,
2852 const char *rvalue,
2853 void *data,
2854 void *userdata) {
2855
2856 char***rt = data, *w, *state;
2857 size_t l;
2858 int r;
2859
2860 assert(filename);
2861 assert(lvalue);
2862 assert(rvalue);
2863 assert(data);
2864
2865 if (isempty(rvalue)) {
2866 /* Empty assignment resets the list */
2867 strv_free(*rt);
2868 *rt = NULL;
2869 return 0;
2870 }
2871
2872 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
2873 _cleanup_free_ char *n;
2874
2875 n = strndup(w, l);
2876 if (!n)
2877 return log_oom();
2878
2879 if (!filename_is_safe(n)) {
2880 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
2881 continue;
2882 }
2883
2884 r = strv_push(rt, n);
2885 if (r < 0)
2886 return log_oom();
2887
2888 n = NULL;
2889 }
2890
2891 return 0;
2892 }
2893
2894 int config_parse_set_status(
2895 const char *unit,
2896 const char *filename,
2897 unsigned line,
2898 const char *section,
2899 unsigned section_line,
2900 const char *lvalue,
2901 int ltype,
2902 const char *rvalue,
2903 void *data,
2904 void *userdata) {
2905
2906 char *w;
2907 size_t l;
2908 char *state;
2909 int r;
2910 ExitStatusSet *status_set = data;
2911
2912 assert(filename);
2913 assert(lvalue);
2914 assert(rvalue);
2915 assert(data);
2916
2917 if (isempty(rvalue)) {
2918 /* Empty assignment resets the list */
2919
2920 set_free(status_set->signal);
2921 set_free(status_set->code);
2922
2923 status_set->signal = status_set->code = NULL;
2924 return 0;
2925 }
2926
2927 FOREACH_WORD(w, l, rvalue, state) {
2928 _cleanup_free_ char *temp;
2929 int val;
2930
2931 temp = strndup(w, l);
2932 if (!temp)
2933 return log_oom();
2934
2935 r = safe_atoi(temp, &val);
2936 if (r < 0) {
2937 val = signal_from_string_try_harder(temp);
2938
2939 if (val > 0) {
2940 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
2941 if (r < 0)
2942 return log_oom();
2943
2944 r = set_put(status_set->signal, 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 } else {
2950 log_syntax(unit, LOG_ERR, filename, line, -val, "Failed to parse value, ignoring: %s", w);
2951 return 0;
2952 }
2953 } else {
2954 if (val < 0 || val > 255)
2955 log_syntax(unit, LOG_ERR, filename, line, ERANGE, "Value %d is outside range 0-255, ignoring", val);
2956 else {
2957 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
2958 if (r < 0)
2959 return log_oom();
2960
2961 r = set_put(status_set->code, INT_TO_PTR(val));
2962 if (r < 0) {
2963 log_syntax(unit, LOG_ERR, filename, line, -r, "Unable to store: %s", w);
2964 return r;
2965 }
2966 }
2967 }
2968 }
2969
2970 return 0;
2971 }
2972
2973 int config_parse_namespace_path_strv(
2974 const char *unit,
2975 const char *filename,
2976 unsigned line,
2977 const char *section,
2978 unsigned section_line,
2979 const char *lvalue,
2980 int ltype,
2981 const char *rvalue,
2982 void *data,
2983 void *userdata) {
2984
2985 char*** sv = data, *w, *state;
2986 size_t l;
2987 int r;
2988
2989 assert(filename);
2990 assert(lvalue);
2991 assert(rvalue);
2992 assert(data);
2993
2994 if (isempty(rvalue)) {
2995 /* Empty assignment resets the list */
2996 strv_free(*sv);
2997 *sv = NULL;
2998 return 0;
2999 }
3000
3001 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
3002 _cleanup_free_ char *n;
3003 int offset;
3004
3005 n = strndup(w, l);
3006 if (!n)
3007 return log_oom();
3008
3009 if (!utf8_is_valid(n)) {
3010 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
3011 continue;
3012 }
3013
3014 offset = n[0] == '-';
3015 if (!path_is_absolute(n + offset)) {
3016 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
3017 continue;
3018 }
3019
3020 path_kill_slashes(n);
3021
3022 r = strv_push(sv, n);
3023 if (r < 0)
3024 return log_oom();
3025
3026 n = NULL;
3027 }
3028
3029 return 0;
3030 }
3031
3032 int config_parse_no_new_privileges(
3033 const char* unit,
3034 const char *filename,
3035 unsigned line,
3036 const char *section,
3037 unsigned section_line,
3038 const char *lvalue,
3039 int ltype,
3040 const char *rvalue,
3041 void *data,
3042 void *userdata) {
3043
3044 ExecContext *c = data;
3045 int k;
3046
3047 assert(filename);
3048 assert(lvalue);
3049 assert(rvalue);
3050 assert(data);
3051
3052 k = parse_boolean(rvalue);
3053 if (k < 0) {
3054 log_syntax(unit, LOG_ERR, filename, line, -k, "Failed to parse boolean value, ignoring: %s", rvalue);
3055 return 0;
3056 }
3057
3058 c->no_new_privileges = !!k;
3059 c->no_new_privileges_set = true;
3060
3061 return 0;
3062 }
3063
3064 #define FOLLOW_MAX 8
3065
3066 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
3067 unsigned c = 0;
3068 int fd, r;
3069 FILE *f;
3070 char *id = NULL;
3071
3072 assert(filename);
3073 assert(*filename);
3074 assert(_f);
3075 assert(names);
3076
3077 /* This will update the filename pointer if the loaded file is
3078 * reached by a symlink. The old string will be freed. */
3079
3080 for (;;) {
3081 char *target, *name;
3082
3083 if (c++ >= FOLLOW_MAX)
3084 return -ELOOP;
3085
3086 path_kill_slashes(*filename);
3087
3088 /* Add the file name we are currently looking at to
3089 * the names of this unit, but only if it is a valid
3090 * unit name. */
3091 name = basename(*filename);
3092
3093 if (unit_name_is_valid(name, TEMPLATE_VALID)) {
3094
3095 id = set_get(names, name);
3096 if (!id) {
3097 id = strdup(name);
3098 if (!id)
3099 return -ENOMEM;
3100
3101 r = set_consume(names, id);
3102 if (r < 0)
3103 return r;
3104 }
3105 }
3106
3107 /* Try to open the file name, but don't if its a symlink */
3108 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
3109 if (fd >= 0)
3110 break;
3111
3112 if (errno != ELOOP)
3113 return -errno;
3114
3115 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3116 r = readlink_and_make_absolute(*filename, &target);
3117 if (r < 0)
3118 return r;
3119
3120 free(*filename);
3121 *filename = target;
3122 }
3123
3124 f = fdopen(fd, "re");
3125 if (!f) {
3126 r = -errno;
3127 safe_close(fd);
3128 return r;
3129 }
3130
3131 *_f = f;
3132 *_final = id;
3133 return 0;
3134 }
3135
3136 static int merge_by_names(Unit **u, Set *names, const char *id) {
3137 char *k;
3138 int r;
3139
3140 assert(u);
3141 assert(*u);
3142 assert(names);
3143
3144 /* Let's try to add in all symlink names we found */
3145 while ((k = set_steal_first(names))) {
3146
3147 /* First try to merge in the other name into our
3148 * unit */
3149 r = unit_merge_by_name(*u, k);
3150 if (r < 0) {
3151 Unit *other;
3152
3153 /* Hmm, we couldn't merge the other unit into
3154 * ours? Then let's try it the other way
3155 * round */
3156
3157 other = manager_get_unit((*u)->manager, k);
3158 free(k);
3159
3160 if (other) {
3161 r = unit_merge(other, *u);
3162 if (r >= 0) {
3163 *u = other;
3164 return merge_by_names(u, names, NULL);
3165 }
3166 }
3167
3168 return r;
3169 }
3170
3171 if (id == k)
3172 unit_choose_id(*u, id);
3173
3174 free(k);
3175 }
3176
3177 return 0;
3178 }
3179
3180 static int load_from_path(Unit *u, const char *path) {
3181 int r;
3182 _cleanup_set_free_free_ Set *symlink_names = NULL;
3183 _cleanup_fclose_ FILE *f = NULL;
3184 _cleanup_free_ char *filename = NULL;
3185 char *id = NULL;
3186 Unit *merged;
3187 struct stat st;
3188
3189 assert(u);
3190 assert(path);
3191
3192 symlink_names = set_new(string_hash_func, string_compare_func);
3193 if (!symlink_names)
3194 return -ENOMEM;
3195
3196 if (path_is_absolute(path)) {
3197
3198 filename = strdup(path);
3199 if (!filename)
3200 return -ENOMEM;
3201
3202 r = open_follow(&filename, &f, symlink_names, &id);
3203 if (r < 0) {
3204 free(filename);
3205 filename = NULL;
3206
3207 if (r != -ENOENT)
3208 return r;
3209 }
3210
3211 } else {
3212 char **p;
3213
3214 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
3215
3216 /* Instead of opening the path right away, we manually
3217 * follow all symlinks and add their name to our unit
3218 * name set while doing so */
3219 filename = path_make_absolute(path, *p);
3220 if (!filename)
3221 return -ENOMEM;
3222
3223 if (u->manager->unit_path_cache &&
3224 !set_get(u->manager->unit_path_cache, filename))
3225 r = -ENOENT;
3226 else
3227 r = open_follow(&filename, &f, symlink_names, &id);
3228
3229 if (r < 0) {
3230 free(filename);
3231 filename = NULL;
3232
3233 if (r != -ENOENT)
3234 return r;
3235
3236 /* Empty the symlink names for the next run */
3237 set_clear_free(symlink_names);
3238 continue;
3239 }
3240
3241 break;
3242 }
3243 }
3244
3245 if (!filename)
3246 /* Hmm, no suitable file found? */
3247 return 0;
3248
3249 merged = u;
3250 r = merge_by_names(&merged, symlink_names, id);
3251 if (r < 0)
3252 return r;
3253
3254 if (merged != u) {
3255 u->load_state = UNIT_MERGED;
3256 return 0;
3257 }
3258
3259 if (fstat(fileno(f), &st) < 0)
3260 return -errno;
3261
3262 if (null_or_empty(&st))
3263 u->load_state = UNIT_MASKED;
3264 else {
3265 u->load_state = UNIT_LOADED;
3266
3267 /* Now, parse the file contents */
3268 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
3269 config_item_perf_lookup,
3270 (void*) load_fragment_gperf_lookup, false, true, u);
3271 if (r < 0)
3272 return r;
3273 }
3274
3275 free(u->fragment_path);
3276 u->fragment_path = filename;
3277 filename = NULL;
3278
3279 u->fragment_mtime = timespec_load(&st.st_mtim);
3280
3281 if (u->source_path) {
3282 if (stat(u->source_path, &st) >= 0)
3283 u->source_mtime = timespec_load(&st.st_mtim);
3284 else
3285 u->source_mtime = 0;
3286 }
3287
3288 return 0;
3289 }
3290
3291 int unit_load_fragment(Unit *u) {
3292 int r;
3293 Iterator i;
3294 const char *t;
3295
3296 assert(u);
3297 assert(u->load_state == UNIT_STUB);
3298 assert(u->id);
3299
3300 /* First, try to find the unit under its id. We always look
3301 * for unit files in the default directories, to make it easy
3302 * to override things by placing things in /etc/systemd/system */
3303 r = load_from_path(u, u->id);
3304 if (r < 0)
3305 return r;
3306
3307 /* Try to find an alias we can load this with */
3308 if (u->load_state == UNIT_STUB)
3309 SET_FOREACH(t, u->names, i) {
3310
3311 if (t == u->id)
3312 continue;
3313
3314 r = load_from_path(u, t);
3315 if (r < 0)
3316 return r;
3317
3318 if (u->load_state != UNIT_STUB)
3319 break;
3320 }
3321
3322 /* And now, try looking for it under the suggested (originally linked) path */
3323 if (u->load_state == UNIT_STUB && u->fragment_path) {
3324
3325 r = load_from_path(u, u->fragment_path);
3326 if (r < 0)
3327 return r;
3328
3329 if (u->load_state == UNIT_STUB) {
3330 /* Hmm, this didn't work? Then let's get rid
3331 * of the fragment path stored for us, so that
3332 * we don't point to an invalid location. */
3333 free(u->fragment_path);
3334 u->fragment_path = NULL;
3335 }
3336 }
3337
3338 /* Look for a template */
3339 if (u->load_state == UNIT_STUB && u->instance) {
3340 _cleanup_free_ char *k;
3341
3342 k = unit_name_template(u->id);
3343 if (!k)
3344 return -ENOMEM;
3345
3346 r = load_from_path(u, k);
3347 if (r < 0)
3348 return r;
3349
3350 if (u->load_state == UNIT_STUB)
3351 SET_FOREACH(t, u->names, i) {
3352 _cleanup_free_ char *z = NULL;
3353
3354 if (t == u->id)
3355 continue;
3356
3357 z = unit_name_template(t);
3358 if (!z)
3359 return -ENOMEM;
3360
3361 r = load_from_path(u, z);
3362 if (r < 0)
3363 return r;
3364
3365 if (u->load_state != UNIT_STUB)
3366 break;
3367 }
3368 }
3369
3370 return 0;
3371 }
3372
3373 void unit_dump_config_items(FILE *f) {
3374 static const struct {
3375 const ConfigParserCallback callback;
3376 const char *rvalue;
3377 } table[] = {
3378 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3379 { config_parse_warn_compat, "NOTSUPPORTED" },
3380 #endif
3381 { config_parse_int, "INTEGER" },
3382 { config_parse_unsigned, "UNSIGNED" },
3383 { config_parse_iec_size, "SIZE" },
3384 { config_parse_iec_off, "SIZE" },
3385 { config_parse_si_size, "SIZE" },
3386 { config_parse_bool, "BOOLEAN" },
3387 { config_parse_string, "STRING" },
3388 { config_parse_path, "PATH" },
3389 { config_parse_unit_path_printf, "PATH" },
3390 { config_parse_strv, "STRING [...]" },
3391 { config_parse_exec_nice, "NICE" },
3392 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
3393 { config_parse_exec_io_class, "IOCLASS" },
3394 { config_parse_exec_io_priority, "IOPRIORITY" },
3395 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
3396 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
3397 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
3398 { config_parse_mode, "MODE" },
3399 { config_parse_unit_env_file, "FILE" },
3400 { config_parse_output, "OUTPUT" },
3401 { config_parse_input, "INPUT" },
3402 { config_parse_log_facility, "FACILITY" },
3403 { config_parse_log_level, "LEVEL" },
3404 { config_parse_exec_capabilities, "CAPABILITIES" },
3405 { config_parse_exec_secure_bits, "SECUREBITS" },
3406 { config_parse_bounding_set, "BOUNDINGSET" },
3407 { config_parse_limit, "LIMIT" },
3408 { config_parse_unit_deps, "UNIT [...]" },
3409 { config_parse_exec, "PATH [ARGUMENT [...]]" },
3410 { config_parse_service_type, "SERVICETYPE" },
3411 { config_parse_service_restart, "SERVICERESTART" },
3412 #ifdef HAVE_SYSV_COMPAT
3413 { config_parse_sysv_priority, "SYSVPRIORITY" },
3414 #endif
3415 { config_parse_kill_mode, "KILLMODE" },
3416 { config_parse_kill_signal, "SIGNAL" },
3417 { config_parse_socket_listen, "SOCKET [...]" },
3418 { config_parse_socket_bind, "SOCKETBIND" },
3419 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
3420 { config_parse_sec, "SECONDS" },
3421 { config_parse_nsec, "NANOSECONDS" },
3422 { config_parse_namespace_path_strv, "PATH [...]" },
3423 { config_parse_unit_requires_mounts_for, "PATH [...]" },
3424 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
3425 { config_parse_unit_string_printf, "STRING" },
3426 { config_parse_trigger_unit, "UNIT" },
3427 { config_parse_timer, "TIMER" },
3428 { config_parse_path_spec, "PATH" },
3429 { config_parse_notify_access, "ACCESS" },
3430 { config_parse_ip_tos, "TOS" },
3431 { config_parse_unit_condition_path, "CONDITION" },
3432 { config_parse_unit_condition_string, "CONDITION" },
3433 { config_parse_unit_condition_null, "CONDITION" },
3434 { config_parse_unit_slice, "SLICE" },
3435 { config_parse_documentation, "URL" },
3436 { config_parse_service_timeout, "SECONDS" },
3437 { config_parse_failure_action, "ACTION" },
3438 { config_parse_set_status, "STATUS" },
3439 { config_parse_service_sockets, "SOCKETS" },
3440 { config_parse_environ, "ENVIRON" },
3441 #ifdef HAVE_SECCOMP
3442 { config_parse_syscall_filter, "SYSCALLS" },
3443 { config_parse_syscall_archs, "ARCHS" },
3444 { config_parse_syscall_errno, "ERRNO" },
3445 { config_parse_address_families, "FAMILIES" },
3446 #endif
3447 { config_parse_cpu_shares, "SHARES" },
3448 { config_parse_memory_limit, "LIMIT" },
3449 { config_parse_device_allow, "DEVICE" },
3450 { config_parse_device_policy, "POLICY" },
3451 { config_parse_blockio_bandwidth, "BANDWIDTH" },
3452 { config_parse_blockio_weight, "WEIGHT" },
3453 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
3454 { config_parse_long, "LONG" },
3455 { config_parse_socket_service, "SERVICE" },
3456 #ifdef HAVE_SELINUX
3457 { config_parse_exec_selinux_context, "LABEL" },
3458 #endif
3459 { config_parse_job_mode, "MODE" },
3460 { config_parse_job_mode_isolate, "BOOLEAN" },
3461 { config_parse_personality, "PERSONALITY" },
3462 };
3463
3464 const char *prev = NULL;
3465 const char *i;
3466
3467 assert(f);
3468
3469 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
3470 const char *rvalue = "OTHER", *lvalue;
3471 unsigned j;
3472 size_t prefix_len;
3473 const char *dot;
3474 const ConfigPerfItem *p;
3475
3476 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
3477
3478 dot = strchr(i, '.');
3479 lvalue = dot ? dot + 1 : i;
3480 prefix_len = dot-i;
3481
3482 if (dot)
3483 if (!prev || !strneq(prev, i, prefix_len+1)) {
3484 if (prev)
3485 fputc('\n', f);
3486
3487 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
3488 }
3489
3490 for (j = 0; j < ELEMENTSOF(table); j++)
3491 if (p->parse == table[j].callback) {
3492 rvalue = table[j].rvalue;
3493 break;
3494 }
3495
3496 fprintf(f, "%s=%s\n", lvalue, rvalue);
3497 prev = i;
3498 }
3499 }