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