]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/load-fragment.c
treewide: fix typos and remove accidental repetition of words
[thirdparty/systemd.git] / src / core / load-fragment.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5 Copyright 2012 Holger Hans Peter Freyther
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <linux/fs.h>
24 #include <linux/oom.h>
25 #ifdef HAVE_SECCOMP
26 #include <seccomp.h>
27 #endif
28 #include <sched.h>
29 #include <string.h>
30 #include <sys/resource.h>
31 #include <sys/stat.h>
32
33 #include "af-list.h"
34 #include "alloc-util.h"
35 #include "bus-error.h"
36 #include "bus-internal.h"
37 #include "bus-util.h"
38 #include "cap-list.h"
39 #include "capability-util.h"
40 #include "cgroup.h"
41 #include "conf-parser.h"
42 #include "cpu-set-util.h"
43 #include "env-util.h"
44 #include "errno-list.h"
45 #include "escape.h"
46 #include "fd-util.h"
47 #include "fs-util.h"
48 #include "ioprio.h"
49 #include "load-fragment.h"
50 #include "log.h"
51 #include "missing.h"
52 #include "parse-util.h"
53 #include "path-util.h"
54 #include "process-util.h"
55 #include "rlimit-util.h"
56 #ifdef HAVE_SECCOMP
57 #include "seccomp-util.h"
58 #endif
59 #include "securebits.h"
60 #include "signal-util.h"
61 #include "stat-util.h"
62 #include "string-util.h"
63 #include "strv.h"
64 #include "unit-name.h"
65 #include "unit-printf.h"
66 #include "unit.h"
67 #include "utf8.h"
68 #include "web-util.h"
69
70 int config_parse_warn_compat(
71 const char *unit,
72 const char *filename,
73 unsigned line,
74 const char *section,
75 unsigned section_line,
76 const char *lvalue,
77 int ltype,
78 const char *rvalue,
79 void *data,
80 void *userdata) {
81 Disabled reason = ltype;
82
83 switch(reason) {
84 case DISABLED_CONFIGURATION:
85 log_syntax(unit, LOG_DEBUG, filename, line, 0,
86 "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
87 break;
88 case DISABLED_LEGACY:
89 log_syntax(unit, LOG_INFO, filename, line, 0,
90 "Support for option %s= has been removed and it is ignored", lvalue);
91 break;
92 case DISABLED_EXPERIMENTAL:
93 log_syntax(unit, LOG_INFO, filename, line, 0,
94 "Support for option %s= has not yet been enabled and it is ignored", lvalue);
95 break;
96 };
97
98 return 0;
99 }
100
101 int config_parse_unit_deps(
102 const char *unit,
103 const char *filename,
104 unsigned line,
105 const char *section,
106 unsigned section_line,
107 const char *lvalue,
108 int ltype,
109 const char *rvalue,
110 void *data,
111 void *userdata) {
112
113 UnitDependency d = ltype;
114 Unit *u = userdata;
115 const char *p;
116
117 assert(filename);
118 assert(lvalue);
119 assert(rvalue);
120
121 p = rvalue;
122 for (;;) {
123 _cleanup_free_ char *word = NULL, *k = NULL;
124 int r;
125
126 r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
127 if (r == 0)
128 break;
129 if (r == -ENOMEM)
130 return log_oom();
131 if (r < 0) {
132 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
133 break;
134 }
135
136 r = unit_name_printf(u, word, &k);
137 if (r < 0) {
138 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
139 continue;
140 }
141
142 r = unit_add_dependency_by_name(u, d, k, NULL, true);
143 if (r < 0)
144 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
145 }
146
147 return 0;
148 }
149
150 int config_parse_obsolete_unit_deps(
151 const char *unit,
152 const char *filename,
153 unsigned line,
154 const char *section,
155 unsigned section_line,
156 const char *lvalue,
157 int ltype,
158 const char *rvalue,
159 void *data,
160 void *userdata) {
161
162 log_syntax(unit, LOG_WARNING, filename, line, 0,
163 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue, unit_dependency_to_string(ltype));
164
165 return config_parse_unit_deps(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
166 }
167
168 int config_parse_unit_string_printf(
169 const char *unit,
170 const char *filename,
171 unsigned line,
172 const char *section,
173 unsigned section_line,
174 const char *lvalue,
175 int ltype,
176 const char *rvalue,
177 void *data,
178 void *userdata) {
179
180 _cleanup_free_ char *k = NULL;
181 Unit *u = userdata;
182 int r;
183
184 assert(filename);
185 assert(lvalue);
186 assert(rvalue);
187 assert(u);
188
189 r = unit_full_printf(u, rvalue, &k);
190 if (r < 0) {
191 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
192 return 0;
193 }
194
195 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
196 }
197
198 int config_parse_unit_strv_printf(
199 const char *unit,
200 const char *filename,
201 unsigned line,
202 const char *section,
203 unsigned section_line,
204 const char *lvalue,
205 int ltype,
206 const char *rvalue,
207 void *data,
208 void *userdata) {
209
210 Unit *u = userdata;
211 _cleanup_free_ char *k = NULL;
212 int r;
213
214 assert(filename);
215 assert(lvalue);
216 assert(rvalue);
217 assert(u);
218
219 r = unit_full_printf(u, rvalue, &k);
220 if (r < 0) {
221 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
222 return 0;
223 }
224
225 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
226 }
227
228 int config_parse_unit_path_printf(
229 const char *unit,
230 const char *filename,
231 unsigned line,
232 const char *section,
233 unsigned section_line,
234 const char *lvalue,
235 int ltype,
236 const char *rvalue,
237 void *data,
238 void *userdata) {
239
240 _cleanup_free_ char *k = NULL;
241 Unit *u = userdata;
242 int r;
243
244 assert(filename);
245 assert(lvalue);
246 assert(rvalue);
247 assert(u);
248
249 r = unit_full_printf(u, rvalue, &k);
250 if (r < 0) {
251 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
252 return 0;
253 }
254
255 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
256 }
257
258 int config_parse_unit_path_strv_printf(
259 const char *unit,
260 const char *filename,
261 unsigned line,
262 const char *section,
263 unsigned section_line,
264 const char *lvalue,
265 int ltype,
266 const char *rvalue,
267 void *data,
268 void *userdata) {
269
270 char ***x = data;
271 const char *word, *state;
272 Unit *u = userdata;
273 size_t l;
274 int r;
275
276 assert(filename);
277 assert(lvalue);
278 assert(rvalue);
279 assert(u);
280
281 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
282 _cleanup_free_ char *k = NULL;
283 char t[l+1];
284
285 memcpy(t, word, l);
286 t[l] = 0;
287
288 r = unit_full_printf(u, t, &k);
289 if (r < 0) {
290 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", t);
291 return 0;
292 }
293
294 if (!utf8_is_valid(k)) {
295 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
296 return 0;
297 }
298
299 if (!path_is_absolute(k)) {
300 log_syntax(unit, LOG_ERR, filename, line, 0, "Symlink path %s is not absolute, ignoring: %m", k);
301 return 0;
302 }
303
304 path_kill_slashes(k);
305
306 r = strv_push(x, k);
307 if (r < 0)
308 return log_oom();
309
310 k = NULL;
311 }
312 if (!isempty(state))
313 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring.");
314
315 return 0;
316 }
317
318 int config_parse_socket_listen(const char *unit,
319 const char *filename,
320 unsigned line,
321 const char *section,
322 unsigned section_line,
323 const char *lvalue,
324 int ltype,
325 const char *rvalue,
326 void *data,
327 void *userdata) {
328
329 _cleanup_free_ SocketPort *p = NULL;
330 SocketPort *tail;
331 Socket *s;
332 int r;
333
334 assert(filename);
335 assert(lvalue);
336 assert(rvalue);
337 assert(data);
338
339 s = SOCKET(data);
340
341 if (isempty(rvalue)) {
342 /* An empty assignment removes all ports */
343 socket_free_ports(s);
344 return 0;
345 }
346
347 p = new0(SocketPort, 1);
348 if (!p)
349 return log_oom();
350
351 if (ltype != SOCKET_SOCKET) {
352
353 p->type = ltype;
354 r = unit_full_printf(UNIT(s), rvalue, &p->path);
355 if (r < 0) {
356 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
357 return 0;
358 }
359
360 path_kill_slashes(p->path);
361
362 } else if (streq(lvalue, "ListenNetlink")) {
363 _cleanup_free_ char *k = NULL;
364
365 p->type = SOCKET_SOCKET;
366 r = unit_full_printf(UNIT(s), rvalue, &k);
367 if (r < 0) {
368 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
369 return 0;
370 }
371
372 r = socket_address_parse_netlink(&p->address, k);
373 if (r < 0) {
374 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue);
375 return 0;
376 }
377
378 } else {
379 _cleanup_free_ char *k = NULL;
380
381 p->type = SOCKET_SOCKET;
382 r = unit_full_printf(UNIT(s), rvalue, &k);
383 if (r < 0) {
384 log_syntax(unit, LOG_ERR, filename, line, r,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
385 return 0;
386 }
387
388 r = socket_address_parse_and_warn(&p->address, k);
389 if (r < 0) {
390 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue);
391 return 0;
392 }
393
394 if (streq(lvalue, "ListenStream"))
395 p->address.type = SOCK_STREAM;
396 else if (streq(lvalue, "ListenDatagram"))
397 p->address.type = SOCK_DGRAM;
398 else {
399 assert(streq(lvalue, "ListenSequentialPacket"));
400 p->address.type = SOCK_SEQPACKET;
401 }
402
403 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
404 log_syntax(unit, LOG_ERR, filename, line, 0, "Address family not supported, ignoring: %s", rvalue);
405 return 0;
406 }
407 }
408
409 p->fd = -1;
410 p->auxiliary_fds = NULL;
411 p->n_auxiliary_fds = 0;
412 p->socket = s;
413
414 if (s->ports) {
415 LIST_FIND_TAIL(port, s->ports, tail);
416 LIST_INSERT_AFTER(port, s->ports, tail, p);
417 } else
418 LIST_PREPEND(port, s->ports, p);
419 p = NULL;
420
421 return 0;
422 }
423
424 int config_parse_socket_protocol(const char *unit,
425 const char *filename,
426 unsigned line,
427 const char *section,
428 unsigned section_line,
429 const char *lvalue,
430 int ltype,
431 const char *rvalue,
432 void *data,
433 void *userdata) {
434 Socket *s;
435
436 assert(filename);
437 assert(lvalue);
438 assert(rvalue);
439 assert(data);
440
441 s = SOCKET(data);
442
443 if (streq(rvalue, "udplite"))
444 s->socket_protocol = IPPROTO_UDPLITE;
445 else if (streq(rvalue, "sctp"))
446 s->socket_protocol = IPPROTO_SCTP;
447 else {
448 log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue);
449 return 0;
450 }
451
452 return 0;
453 }
454
455 int config_parse_socket_bind(const char *unit,
456 const char *filename,
457 unsigned line,
458 const char *section,
459 unsigned section_line,
460 const char *lvalue,
461 int ltype,
462 const char *rvalue,
463 void *data,
464 void *userdata) {
465
466 Socket *s;
467 SocketAddressBindIPv6Only b;
468
469 assert(filename);
470 assert(lvalue);
471 assert(rvalue);
472 assert(data);
473
474 s = SOCKET(data);
475
476 b = socket_address_bind_ipv6_only_from_string(rvalue);
477 if (b < 0) {
478 int r;
479
480 r = parse_boolean(rvalue);
481 if (r < 0) {
482 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
483 return 0;
484 }
485
486 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
487 } else
488 s->bind_ipv6_only = b;
489
490 return 0;
491 }
492
493 int config_parse_exec_nice(const char *unit,
494 const char *filename,
495 unsigned line,
496 const char *section,
497 unsigned section_line,
498 const char *lvalue,
499 int ltype,
500 const char *rvalue,
501 void *data,
502 void *userdata) {
503
504 ExecContext *c = data;
505 int priority, r;
506
507 assert(filename);
508 assert(lvalue);
509 assert(rvalue);
510 assert(data);
511
512 r = safe_atoi(rvalue, &priority);
513 if (r < 0) {
514 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue);
515 return 0;
516 }
517
518 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
519 log_syntax(unit, LOG_ERR, filename, line, 0, "Nice priority out of range, ignoring: %s", rvalue);
520 return 0;
521 }
522
523 c->nice = priority;
524 c->nice_set = true;
525
526 return 0;
527 }
528
529 int config_parse_exec_oom_score_adjust(const char* unit,
530 const char *filename,
531 unsigned line,
532 const char *section,
533 unsigned section_line,
534 const char *lvalue,
535 int ltype,
536 const char *rvalue,
537 void *data,
538 void *userdata) {
539
540 ExecContext *c = data;
541 int oa, r;
542
543 assert(filename);
544 assert(lvalue);
545 assert(rvalue);
546 assert(data);
547
548 r = safe_atoi(rvalue, &oa);
549 if (r < 0) {
550 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
551 return 0;
552 }
553
554 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
555 log_syntax(unit, LOG_ERR, filename, line, 0, "OOM score adjust value out of range, ignoring: %s", rvalue);
556 return 0;
557 }
558
559 c->oom_score_adjust = oa;
560 c->oom_score_adjust_set = true;
561
562 return 0;
563 }
564
565 int config_parse_exec(
566 const char *unit,
567 const char *filename,
568 unsigned line,
569 const char *section,
570 unsigned section_line,
571 const char *lvalue,
572 int ltype,
573 const char *rvalue,
574 void *data,
575 void *userdata) {
576
577 ExecCommand **e = data;
578 const char *p;
579 bool semicolon;
580 int r;
581
582 assert(filename);
583 assert(lvalue);
584 assert(rvalue);
585 assert(e);
586
587 e += ltype;
588 rvalue += strspn(rvalue, WHITESPACE);
589
590 if (isempty(rvalue)) {
591 /* An empty assignment resets the list */
592 *e = exec_command_free_list(*e);
593 return 0;
594 }
595
596 p = rvalue;
597 do {
598 _cleanup_free_ char *path = NULL, *firstword = NULL;
599 bool separate_argv0 = false, ignore = false, privileged = false;
600 _cleanup_free_ ExecCommand *nce = NULL;
601 _cleanup_strv_free_ char **n = NULL;
602 size_t nlen = 0, nbufsize = 0;
603 char *f;
604 int i;
605
606 semicolon = false;
607
608 r = extract_first_word_and_warn(&p, &firstword, WHITESPACE, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
609 if (r <= 0)
610 return 0;
611
612 f = firstword;
613 for (i = 0; i < 3; i++) {
614 /* We accept an absolute path as first argument.
615 * If it's prefixed with - and the path doesn't exist,
616 * we ignore it instead of erroring out;
617 * if it's prefixed with @, we allow overriding of argv[0];
618 * and if it's prefixed with !, it will be run with full privileges */
619 if (*f == '-' && !ignore)
620 ignore = true;
621 else if (*f == '@' && !separate_argv0)
622 separate_argv0 = true;
623 else if (*f == '!' && !privileged)
624 privileged = true;
625 else
626 break;
627 f++;
628 }
629
630 if (isempty(f)) {
631 /* First word is either "-" or "@" with no command. */
632 log_syntax(unit, LOG_ERR, filename, line, 0, "Empty path in command line, ignoring: \"%s\"", rvalue);
633 return 0;
634 }
635 if (!string_is_safe(f)) {
636 log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path contains special characters, ignoring: %s", rvalue);
637 return 0;
638 }
639 if (!path_is_absolute(f)) {
640 log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path is not absolute, ignoring: %s", rvalue);
641 return 0;
642 }
643 if (endswith(f, "/")) {
644 log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory, ignoring: %s", rvalue);
645 return 0;
646 }
647
648 if (f == firstword) {
649 path = firstword;
650 firstword = NULL;
651 } else {
652 path = strdup(f);
653 if (!path)
654 return log_oom();
655 }
656
657 if (!separate_argv0) {
658 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
659 return log_oom();
660 f = strdup(path);
661 if (!f)
662 return log_oom();
663 n[nlen++] = f;
664 n[nlen] = NULL;
665 }
666
667 path_kill_slashes(path);
668
669 while (!isempty(p)) {
670 _cleanup_free_ char *word = NULL;
671
672 /* Check explicitly for an unquoted semicolon as
673 * command separator token. */
674 if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
675 p++;
676 p += strspn(p, WHITESPACE);
677 semicolon = true;
678 break;
679 }
680
681 /* Check for \; explicitly, to not confuse it with \\;
682 * or "\;" or "\\;" etc. extract_first_word would
683 * return the same for all of those. */
684 if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
685 p += 2;
686 p += strspn(p, WHITESPACE);
687 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
688 return log_oom();
689 f = strdup(";");
690 if (!f)
691 return log_oom();
692 n[nlen++] = f;
693 n[nlen] = NULL;
694 continue;
695 }
696
697 r = extract_first_word_and_warn(&p, &word, WHITESPACE, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
698 if (r == 0)
699 break;
700 else if (r < 0)
701 return 0;
702
703 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
704 return log_oom();
705 n[nlen++] = word;
706 n[nlen] = NULL;
707 word = NULL;
708 }
709
710 if (!n || !n[0]) {
711 log_syntax(unit, LOG_ERR, filename, line, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue);
712 return 0;
713 }
714
715 nce = new0(ExecCommand, 1);
716 if (!nce)
717 return log_oom();
718
719 nce->argv = n;
720 nce->path = path;
721 nce->ignore = ignore;
722 nce->privileged = privileged;
723
724 exec_command_append_list(e, nce);
725
726 /* Do not _cleanup_free_ these. */
727 n = NULL;
728 path = NULL;
729 nce = NULL;
730
731 rvalue = p;
732 } while (semicolon);
733
734 return 0;
735 }
736
737 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
738 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
739
740 int config_parse_socket_bindtodevice(
741 const char* unit,
742 const char *filename,
743 unsigned line,
744 const char *section,
745 unsigned section_line,
746 const char *lvalue,
747 int ltype,
748 const char *rvalue,
749 void *data,
750 void *userdata) {
751
752 Socket *s = data;
753 char *n;
754
755 assert(filename);
756 assert(lvalue);
757 assert(rvalue);
758 assert(data);
759
760 if (rvalue[0] && !streq(rvalue, "*")) {
761 if (!ifname_valid(rvalue)) {
762 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is invalid, ignoring: %s", rvalue);
763 return 0;
764 }
765
766 n = strdup(rvalue);
767 if (!n)
768 return log_oom();
769 } else
770 n = NULL;
771
772 free(s->bind_to_device);
773 s->bind_to_device = n;
774
775 return 0;
776 }
777
778 DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
779 DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
780
781 int config_parse_exec_io_class(const char *unit,
782 const char *filename,
783 unsigned line,
784 const char *section,
785 unsigned section_line,
786 const char *lvalue,
787 int ltype,
788 const char *rvalue,
789 void *data,
790 void *userdata) {
791
792 ExecContext *c = data;
793 int x;
794
795 assert(filename);
796 assert(lvalue);
797 assert(rvalue);
798 assert(data);
799
800 x = ioprio_class_from_string(rvalue);
801 if (x < 0) {
802 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue);
803 return 0;
804 }
805
806 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
807 c->ioprio_set = true;
808
809 return 0;
810 }
811
812 int config_parse_exec_io_priority(const char *unit,
813 const char *filename,
814 unsigned line,
815 const char *section,
816 unsigned section_line,
817 const char *lvalue,
818 int ltype,
819 const char *rvalue,
820 void *data,
821 void *userdata) {
822
823 ExecContext *c = data;
824 int i, r;
825
826 assert(filename);
827 assert(lvalue);
828 assert(rvalue);
829 assert(data);
830
831 r = safe_atoi(rvalue, &i);
832 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
833 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue);
834 return 0;
835 }
836
837 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
838 c->ioprio_set = true;
839
840 return 0;
841 }
842
843 int config_parse_exec_cpu_sched_policy(const char *unit,
844 const char *filename,
845 unsigned line,
846 const char *section,
847 unsigned section_line,
848 const char *lvalue,
849 int ltype,
850 const char *rvalue,
851 void *data,
852 void *userdata) {
853
854
855 ExecContext *c = data;
856 int x;
857
858 assert(filename);
859 assert(lvalue);
860 assert(rvalue);
861 assert(data);
862
863 x = sched_policy_from_string(rvalue);
864 if (x < 0) {
865 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
866 return 0;
867 }
868
869 c->cpu_sched_policy = x;
870 /* Moving to or from real-time policy? We need to adjust the priority */
871 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
872 c->cpu_sched_set = true;
873
874 return 0;
875 }
876
877 int config_parse_exec_cpu_sched_prio(const char *unit,
878 const char *filename,
879 unsigned line,
880 const char *section,
881 unsigned section_line,
882 const char *lvalue,
883 int ltype,
884 const char *rvalue,
885 void *data,
886 void *userdata) {
887
888 ExecContext *c = data;
889 int i, min, max, r;
890
891 assert(filename);
892 assert(lvalue);
893 assert(rvalue);
894 assert(data);
895
896 r = safe_atoi(rvalue, &i);
897 if (r < 0) {
898 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
899 return 0;
900 }
901
902 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
903 min = sched_get_priority_min(c->cpu_sched_policy);
904 max = sched_get_priority_max(c->cpu_sched_policy);
905
906 if (i < min || i > max) {
907 log_syntax(unit, LOG_ERR, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue);
908 return 0;
909 }
910
911 c->cpu_sched_priority = i;
912 c->cpu_sched_set = true;
913
914 return 0;
915 }
916
917 int config_parse_exec_cpu_affinity(const char *unit,
918 const char *filename,
919 unsigned line,
920 const char *section,
921 unsigned section_line,
922 const char *lvalue,
923 int ltype,
924 const char *rvalue,
925 void *data,
926 void *userdata) {
927
928 ExecContext *c = data;
929 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
930 int ncpus;
931
932 assert(filename);
933 assert(lvalue);
934 assert(rvalue);
935 assert(data);
936
937 ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
938 if (ncpus < 0)
939 return ncpus;
940
941 if (c->cpuset)
942 CPU_FREE(c->cpuset);
943
944 if (ncpus == 0)
945 /* An empty assignment resets the CPU list */
946 c->cpuset = NULL;
947 else {
948 c->cpuset = cpuset;
949 cpuset = NULL;
950 }
951 c->cpuset_ncpus = ncpus;
952
953 return 0;
954 }
955
956 int config_parse_exec_secure_bits(const char *unit,
957 const char *filename,
958 unsigned line,
959 const char *section,
960 unsigned section_line,
961 const char *lvalue,
962 int ltype,
963 const char *rvalue,
964 void *data,
965 void *userdata) {
966
967 ExecContext *c = data;
968 size_t l;
969 const char *word, *state;
970
971 assert(filename);
972 assert(lvalue);
973 assert(rvalue);
974 assert(data);
975
976 if (isempty(rvalue)) {
977 /* An empty assignment resets the field */
978 c->secure_bits = 0;
979 return 0;
980 }
981
982 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
983 if (first_word(word, "keep-caps"))
984 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
985 else if (first_word(word, "keep-caps-locked"))
986 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
987 else if (first_word(word, "no-setuid-fixup"))
988 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
989 else if (first_word(word, "no-setuid-fixup-locked"))
990 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
991 else if (first_word(word, "noroot"))
992 c->secure_bits |= 1<<SECURE_NOROOT;
993 else if (first_word(word, "noroot-locked"))
994 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
995 else {
996 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse secure bits, ignoring: %s", rvalue);
997 return 0;
998 }
999 }
1000 if (!isempty(state))
1001 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, garbage at the end, ignoring.");
1002
1003 return 0;
1004 }
1005
1006 int config_parse_capability_set(
1007 const char *unit,
1008 const char *filename,
1009 unsigned line,
1010 const char *section,
1011 unsigned section_line,
1012 const char *lvalue,
1013 int ltype,
1014 const char *rvalue,
1015 void *data,
1016 void *userdata) {
1017
1018 uint64_t *capability_set = data;
1019 uint64_t sum = 0, initial = 0;
1020 bool invert = false;
1021 const char *p;
1022
1023 assert(filename);
1024 assert(lvalue);
1025 assert(rvalue);
1026 assert(data);
1027
1028 if (rvalue[0] == '~') {
1029 invert = true;
1030 rvalue++;
1031 }
1032
1033 if (strcmp(lvalue, "CapabilityBoundingSet") == 0)
1034 initial = CAP_ALL; /* initialized to all bits on */
1035 /* else "AmbientCapabilities" initialized to all bits off */
1036
1037 p = rvalue;
1038 for (;;) {
1039 _cleanup_free_ char *word = NULL;
1040 int cap, r;
1041
1042 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
1043 if (r == 0)
1044 break;
1045 if (r == -ENOMEM)
1046 return log_oom();
1047 if (r < 0) {
1048 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse word, ignoring: %s", rvalue);
1049 break;
1050 }
1051
1052 cap = capability_from_name(word);
1053 if (cap < 0) {
1054 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word);
1055 continue;
1056 }
1057
1058 sum |= ((uint64_t) UINT64_C(1)) << (uint64_t) cap;
1059 }
1060
1061 sum = invert ? ~sum : sum;
1062
1063 if (sum == 0 || *capability_set == initial)
1064 /* "" or uninitialized data -> replace */
1065 *capability_set = sum;
1066 else
1067 /* previous data -> merge */
1068 *capability_set |= sum;
1069
1070 return 0;
1071 }
1072
1073 int config_parse_limit(
1074 const char *unit,
1075 const char *filename,
1076 unsigned line,
1077 const char *section,
1078 unsigned section_line,
1079 const char *lvalue,
1080 int ltype,
1081 const char *rvalue,
1082 void *data,
1083 void *userdata) {
1084
1085 struct rlimit **rl = data, d = {};
1086 int r;
1087
1088 assert(filename);
1089 assert(lvalue);
1090 assert(rvalue);
1091 assert(data);
1092
1093 r = rlimit_parse(ltype, rvalue, &d);
1094 if (r == -EILSEQ) {
1095 log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue);
1096 return 0;
1097 }
1098 if (r < 0) {
1099 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
1100 return 0;
1101 }
1102
1103 if (rl[ltype])
1104 *rl[ltype] = d;
1105 else {
1106 rl[ltype] = newdup(struct rlimit, &d, 1);
1107 if (!rl[ltype])
1108 return log_oom();
1109 }
1110
1111 return 0;
1112 }
1113
1114 #ifdef HAVE_SYSV_COMPAT
1115 int config_parse_sysv_priority(const char *unit,
1116 const char *filename,
1117 unsigned line,
1118 const char *section,
1119 unsigned section_line,
1120 const char *lvalue,
1121 int ltype,
1122 const char *rvalue,
1123 void *data,
1124 void *userdata) {
1125
1126 int *priority = data;
1127 int i, r;
1128
1129 assert(filename);
1130 assert(lvalue);
1131 assert(rvalue);
1132 assert(data);
1133
1134 r = safe_atoi(rvalue, &i);
1135 if (r < 0 || i < 0) {
1136 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue);
1137 return 0;
1138 }
1139
1140 *priority = (int) i;
1141 return 0;
1142 }
1143 #endif
1144
1145 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
1146 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1147
1148 int config_parse_exec_mount_flags(const char *unit,
1149 const char *filename,
1150 unsigned line,
1151 const char *section,
1152 unsigned section_line,
1153 const char *lvalue,
1154 int ltype,
1155 const char *rvalue,
1156 void *data,
1157 void *userdata) {
1158
1159
1160 unsigned long flags = 0;
1161 ExecContext *c = data;
1162
1163 assert(filename);
1164 assert(lvalue);
1165 assert(rvalue);
1166 assert(data);
1167
1168 if (streq(rvalue, "shared"))
1169 flags = MS_SHARED;
1170 else if (streq(rvalue, "slave"))
1171 flags = MS_SLAVE;
1172 else if (streq(rvalue, "private"))
1173 flags = MS_PRIVATE;
1174 else {
1175 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring.", rvalue);
1176 return 0;
1177 }
1178
1179 c->mount_flags = flags;
1180
1181 return 0;
1182 }
1183
1184 int config_parse_exec_selinux_context(
1185 const char *unit,
1186 const char *filename,
1187 unsigned line,
1188 const char *section,
1189 unsigned section_line,
1190 const char *lvalue,
1191 int ltype,
1192 const char *rvalue,
1193 void *data,
1194 void *userdata) {
1195
1196 ExecContext *c = data;
1197 Unit *u = userdata;
1198 bool ignore;
1199 char *k;
1200 int r;
1201
1202 assert(filename);
1203 assert(lvalue);
1204 assert(rvalue);
1205 assert(data);
1206
1207 if (isempty(rvalue)) {
1208 c->selinux_context = mfree(c->selinux_context);
1209 c->selinux_context_ignore = false;
1210 return 0;
1211 }
1212
1213 if (rvalue[0] == '-') {
1214 ignore = true;
1215 rvalue++;
1216 } else
1217 ignore = false;
1218
1219 r = unit_name_printf(u, rvalue, &k);
1220 if (r < 0) {
1221 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1222 return 0;
1223 }
1224
1225 free(c->selinux_context);
1226 c->selinux_context = k;
1227 c->selinux_context_ignore = ignore;
1228
1229 return 0;
1230 }
1231
1232 int config_parse_exec_apparmor_profile(
1233 const char *unit,
1234 const char *filename,
1235 unsigned line,
1236 const char *section,
1237 unsigned section_line,
1238 const char *lvalue,
1239 int ltype,
1240 const char *rvalue,
1241 void *data,
1242 void *userdata) {
1243
1244 ExecContext *c = data;
1245 Unit *u = userdata;
1246 bool ignore;
1247 char *k;
1248 int r;
1249
1250 assert(filename);
1251 assert(lvalue);
1252 assert(rvalue);
1253 assert(data);
1254
1255 if (isempty(rvalue)) {
1256 c->apparmor_profile = mfree(c->apparmor_profile);
1257 c->apparmor_profile_ignore = false;
1258 return 0;
1259 }
1260
1261 if (rvalue[0] == '-') {
1262 ignore = true;
1263 rvalue++;
1264 } else
1265 ignore = false;
1266
1267 r = unit_name_printf(u, rvalue, &k);
1268 if (r < 0) {
1269 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1270 return 0;
1271 }
1272
1273 free(c->apparmor_profile);
1274 c->apparmor_profile = k;
1275 c->apparmor_profile_ignore = ignore;
1276
1277 return 0;
1278 }
1279
1280 int config_parse_exec_smack_process_label(
1281 const char *unit,
1282 const char *filename,
1283 unsigned line,
1284 const char *section,
1285 unsigned section_line,
1286 const char *lvalue,
1287 int ltype,
1288 const char *rvalue,
1289 void *data,
1290 void *userdata) {
1291
1292 ExecContext *c = data;
1293 Unit *u = userdata;
1294 bool ignore;
1295 char *k;
1296 int r;
1297
1298 assert(filename);
1299 assert(lvalue);
1300 assert(rvalue);
1301 assert(data);
1302
1303 if (isempty(rvalue)) {
1304 c->smack_process_label = mfree(c->smack_process_label);
1305 c->smack_process_label_ignore = false;
1306 return 0;
1307 }
1308
1309 if (rvalue[0] == '-') {
1310 ignore = true;
1311 rvalue++;
1312 } else
1313 ignore = false;
1314
1315 r = unit_name_printf(u, rvalue, &k);
1316 if (r < 0) {
1317 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1318 return 0;
1319 }
1320
1321 free(c->smack_process_label);
1322 c->smack_process_label = k;
1323 c->smack_process_label_ignore = ignore;
1324
1325 return 0;
1326 }
1327
1328 int config_parse_timer(const char *unit,
1329 const char *filename,
1330 unsigned line,
1331 const char *section,
1332 unsigned section_line,
1333 const char *lvalue,
1334 int ltype,
1335 const char *rvalue,
1336 void *data,
1337 void *userdata) {
1338
1339 Timer *t = data;
1340 usec_t u = 0;
1341 TimerValue *v;
1342 TimerBase b;
1343 CalendarSpec *c = NULL;
1344
1345 assert(filename);
1346 assert(lvalue);
1347 assert(rvalue);
1348 assert(data);
1349
1350 if (isempty(rvalue)) {
1351 /* Empty assignment resets list */
1352 timer_free_values(t);
1353 return 0;
1354 }
1355
1356 b = timer_base_from_string(lvalue);
1357 if (b < 0) {
1358 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer base, ignoring: %s", lvalue);
1359 return 0;
1360 }
1361
1362 if (b == TIMER_CALENDAR) {
1363 if (calendar_spec_from_string(rvalue, &c) < 0) {
1364 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", rvalue);
1365 return 0;
1366 }
1367 } else {
1368 if (parse_sec(rvalue, &u) < 0) {
1369 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", rvalue);
1370 return 0;
1371 }
1372 }
1373
1374 v = new0(TimerValue, 1);
1375 if (!v) {
1376 calendar_spec_free(c);
1377 return log_oom();
1378 }
1379
1380 v->base = b;
1381 v->value = u;
1382 v->calendar_spec = c;
1383
1384 LIST_PREPEND(value, t->values, v);
1385
1386 return 0;
1387 }
1388
1389 int config_parse_trigger_unit(
1390 const char *unit,
1391 const char *filename,
1392 unsigned line,
1393 const char *section,
1394 unsigned section_line,
1395 const char *lvalue,
1396 int ltype,
1397 const char *rvalue,
1398 void *data,
1399 void *userdata) {
1400
1401 _cleanup_free_ char *p = NULL;
1402 Unit *u = data;
1403 UnitType type;
1404 int r;
1405
1406 assert(filename);
1407 assert(lvalue);
1408 assert(rvalue);
1409 assert(data);
1410
1411 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1412 log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
1413 return 0;
1414 }
1415
1416 r = unit_name_printf(u, rvalue, &p);
1417 if (r < 0) {
1418 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1419 return 0;
1420 }
1421
1422 type = unit_name_to_type(p);
1423 if (type < 0) {
1424 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue);
1425 return 0;
1426 }
1427
1428 if (type == u->type) {
1429 log_syntax(unit, LOG_ERR, filename, line, 0, "Trigger cannot be of same type, ignoring: %s", rvalue);
1430 return 0;
1431 }
1432
1433 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
1434 if (r < 0) {
1435 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
1436 return 0;
1437 }
1438
1439 return 0;
1440 }
1441
1442 int config_parse_path_spec(const char *unit,
1443 const char *filename,
1444 unsigned line,
1445 const char *section,
1446 unsigned section_line,
1447 const char *lvalue,
1448 int ltype,
1449 const char *rvalue,
1450 void *data,
1451 void *userdata) {
1452
1453 Path *p = data;
1454 PathSpec *s;
1455 PathType b;
1456 _cleanup_free_ char *k = NULL;
1457 int r;
1458
1459 assert(filename);
1460 assert(lvalue);
1461 assert(rvalue);
1462 assert(data);
1463
1464 if (isempty(rvalue)) {
1465 /* Empty assignment clears list */
1466 path_free_specs(p);
1467 return 0;
1468 }
1469
1470 b = path_type_from_string(lvalue);
1471 if (b < 0) {
1472 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue);
1473 return 0;
1474 }
1475
1476 r = unit_full_printf(UNIT(p), rvalue, &k);
1477 if (r < 0) {
1478 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
1479 return 0;
1480 }
1481
1482 if (!path_is_absolute(k)) {
1483 log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k);
1484 return 0;
1485 }
1486
1487 s = new0(PathSpec, 1);
1488 if (!s)
1489 return log_oom();
1490
1491 s->unit = UNIT(p);
1492 s->path = path_kill_slashes(k);
1493 k = NULL;
1494 s->type = b;
1495 s->inotify_fd = -1;
1496
1497 LIST_PREPEND(spec, p->specs, s);
1498
1499 return 0;
1500 }
1501
1502 int config_parse_socket_service(
1503 const char *unit,
1504 const char *filename,
1505 unsigned line,
1506 const char *section,
1507 unsigned section_line,
1508 const char *lvalue,
1509 int ltype,
1510 const char *rvalue,
1511 void *data,
1512 void *userdata) {
1513
1514 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1515 _cleanup_free_ char *p = NULL;
1516 Socket *s = data;
1517 Unit *x;
1518 int r;
1519
1520 assert(filename);
1521 assert(lvalue);
1522 assert(rvalue);
1523 assert(data);
1524
1525 r = unit_name_printf(UNIT(s), rvalue, &p);
1526 if (r < 0) {
1527 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1528 return 0;
1529 }
1530
1531 if (!endswith(p, ".service")) {
1532 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
1533 return 0;
1534 }
1535
1536 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1537 if (r < 0) {
1538 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1539 return 0;
1540 }
1541
1542 unit_ref_set(&s->service, x);
1543
1544 return 0;
1545 }
1546
1547 int config_parse_fdname(
1548 const char *unit,
1549 const char *filename,
1550 unsigned line,
1551 const char *section,
1552 unsigned section_line,
1553 const char *lvalue,
1554 int ltype,
1555 const char *rvalue,
1556 void *data,
1557 void *userdata) {
1558
1559 _cleanup_free_ char *p = NULL;
1560 Socket *s = data;
1561 int r;
1562
1563 assert(filename);
1564 assert(lvalue);
1565 assert(rvalue);
1566 assert(data);
1567
1568 if (isempty(rvalue)) {
1569 s->fdname = mfree(s->fdname);
1570 return 0;
1571 }
1572
1573 r = unit_name_printf(UNIT(s), rvalue, &p);
1574 if (r < 0) {
1575 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1576 return 0;
1577 }
1578
1579 if (!fdname_is_valid(p)) {
1580 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
1581 return 0;
1582 }
1583
1584 free(s->fdname);
1585 s->fdname = p;
1586 p = NULL;
1587
1588 return 0;
1589 }
1590
1591 int config_parse_service_sockets(
1592 const char *unit,
1593 const char *filename,
1594 unsigned line,
1595 const char *section,
1596 unsigned section_line,
1597 const char *lvalue,
1598 int ltype,
1599 const char *rvalue,
1600 void *data,
1601 void *userdata) {
1602
1603 Service *s = data;
1604 const char *p;
1605 int r;
1606
1607 assert(filename);
1608 assert(lvalue);
1609 assert(rvalue);
1610 assert(data);
1611
1612 p = rvalue;
1613 for (;;) {
1614 _cleanup_free_ char *word = NULL, *k = NULL;
1615
1616 r = extract_first_word(&p, &word, NULL, 0);
1617 if (r == 0)
1618 break;
1619 if (r == -ENOMEM)
1620 return log_oom();
1621 if (r < 0) {
1622 log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
1623 break;
1624 }
1625
1626 r = unit_name_printf(UNIT(s), word, &k);
1627 if (r < 0) {
1628 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1629 continue;
1630 }
1631
1632 if (!endswith(k, ".socket")) {
1633 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
1634 continue;
1635 }
1636
1637 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
1638 if (r < 0)
1639 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
1640
1641 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
1642 if (r < 0)
1643 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
1644 }
1645
1646 return 0;
1647 }
1648
1649 int config_parse_bus_name(
1650 const char *unit,
1651 const char *filename,
1652 unsigned line,
1653 const char *section,
1654 unsigned section_line,
1655 const char *lvalue,
1656 int ltype,
1657 const char *rvalue,
1658 void *data,
1659 void *userdata) {
1660
1661 _cleanup_free_ char *k = NULL;
1662 Unit *u = userdata;
1663 int r;
1664
1665 assert(filename);
1666 assert(lvalue);
1667 assert(rvalue);
1668 assert(u);
1669
1670 r = unit_full_printf(u, rvalue, &k);
1671 if (r < 0) {
1672 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
1673 return 0;
1674 }
1675
1676 if (!service_name_is_valid(k)) {
1677 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k);
1678 return 0;
1679 }
1680
1681 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
1682 }
1683
1684 int config_parse_service_timeout(
1685 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 Service *s = userdata;
1697 usec_t usec;
1698 int r;
1699
1700 assert(filename);
1701 assert(lvalue);
1702 assert(rvalue);
1703 assert(s);
1704
1705 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1706
1707 r = parse_sec(rvalue, &usec);
1708 if (r < 0) {
1709 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
1710 return 0;
1711 }
1712
1713 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1714 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1715 * all other timeouts. */
1716 if (usec <= 0)
1717 usec = USEC_INFINITY;
1718
1719 if (!streq(lvalue, "TimeoutStopSec")) {
1720 s->start_timeout_defined = true;
1721 s->timeout_start_usec = usec;
1722 }
1723
1724 if (!streq(lvalue, "TimeoutStartSec"))
1725 s->timeout_stop_usec = usec;
1726
1727 return 0;
1728 }
1729
1730 int config_parse_sec_fix_0(
1731 const char *unit,
1732 const char *filename,
1733 unsigned line,
1734 const char *section,
1735 unsigned section_line,
1736 const char *lvalue,
1737 int ltype,
1738 const char *rvalue,
1739 void *data,
1740 void *userdata) {
1741
1742 usec_t *usec = data;
1743 int r;
1744
1745 assert(filename);
1746 assert(lvalue);
1747 assert(rvalue);
1748 assert(usec);
1749
1750 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1751 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1752 * timeout. */
1753
1754 r = parse_sec(rvalue, usec);
1755 if (r < 0) {
1756 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
1757 return 0;
1758 }
1759
1760 if (*usec <= 0)
1761 *usec = USEC_INFINITY;
1762
1763 return 0;
1764 }
1765
1766 int config_parse_busname_service(
1767 const char *unit,
1768 const char *filename,
1769 unsigned line,
1770 const char *section,
1771 unsigned section_line,
1772 const char *lvalue,
1773 int ltype,
1774 const char *rvalue,
1775 void *data,
1776 void *userdata) {
1777
1778 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1779 BusName *n = data;
1780 int r;
1781 Unit *x;
1782 _cleanup_free_ char *p = NULL;
1783
1784 assert(filename);
1785 assert(lvalue);
1786 assert(rvalue);
1787 assert(data);
1788
1789 r = unit_name_printf(UNIT(n), rvalue, &p);
1790 if (r < 0) {
1791 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1792 return 0;
1793 }
1794
1795 if (!endswith(p, ".service")) {
1796 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
1797 return 0;
1798 }
1799
1800 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1801 if (r < 0) {
1802 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1803 return 0;
1804 }
1805
1806 unit_ref_set(&n->service, x);
1807
1808 return 0;
1809 }
1810
1811 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world, bus_policy_access, BusPolicyAccess, "Failed to parse bus name policy access");
1812
1813 int config_parse_bus_policy(
1814 const char *unit,
1815 const char *filename,
1816 unsigned line,
1817 const char *section,
1818 unsigned section_line,
1819 const char *lvalue,
1820 int ltype,
1821 const char *rvalue,
1822 void *data,
1823 void *userdata) {
1824
1825 _cleanup_free_ BusNamePolicy *p = NULL;
1826 _cleanup_free_ char *id_str = NULL;
1827 BusName *busname = data;
1828 char *access_str;
1829
1830 assert(filename);
1831 assert(lvalue);
1832 assert(rvalue);
1833 assert(data);
1834
1835 p = new0(BusNamePolicy, 1);
1836 if (!p)
1837 return log_oom();
1838
1839 if (streq(lvalue, "AllowUser"))
1840 p->type = BUSNAME_POLICY_TYPE_USER;
1841 else if (streq(lvalue, "AllowGroup"))
1842 p->type = BUSNAME_POLICY_TYPE_GROUP;
1843 else
1844 assert_not_reached("Unknown lvalue");
1845
1846 id_str = strdup(rvalue);
1847 if (!id_str)
1848 return log_oom();
1849
1850 access_str = strpbrk(id_str, WHITESPACE);
1851 if (!access_str) {
1852 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy value '%s'", rvalue);
1853 return 0;
1854 }
1855
1856 *access_str = '\0';
1857 access_str++;
1858 access_str += strspn(access_str, WHITESPACE);
1859
1860 p->access = bus_policy_access_from_string(access_str);
1861 if (p->access < 0) {
1862 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy access type '%s'", access_str);
1863 return 0;
1864 }
1865
1866 p->name = id_str;
1867 id_str = NULL;
1868
1869 LIST_PREPEND(policy, busname->policy, p);
1870 p = NULL;
1871
1872 return 0;
1873 }
1874
1875 int config_parse_working_directory(
1876 const char *unit,
1877 const char *filename,
1878 unsigned line,
1879 const char *section,
1880 unsigned section_line,
1881 const char *lvalue,
1882 int ltype,
1883 const char *rvalue,
1884 void *data,
1885 void *userdata) {
1886
1887 ExecContext *c = data;
1888 Unit *u = userdata;
1889 bool missing_ok;
1890 int r;
1891
1892 assert(filename);
1893 assert(lvalue);
1894 assert(rvalue);
1895 assert(c);
1896 assert(u);
1897
1898 if (rvalue[0] == '-') {
1899 missing_ok = true;
1900 rvalue++;
1901 } else
1902 missing_ok = false;
1903
1904 if (streq(rvalue, "~")) {
1905 c->working_directory_home = true;
1906 c->working_directory = mfree(c->working_directory);
1907 } else {
1908 _cleanup_free_ char *k = NULL;
1909
1910 r = unit_full_printf(u, rvalue, &k);
1911 if (r < 0) {
1912 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue);
1913 return 0;
1914 }
1915
1916 path_kill_slashes(k);
1917
1918 if (!utf8_is_valid(k)) {
1919 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
1920 return 0;
1921 }
1922
1923 if (!path_is_absolute(k)) {
1924 log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue);
1925 return 0;
1926 }
1927
1928 free(c->working_directory);
1929 c->working_directory = k;
1930 k = NULL;
1931
1932 c->working_directory_home = false;
1933 }
1934
1935 c->working_directory_missing_ok = missing_ok;
1936 return 0;
1937 }
1938
1939 int config_parse_unit_env_file(const char *unit,
1940 const char *filename,
1941 unsigned line,
1942 const char *section,
1943 unsigned section_line,
1944 const char *lvalue,
1945 int ltype,
1946 const char *rvalue,
1947 void *data,
1948 void *userdata) {
1949
1950 char ***env = data;
1951 Unit *u = userdata;
1952 _cleanup_free_ char *n = NULL;
1953 int r;
1954
1955 assert(filename);
1956 assert(lvalue);
1957 assert(rvalue);
1958 assert(data);
1959
1960 if (isempty(rvalue)) {
1961 /* Empty assignment frees the list */
1962 *env = strv_free(*env);
1963 return 0;
1964 }
1965
1966 r = unit_full_printf(u, rvalue, &n);
1967 if (r < 0) {
1968 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1969 return 0;
1970 }
1971
1972 if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) {
1973 log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n);
1974 return 0;
1975 }
1976
1977 r = strv_extend(env, n);
1978 if (r < 0)
1979 return log_oom();
1980
1981 return 0;
1982 }
1983
1984 int config_parse_environ(const char *unit,
1985 const char *filename,
1986 unsigned line,
1987 const char *section,
1988 unsigned section_line,
1989 const char *lvalue,
1990 int ltype,
1991 const char *rvalue,
1992 void *data,
1993 void *userdata) {
1994
1995 Unit *u = userdata;
1996 char*** env = data;
1997 const char *word, *state;
1998 size_t l;
1999 _cleanup_free_ char *k = NULL;
2000 int r;
2001
2002 assert(filename);
2003 assert(lvalue);
2004 assert(rvalue);
2005 assert(data);
2006
2007 if (isempty(rvalue)) {
2008 /* Empty assignment resets the list */
2009 *env = strv_free(*env);
2010 return 0;
2011 }
2012
2013 if (u) {
2014 r = unit_full_printf(u, rvalue, &k);
2015 if (r < 0) {
2016 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2017 return 0;
2018 }
2019 }
2020
2021 if (!k) {
2022 k = strdup(rvalue);
2023 if (!k)
2024 return log_oom();
2025 }
2026
2027 FOREACH_WORD_QUOTED(word, l, k, state) {
2028 _cleanup_free_ char *n = NULL;
2029 char **x;
2030
2031 r = cunescape_length(word, l, 0, &n);
2032 if (r < 0) {
2033 log_syntax(unit, LOG_ERR, filename, line, r, "Couldn't unescape assignment, ignoring: %s", rvalue);
2034 continue;
2035 }
2036
2037 if (!env_assignment_is_valid(n)) {
2038 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid environment assignment, ignoring: %s", rvalue);
2039 continue;
2040 }
2041
2042 x = strv_env_set(*env, n);
2043 if (!x)
2044 return log_oom();
2045
2046 strv_free(*env);
2047 *env = x;
2048 }
2049 if (!isempty(state))
2050 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
2051
2052 return 0;
2053 }
2054
2055 int config_parse_pass_environ(const char *unit,
2056 const char *filename,
2057 unsigned line,
2058 const char *section,
2059 unsigned section_line,
2060 const char *lvalue,
2061 int ltype,
2062 const char *rvalue,
2063 void *data,
2064 void *userdata) {
2065
2066 const char *whole_rvalue = rvalue;
2067 char*** passenv = data;
2068 _cleanup_strv_free_ char **n = NULL;
2069 size_t nlen = 0, nbufsize = 0;
2070 int r;
2071
2072 assert(filename);
2073 assert(lvalue);
2074 assert(rvalue);
2075 assert(data);
2076
2077 if (isempty(rvalue)) {
2078 /* Empty assignment resets the list */
2079 *passenv = strv_free(*passenv);
2080 return 0;
2081 }
2082
2083 for (;;) {
2084 _cleanup_free_ char *word = NULL;
2085
2086 r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
2087 if (r == 0)
2088 break;
2089 if (r == -ENOMEM)
2090 return log_oom();
2091 if (r < 0) {
2092 log_syntax(unit, LOG_ERR, filename, line, r,
2093 "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
2094 break;
2095 }
2096
2097 if (!env_name_is_valid(word)) {
2098 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2099 "Invalid environment name for %s, ignoring: %s", lvalue, word);
2100 continue;
2101 }
2102
2103 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2104 return log_oom();
2105 n[nlen++] = word;
2106 n[nlen] = NULL;
2107 word = NULL;
2108 }
2109
2110 if (n) {
2111 r = strv_extend_strv(passenv, n, true);
2112 if (r < 0)
2113 return r;
2114 }
2115
2116 return 0;
2117 }
2118
2119 int config_parse_ip_tos(const char *unit,
2120 const char *filename,
2121 unsigned line,
2122 const char *section,
2123 unsigned section_line,
2124 const char *lvalue,
2125 int ltype,
2126 const char *rvalue,
2127 void *data,
2128 void *userdata) {
2129
2130 int *ip_tos = data, x;
2131
2132 assert(filename);
2133 assert(lvalue);
2134 assert(rvalue);
2135 assert(data);
2136
2137 x = ip_tos_from_string(rvalue);
2138 if (x < 0) {
2139 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue);
2140 return 0;
2141 }
2142
2143 *ip_tos = x;
2144 return 0;
2145 }
2146
2147 int config_parse_unit_condition_path(
2148 const char *unit,
2149 const char *filename,
2150 unsigned line,
2151 const char *section,
2152 unsigned section_line,
2153 const char *lvalue,
2154 int ltype,
2155 const char *rvalue,
2156 void *data,
2157 void *userdata) {
2158
2159 _cleanup_free_ char *p = NULL;
2160 Condition **list = data, *c;
2161 ConditionType t = ltype;
2162 bool trigger, negate;
2163 Unit *u = userdata;
2164 int r;
2165
2166 assert(filename);
2167 assert(lvalue);
2168 assert(rvalue);
2169 assert(data);
2170
2171 if (isempty(rvalue)) {
2172 /* Empty assignment resets the list */
2173 *list = condition_free_list(*list);
2174 return 0;
2175 }
2176
2177 trigger = rvalue[0] == '|';
2178 if (trigger)
2179 rvalue++;
2180
2181 negate = rvalue[0] == '!';
2182 if (negate)
2183 rvalue++;
2184
2185 r = unit_full_printf(u, rvalue, &p);
2186 if (r < 0) {
2187 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2188 return 0;
2189 }
2190
2191 if (!path_is_absolute(p)) {
2192 log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p);
2193 return 0;
2194 }
2195
2196 c = condition_new(t, p, trigger, negate);
2197 if (!c)
2198 return log_oom();
2199
2200 LIST_PREPEND(conditions, *list, c);
2201 return 0;
2202 }
2203
2204 int config_parse_unit_condition_string(
2205 const char *unit,
2206 const char *filename,
2207 unsigned line,
2208 const char *section,
2209 unsigned section_line,
2210 const char *lvalue,
2211 int ltype,
2212 const char *rvalue,
2213 void *data,
2214 void *userdata) {
2215
2216 _cleanup_free_ char *s = NULL;
2217 Condition **list = data, *c;
2218 ConditionType t = ltype;
2219 bool trigger, negate;
2220 Unit *u = userdata;
2221 int r;
2222
2223 assert(filename);
2224 assert(lvalue);
2225 assert(rvalue);
2226 assert(data);
2227
2228 if (isempty(rvalue)) {
2229 /* Empty assignment resets the list */
2230 *list = condition_free_list(*list);
2231 return 0;
2232 }
2233
2234 trigger = rvalue[0] == '|';
2235 if (trigger)
2236 rvalue++;
2237
2238 negate = rvalue[0] == '!';
2239 if (negate)
2240 rvalue++;
2241
2242 r = unit_full_printf(u, rvalue, &s);
2243 if (r < 0) {
2244 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2245 return 0;
2246 }
2247
2248 c = condition_new(t, s, trigger, negate);
2249 if (!c)
2250 return log_oom();
2251
2252 LIST_PREPEND(conditions, *list, c);
2253 return 0;
2254 }
2255
2256 int config_parse_unit_condition_null(
2257 const char *unit,
2258 const char *filename,
2259 unsigned line,
2260 const char *section,
2261 unsigned section_line,
2262 const char *lvalue,
2263 int ltype,
2264 const char *rvalue,
2265 void *data,
2266 void *userdata) {
2267
2268 Condition **list = data, *c;
2269 bool trigger, negate;
2270 int b;
2271
2272 assert(filename);
2273 assert(lvalue);
2274 assert(rvalue);
2275 assert(data);
2276
2277 if (isempty(rvalue)) {
2278 /* Empty assignment resets the list */
2279 *list = condition_free_list(*list);
2280 return 0;
2281 }
2282
2283 trigger = rvalue[0] == '|';
2284 if (trigger)
2285 rvalue++;
2286
2287 negate = rvalue[0] == '!';
2288 if (negate)
2289 rvalue++;
2290
2291 b = parse_boolean(rvalue);
2292 if (b < 0) {
2293 log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
2294 return 0;
2295 }
2296
2297 if (!b)
2298 negate = !negate;
2299
2300 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2301 if (!c)
2302 return log_oom();
2303
2304 LIST_PREPEND(conditions, *list, c);
2305 return 0;
2306 }
2307
2308 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
2309 DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action, failure_action, FailureAction, "Failed to parse failure action specifier");
2310
2311 int config_parse_unit_requires_mounts_for(
2312 const char *unit,
2313 const char *filename,
2314 unsigned line,
2315 const char *section,
2316 unsigned section_line,
2317 const char *lvalue,
2318 int ltype,
2319 const char *rvalue,
2320 void *data,
2321 void *userdata) {
2322
2323 Unit *u = userdata;
2324 const char *word, *state;
2325 size_t l;
2326
2327 assert(filename);
2328 assert(lvalue);
2329 assert(rvalue);
2330 assert(data);
2331
2332 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
2333 int r;
2334 _cleanup_free_ char *n;
2335
2336 n = strndup(word, l);
2337 if (!n)
2338 return log_oom();
2339
2340 if (!utf8_is_valid(n)) {
2341 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
2342 continue;
2343 }
2344
2345 r = unit_require_mounts_for(u, n);
2346 if (r < 0) {
2347 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount for, ignoring: %s", rvalue);
2348 continue;
2349 }
2350 }
2351 if (!isempty(state))
2352 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
2353
2354 return 0;
2355 }
2356
2357 int config_parse_documentation(const char *unit,
2358 const char *filename,
2359 unsigned line,
2360 const char *section,
2361 unsigned section_line,
2362 const char *lvalue,
2363 int ltype,
2364 const char *rvalue,
2365 void *data,
2366 void *userdata) {
2367
2368 Unit *u = userdata;
2369 int r;
2370 char **a, **b;
2371
2372 assert(filename);
2373 assert(lvalue);
2374 assert(rvalue);
2375 assert(u);
2376
2377 if (isempty(rvalue)) {
2378 /* Empty assignment resets the list */
2379 u->documentation = strv_free(u->documentation);
2380 return 0;
2381 }
2382
2383 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
2384 rvalue, data, userdata);
2385 if (r < 0)
2386 return r;
2387
2388 for (a = b = u->documentation; a && *a; a++) {
2389
2390 if (documentation_url_is_valid(*a))
2391 *(b++) = *a;
2392 else {
2393 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a);
2394 free(*a);
2395 }
2396 }
2397 if (b)
2398 *b = NULL;
2399
2400 return r;
2401 }
2402
2403 #ifdef HAVE_SECCOMP
2404 static int syscall_filter_parse_one(
2405 const char *unit,
2406 const char *filename,
2407 unsigned line,
2408 ExecContext *c,
2409 bool invert,
2410 const char *t,
2411 bool warn) {
2412 int r;
2413
2414 if (*t == '@') {
2415 const SystemCallFilterSet *set;
2416
2417 for (set = syscall_filter_sets; set->set_name; set++)
2418 if (streq(set->set_name, t)) {
2419 const char *sys;
2420
2421 NULSTR_FOREACH(sys, set->value) {
2422 r = syscall_filter_parse_one(unit, filename, line, c, invert, sys, false);
2423 if (r < 0)
2424 return r;
2425 }
2426 break;
2427 }
2428 } else {
2429 int id;
2430
2431 id = seccomp_syscall_resolve_name(t);
2432 if (id < 0) {
2433 if (warn)
2434 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
2435 return 0;
2436 }
2437
2438 /* If we previously wanted to forbid a syscall and now
2439 * we want to allow it, then remove it from the list
2440 */
2441 if (!invert == c->syscall_whitelist) {
2442 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2443 if (r == 0)
2444 return 0;
2445 if (r < 0)
2446 return log_oom();
2447 } else
2448 set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
2449 }
2450 return 0;
2451 }
2452
2453 int config_parse_syscall_filter(
2454 const char *unit,
2455 const char *filename,
2456 unsigned line,
2457 const char *section,
2458 unsigned section_line,
2459 const char *lvalue,
2460 int ltype,
2461 const char *rvalue,
2462 void *data,
2463 void *userdata) {
2464
2465 ExecContext *c = data;
2466 Unit *u = userdata;
2467 bool invert = false;
2468 const char *word, *state;
2469 size_t l;
2470 int r;
2471
2472 assert(filename);
2473 assert(lvalue);
2474 assert(rvalue);
2475 assert(u);
2476
2477 if (isempty(rvalue)) {
2478 /* Empty assignment resets the list */
2479 c->syscall_filter = set_free(c->syscall_filter);
2480 c->syscall_whitelist = false;
2481 return 0;
2482 }
2483
2484 if (rvalue[0] == '~') {
2485 invert = true;
2486 rvalue++;
2487 }
2488
2489 if (!c->syscall_filter) {
2490 c->syscall_filter = set_new(NULL);
2491 if (!c->syscall_filter)
2492 return log_oom();
2493
2494 if (invert)
2495 /* Allow everything but the ones listed */
2496 c->syscall_whitelist = false;
2497 else {
2498 /* Allow nothing but the ones listed */
2499 c->syscall_whitelist = true;
2500
2501 /* Accept default syscalls if we are on a whitelist */
2502 r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false);
2503 if (r < 0)
2504 return r;
2505 }
2506 }
2507
2508 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
2509 _cleanup_free_ char *t = NULL;
2510
2511 t = strndup(word, l);
2512 if (!t)
2513 return log_oom();
2514
2515 r = syscall_filter_parse_one(unit, filename, line, c, invert, t, true);
2516 if (r < 0)
2517 return r;
2518 }
2519 if (!isempty(state))
2520 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
2521
2522 /* Turn on NNP, but only if it wasn't configured explicitly
2523 * before, and only if we are in user mode. */
2524 if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager))
2525 c->no_new_privileges = true;
2526
2527 return 0;
2528 }
2529
2530 int config_parse_syscall_archs(
2531 const char *unit,
2532 const char *filename,
2533 unsigned line,
2534 const char *section,
2535 unsigned section_line,
2536 const char *lvalue,
2537 int ltype,
2538 const char *rvalue,
2539 void *data,
2540 void *userdata) {
2541
2542 Set **archs = data;
2543 const char *word, *state;
2544 size_t l;
2545 int r;
2546
2547 if (isempty(rvalue)) {
2548 *archs = set_free(*archs);
2549 return 0;
2550 }
2551
2552 r = set_ensure_allocated(archs, NULL);
2553 if (r < 0)
2554 return log_oom();
2555
2556 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
2557 _cleanup_free_ char *t = NULL;
2558 uint32_t a;
2559
2560 t = strndup(word, l);
2561 if (!t)
2562 return log_oom();
2563
2564 r = seccomp_arch_from_string(t, &a);
2565 if (r < 0) {
2566 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call architecture, ignoring: %s", t);
2567 continue;
2568 }
2569
2570 r = set_put(*archs, UINT32_TO_PTR(a + 1));
2571 if (r == 0)
2572 continue;
2573 if (r < 0)
2574 return log_oom();
2575 }
2576 if (!isempty(state))
2577 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
2578
2579 return 0;
2580 }
2581
2582 int config_parse_syscall_errno(
2583 const char *unit,
2584 const char *filename,
2585 unsigned line,
2586 const char *section,
2587 unsigned section_line,
2588 const char *lvalue,
2589 int ltype,
2590 const char *rvalue,
2591 void *data,
2592 void *userdata) {
2593
2594 ExecContext *c = data;
2595 int e;
2596
2597 assert(filename);
2598 assert(lvalue);
2599 assert(rvalue);
2600
2601 if (isempty(rvalue)) {
2602 /* Empty assignment resets to KILL */
2603 c->syscall_errno = 0;
2604 return 0;
2605 }
2606
2607 e = errno_from_name(rvalue);
2608 if (e < 0) {
2609 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
2610 return 0;
2611 }
2612
2613 c->syscall_errno = e;
2614 return 0;
2615 }
2616
2617 int config_parse_address_families(
2618 const char *unit,
2619 const char *filename,
2620 unsigned line,
2621 const char *section,
2622 unsigned section_line,
2623 const char *lvalue,
2624 int ltype,
2625 const char *rvalue,
2626 void *data,
2627 void *userdata) {
2628
2629 ExecContext *c = data;
2630 bool invert = false;
2631 const char *word, *state;
2632 size_t l;
2633 int r;
2634
2635 assert(filename);
2636 assert(lvalue);
2637 assert(rvalue);
2638
2639 if (isempty(rvalue)) {
2640 /* Empty assignment resets the list */
2641 c->address_families = set_free(c->address_families);
2642 c->address_families_whitelist = false;
2643 return 0;
2644 }
2645
2646 if (rvalue[0] == '~') {
2647 invert = true;
2648 rvalue++;
2649 }
2650
2651 if (!c->address_families) {
2652 c->address_families = set_new(NULL);
2653 if (!c->address_families)
2654 return log_oom();
2655
2656 c->address_families_whitelist = !invert;
2657 }
2658
2659 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
2660 _cleanup_free_ char *t = NULL;
2661 int af;
2662
2663 t = strndup(word, l);
2664 if (!t)
2665 return log_oom();
2666
2667 af = af_from_name(t);
2668 if (af <= 0) {
2669 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse address family, ignoring: %s", t);
2670 continue;
2671 }
2672
2673 /* If we previously wanted to forbid an address family and now
2674 * we want to allow it, then remove it from the list
2675 */
2676 if (!invert == c->address_families_whitelist) {
2677 r = set_put(c->address_families, INT_TO_PTR(af));
2678 if (r == 0)
2679 continue;
2680 if (r < 0)
2681 return log_oom();
2682 } else
2683 set_remove(c->address_families, INT_TO_PTR(af));
2684 }
2685 if (!isempty(state))
2686 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
2687
2688 return 0;
2689 }
2690 #endif
2691
2692 int config_parse_unit_slice(
2693 const char *unit,
2694 const char *filename,
2695 unsigned line,
2696 const char *section,
2697 unsigned section_line,
2698 const char *lvalue,
2699 int ltype,
2700 const char *rvalue,
2701 void *data,
2702 void *userdata) {
2703
2704 _cleanup_free_ char *k = NULL;
2705 Unit *u = userdata, *slice = NULL;
2706 int r;
2707
2708 assert(filename);
2709 assert(lvalue);
2710 assert(rvalue);
2711 assert(u);
2712
2713 r = unit_name_printf(u, rvalue, &k);
2714 if (r < 0) {
2715 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2716 return 0;
2717 }
2718
2719 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
2720 if (r < 0) {
2721 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k);
2722 return 0;
2723 }
2724
2725 r = unit_set_slice(u, slice);
2726 if (r < 0) {
2727 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id);
2728 return 0;
2729 }
2730
2731 return 0;
2732 }
2733
2734 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2735
2736 int config_parse_cpu_shares(
2737 const char *unit,
2738 const char *filename,
2739 unsigned line,
2740 const char *section,
2741 unsigned section_line,
2742 const char *lvalue,
2743 int ltype,
2744 const char *rvalue,
2745 void *data,
2746 void *userdata) {
2747
2748 uint64_t *shares = data;
2749 int r;
2750
2751 assert(filename);
2752 assert(lvalue);
2753 assert(rvalue);
2754
2755 r = cg_cpu_shares_parse(rvalue, shares);
2756 if (r < 0) {
2757 log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
2758 return 0;
2759 }
2760
2761 return 0;
2762 }
2763
2764 int config_parse_cpu_quota(
2765 const char *unit,
2766 const char *filename,
2767 unsigned line,
2768 const char *section,
2769 unsigned section_line,
2770 const char *lvalue,
2771 int ltype,
2772 const char *rvalue,
2773 void *data,
2774 void *userdata) {
2775
2776 CGroupContext *c = data;
2777 int r;
2778
2779 assert(filename);
2780 assert(lvalue);
2781 assert(rvalue);
2782
2783 if (isempty(rvalue)) {
2784 c->cpu_quota_per_sec_usec = USEC_INFINITY;
2785 return 0;
2786 }
2787
2788 r = parse_percent(rvalue);
2789 if (r <= 0) {
2790 log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
2791 return 0;
2792 }
2793
2794 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
2795 return 0;
2796 }
2797
2798 int config_parse_memory_limit(
2799 const char *unit,
2800 const char *filename,
2801 unsigned line,
2802 const char *section,
2803 unsigned section_line,
2804 const char *lvalue,
2805 int ltype,
2806 const char *rvalue,
2807 void *data,
2808 void *userdata) {
2809
2810 CGroupContext *c = data;
2811 uint64_t bytes = CGROUP_LIMIT_MAX;
2812 int r;
2813
2814 if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
2815
2816 r = parse_percent(rvalue);
2817 if (r < 0) {
2818 r = parse_size(rvalue, 1024, &bytes);
2819 if (r < 0) {
2820 log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
2821 return 0;
2822 }
2823 } else
2824 bytes = physical_memory_scale(r, 100U);
2825
2826 if (bytes < 1) {
2827 log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' too small. Ignoring.", rvalue);
2828 return 0;
2829 }
2830 }
2831
2832 if (streq(lvalue, "MemoryLow"))
2833 c->memory_low = bytes;
2834 else if (streq(lvalue, "MemoryHigh"))
2835 c->memory_high = bytes;
2836 else if (streq(lvalue, "MemoryMax"))
2837 c->memory_max = bytes;
2838 else
2839 c->memory_limit = bytes;
2840
2841 return 0;
2842 }
2843
2844 int config_parse_tasks_max(
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 uint64_t *tasks_max = data, u;
2857 int r;
2858
2859 if (isempty(rvalue) || streq(rvalue, "infinity")) {
2860 *tasks_max = (uint64_t) -1;
2861 return 0;
2862 }
2863
2864 r = safe_atou64(rvalue, &u);
2865 if (r < 0 || u < 1) {
2866 log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
2867 return 0;
2868 }
2869
2870 *tasks_max = u;
2871 return 0;
2872 }
2873
2874 int config_parse_device_allow(
2875 const char *unit,
2876 const char *filename,
2877 unsigned line,
2878 const char *section,
2879 unsigned section_line,
2880 const char *lvalue,
2881 int ltype,
2882 const char *rvalue,
2883 void *data,
2884 void *userdata) {
2885
2886 _cleanup_free_ char *path = NULL, *t = NULL;
2887 CGroupContext *c = data;
2888 CGroupDeviceAllow *a;
2889 const char *m = NULL;
2890 size_t n;
2891 int r;
2892
2893 if (isempty(rvalue)) {
2894 while (c->device_allow)
2895 cgroup_context_free_device_allow(c, c->device_allow);
2896
2897 return 0;
2898 }
2899
2900 r = unit_full_printf(userdata, rvalue, &t);
2901 if(r < 0) {
2902 log_syntax(unit, LOG_WARNING, filename, line, r,
2903 "Failed to resolve specifiers in %s, ignoring: %m",
2904 rvalue);
2905 }
2906
2907 n = strcspn(t, WHITESPACE);
2908
2909 path = strndup(t, n);
2910 if (!path)
2911 return log_oom();
2912
2913 if (!startswith(path, "/dev/") &&
2914 !startswith(path, "block-") &&
2915 !startswith(path, "char-")) {
2916 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
2917 return 0;
2918 }
2919
2920 m = t + n + strspn(t + n, WHITESPACE);
2921 if (isempty(m))
2922 m = "rwm";
2923
2924 if (!in_charset(m, "rwm")) {
2925 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
2926 return 0;
2927 }
2928
2929 a = new0(CGroupDeviceAllow, 1);
2930 if (!a)
2931 return log_oom();
2932
2933 a->path = path;
2934 path = NULL;
2935 a->r = !!strchr(m, 'r');
2936 a->w = !!strchr(m, 'w');
2937 a->m = !!strchr(m, 'm');
2938
2939 LIST_PREPEND(device_allow, c->device_allow, a);
2940 return 0;
2941 }
2942
2943 int config_parse_io_weight(
2944 const char *unit,
2945 const char *filename,
2946 unsigned line,
2947 const char *section,
2948 unsigned section_line,
2949 const char *lvalue,
2950 int ltype,
2951 const char *rvalue,
2952 void *data,
2953 void *userdata) {
2954
2955 uint64_t *weight = data;
2956 int r;
2957
2958 assert(filename);
2959 assert(lvalue);
2960 assert(rvalue);
2961
2962 r = cg_weight_parse(rvalue, weight);
2963 if (r < 0) {
2964 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue);
2965 return 0;
2966 }
2967
2968 return 0;
2969 }
2970
2971 int config_parse_io_device_weight(
2972 const char *unit,
2973 const char *filename,
2974 unsigned line,
2975 const char *section,
2976 unsigned section_line,
2977 const char *lvalue,
2978 int ltype,
2979 const char *rvalue,
2980 void *data,
2981 void *userdata) {
2982
2983 _cleanup_free_ char *path = NULL;
2984 CGroupIODeviceWeight *w;
2985 CGroupContext *c = data;
2986 const char *weight;
2987 uint64_t u;
2988 size_t n;
2989 int r;
2990
2991 assert(filename);
2992 assert(lvalue);
2993 assert(rvalue);
2994
2995 if (isempty(rvalue)) {
2996 while (c->io_device_weights)
2997 cgroup_context_free_io_device_weight(c, c->io_device_weights);
2998
2999 return 0;
3000 }
3001
3002 n = strcspn(rvalue, WHITESPACE);
3003 weight = rvalue + n;
3004 weight += strspn(weight, WHITESPACE);
3005
3006 if (isempty(weight)) {
3007 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
3008 return 0;
3009 }
3010
3011 path = strndup(rvalue, n);
3012 if (!path)
3013 return log_oom();
3014
3015 if (!path_startswith(path, "/dev")) {
3016 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3017 return 0;
3018 }
3019
3020 r = cg_weight_parse(weight, &u);
3021 if (r < 0) {
3022 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight);
3023 return 0;
3024 }
3025
3026 assert(u != CGROUP_WEIGHT_INVALID);
3027
3028 w = new0(CGroupIODeviceWeight, 1);
3029 if (!w)
3030 return log_oom();
3031
3032 w->path = path;
3033 path = NULL;
3034
3035 w->weight = u;
3036
3037 LIST_PREPEND(device_weights, c->io_device_weights, w);
3038 return 0;
3039 }
3040
3041 int config_parse_io_limit(
3042 const char *unit,
3043 const char *filename,
3044 unsigned line,
3045 const char *section,
3046 unsigned section_line,
3047 const char *lvalue,
3048 int ltype,
3049 const char *rvalue,
3050 void *data,
3051 void *userdata) {
3052
3053 _cleanup_free_ char *path = NULL;
3054 CGroupIODeviceLimit *l = NULL, *t;
3055 CGroupContext *c = data;
3056 CGroupIOLimitType type;
3057 const char *limit;
3058 uint64_t num;
3059 size_t n;
3060 int r;
3061
3062 assert(filename);
3063 assert(lvalue);
3064 assert(rvalue);
3065
3066 type = cgroup_io_limit_type_from_string(lvalue);
3067 assert(type >= 0);
3068
3069 if (isempty(rvalue)) {
3070 LIST_FOREACH(device_limits, l, c->io_device_limits)
3071 l->limits[type] = cgroup_io_limit_defaults[type];
3072 return 0;
3073 }
3074
3075 n = strcspn(rvalue, WHITESPACE);
3076 limit = rvalue + n;
3077 limit += strspn(limit, WHITESPACE);
3078
3079 if (!*limit) {
3080 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3081 return 0;
3082 }
3083
3084 path = strndup(rvalue, n);
3085 if (!path)
3086 return log_oom();
3087
3088 if (!path_startswith(path, "/dev")) {
3089 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3090 return 0;
3091 }
3092
3093 if (streq("infinity", limit)) {
3094 num = CGROUP_LIMIT_MAX;
3095 } else {
3096 r = parse_size(limit, 1000, &num);
3097 if (r < 0 || num <= 0) {
3098 log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue);
3099 return 0;
3100 }
3101 }
3102
3103 LIST_FOREACH(device_limits, t, c->io_device_limits) {
3104 if (path_equal(path, t->path)) {
3105 l = t;
3106 break;
3107 }
3108 }
3109
3110 if (!l) {
3111 CGroupIOLimitType ttype;
3112
3113 l = new0(CGroupIODeviceLimit, 1);
3114 if (!l)
3115 return log_oom();
3116
3117 l->path = path;
3118 path = NULL;
3119 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
3120 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
3121
3122 LIST_PREPEND(device_limits, c->io_device_limits, l);
3123 }
3124
3125 l->limits[type] = num;
3126
3127 return 0;
3128 }
3129
3130 int config_parse_blockio_weight(
3131 const char *unit,
3132 const char *filename,
3133 unsigned line,
3134 const char *section,
3135 unsigned section_line,
3136 const char *lvalue,
3137 int ltype,
3138 const char *rvalue,
3139 void *data,
3140 void *userdata) {
3141
3142 uint64_t *weight = data;
3143 int r;
3144
3145 assert(filename);
3146 assert(lvalue);
3147 assert(rvalue);
3148
3149 r = cg_blkio_weight_parse(rvalue, weight);
3150 if (r < 0) {
3151 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
3152 return 0;
3153 }
3154
3155 return 0;
3156 }
3157
3158 int config_parse_blockio_device_weight(
3159 const char *unit,
3160 const char *filename,
3161 unsigned line,
3162 const char *section,
3163 unsigned section_line,
3164 const char *lvalue,
3165 int ltype,
3166 const char *rvalue,
3167 void *data,
3168 void *userdata) {
3169
3170 _cleanup_free_ char *path = NULL;
3171 CGroupBlockIODeviceWeight *w;
3172 CGroupContext *c = data;
3173 const char *weight;
3174 uint64_t u;
3175 size_t n;
3176 int r;
3177
3178 assert(filename);
3179 assert(lvalue);
3180 assert(rvalue);
3181
3182 if (isempty(rvalue)) {
3183 while (c->blockio_device_weights)
3184 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
3185
3186 return 0;
3187 }
3188
3189 n = strcspn(rvalue, WHITESPACE);
3190 weight = rvalue + n;
3191 weight += strspn(weight, WHITESPACE);
3192
3193 if (isempty(weight)) {
3194 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
3195 return 0;
3196 }
3197
3198 path = strndup(rvalue, n);
3199 if (!path)
3200 return log_oom();
3201
3202 if (!path_startswith(path, "/dev")) {
3203 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3204 return 0;
3205 }
3206
3207 r = cg_blkio_weight_parse(weight, &u);
3208 if (r < 0) {
3209 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
3210 return 0;
3211 }
3212
3213 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
3214
3215 w = new0(CGroupBlockIODeviceWeight, 1);
3216 if (!w)
3217 return log_oom();
3218
3219 w->path = path;
3220 path = NULL;
3221
3222 w->weight = u;
3223
3224 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
3225 return 0;
3226 }
3227
3228 int config_parse_blockio_bandwidth(
3229 const char *unit,
3230 const char *filename,
3231 unsigned line,
3232 const char *section,
3233 unsigned section_line,
3234 const char *lvalue,
3235 int ltype,
3236 const char *rvalue,
3237 void *data,
3238 void *userdata) {
3239
3240 _cleanup_free_ char *path = NULL;
3241 CGroupBlockIODeviceBandwidth *b = NULL, *t;
3242 CGroupContext *c = data;
3243 const char *bandwidth;
3244 uint64_t bytes;
3245 bool read;
3246 size_t n;
3247 int r;
3248
3249 assert(filename);
3250 assert(lvalue);
3251 assert(rvalue);
3252
3253 read = streq("BlockIOReadBandwidth", lvalue);
3254
3255 if (isempty(rvalue)) {
3256 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
3257 b->rbps = CGROUP_LIMIT_MAX;
3258 b->wbps = CGROUP_LIMIT_MAX;
3259 }
3260 return 0;
3261 }
3262
3263 n = strcspn(rvalue, WHITESPACE);
3264 bandwidth = rvalue + n;
3265 bandwidth += strspn(bandwidth, WHITESPACE);
3266
3267 if (!*bandwidth) {
3268 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3269 return 0;
3270 }
3271
3272 path = strndup(rvalue, n);
3273 if (!path)
3274 return log_oom();
3275
3276 if (!path_startswith(path, "/dev")) {
3277 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3278 return 0;
3279 }
3280
3281 r = parse_size(bandwidth, 1000, &bytes);
3282 if (r < 0 || bytes <= 0) {
3283 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
3284 return 0;
3285 }
3286
3287 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
3288 if (path_equal(path, t->path)) {
3289 b = t;
3290 break;
3291 }
3292 }
3293
3294 if (!t) {
3295 b = new0(CGroupBlockIODeviceBandwidth, 1);
3296 if (!b)
3297 return log_oom();
3298
3299 b->path = path;
3300 path = NULL;
3301 b->rbps = CGROUP_LIMIT_MAX;
3302 b->wbps = CGROUP_LIMIT_MAX;
3303
3304 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
3305 }
3306
3307 if (read)
3308 b->rbps = bytes;
3309 else
3310 b->wbps = bytes;
3311
3312 return 0;
3313 }
3314
3315 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
3316
3317 int config_parse_job_mode_isolate(
3318 const char *unit,
3319 const char *filename,
3320 unsigned line,
3321 const char *section,
3322 unsigned section_line,
3323 const char *lvalue,
3324 int ltype,
3325 const char *rvalue,
3326 void *data,
3327 void *userdata) {
3328
3329 JobMode *m = data;
3330 int r;
3331
3332 assert(filename);
3333 assert(lvalue);
3334 assert(rvalue);
3335
3336 r = parse_boolean(rvalue);
3337 if (r < 0) {
3338 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
3339 return 0;
3340 }
3341
3342 *m = r ? JOB_ISOLATE : JOB_REPLACE;
3343 return 0;
3344 }
3345
3346 int config_parse_runtime_directory(
3347 const char *unit,
3348 const char *filename,
3349 unsigned line,
3350 const char *section,
3351 unsigned section_line,
3352 const char *lvalue,
3353 int ltype,
3354 const char *rvalue,
3355 void *data,
3356 void *userdata) {
3357
3358 char***rt = data;
3359 Unit *u = userdata;
3360 const char *word, *state;
3361 size_t l;
3362 int r;
3363
3364 assert(filename);
3365 assert(lvalue);
3366 assert(rvalue);
3367 assert(data);
3368
3369 if (isempty(rvalue)) {
3370 /* Empty assignment resets the list */
3371 *rt = strv_free(*rt);
3372 return 0;
3373 }
3374
3375 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
3376 _cleanup_free_ char *t = NULL, *n = NULL;
3377
3378 t = strndup(word, l);
3379 if (!t)
3380 return log_oom();
3381
3382 r = unit_name_printf(u, t, &n);
3383 if (r < 0) {
3384 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
3385 continue;
3386 }
3387
3388 if (!filename_is_valid(n)) {
3389 log_syntax(unit, LOG_ERR, filename, line, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
3390 continue;
3391 }
3392
3393 r = strv_push(rt, n);
3394 if (r < 0)
3395 return log_oom();
3396
3397 n = NULL;
3398 }
3399 if (!isempty(state))
3400 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3401
3402 return 0;
3403 }
3404
3405 int config_parse_set_status(
3406 const char *unit,
3407 const char *filename,
3408 unsigned line,
3409 const char *section,
3410 unsigned section_line,
3411 const char *lvalue,
3412 int ltype,
3413 const char *rvalue,
3414 void *data,
3415 void *userdata) {
3416
3417 size_t l;
3418 const char *word, *state;
3419 int r;
3420 ExitStatusSet *status_set = data;
3421
3422 assert(filename);
3423 assert(lvalue);
3424 assert(rvalue);
3425 assert(data);
3426
3427 /* Empty assignment resets the list */
3428 if (isempty(rvalue)) {
3429 exit_status_set_free(status_set);
3430 return 0;
3431 }
3432
3433 FOREACH_WORD(word, l, rvalue, state) {
3434 _cleanup_free_ char *temp;
3435 int val;
3436 Set **set;
3437
3438 temp = strndup(word, l);
3439 if (!temp)
3440 return log_oom();
3441
3442 r = safe_atoi(temp, &val);
3443 if (r < 0) {
3444 val = signal_from_string_try_harder(temp);
3445
3446 if (val <= 0) {
3447 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
3448 continue;
3449 }
3450 set = &status_set->signal;
3451 } else {
3452 if (val < 0 || val > 255) {
3453 log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
3454 continue;
3455 }
3456 set = &status_set->status;
3457 }
3458
3459 r = set_ensure_allocated(set, NULL);
3460 if (r < 0)
3461 return log_oom();
3462
3463 r = set_put(*set, INT_TO_PTR(val));
3464 if (r < 0) {
3465 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
3466 return r;
3467 }
3468 }
3469 if (!isempty(state))
3470 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3471
3472 return 0;
3473 }
3474
3475 int config_parse_namespace_path_strv(
3476 const char *unit,
3477 const char *filename,
3478 unsigned line,
3479 const char *section,
3480 unsigned section_line,
3481 const char *lvalue,
3482 int ltype,
3483 const char *rvalue,
3484 void *data,
3485 void *userdata) {
3486
3487 char*** sv = data;
3488 const char *prev;
3489 const char *cur;
3490 int r;
3491
3492 assert(filename);
3493 assert(lvalue);
3494 assert(rvalue);
3495 assert(data);
3496
3497 if (isempty(rvalue)) {
3498 /* Empty assignment resets the list */
3499 *sv = strv_free(*sv);
3500 return 0;
3501 }
3502
3503 prev = cur = rvalue;
3504 for (;;) {
3505 _cleanup_free_ char *word = NULL;
3506 int offset;
3507
3508 r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
3509 if (r == 0)
3510 break;
3511 if (r == -ENOMEM)
3512 return log_oom();
3513 if (r < 0) {
3514 log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage, ignoring: %s", prev);
3515 return 0;
3516 }
3517
3518 if (!utf8_is_valid(word)) {
3519 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
3520 prev = cur;
3521 continue;
3522 }
3523
3524 offset = word[0] == '-';
3525 if (!path_is_absolute(word + offset)) {
3526 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", word);
3527 prev = cur;
3528 continue;
3529 }
3530
3531 path_kill_slashes(word + offset);
3532
3533 r = strv_push(sv, word);
3534 if (r < 0)
3535 return log_oom();
3536
3537 prev = cur;
3538 word = NULL;
3539 }
3540
3541 return 0;
3542 }
3543
3544 int config_parse_no_new_privileges(
3545 const char* unit,
3546 const char *filename,
3547 unsigned line,
3548 const char *section,
3549 unsigned section_line,
3550 const char *lvalue,
3551 int ltype,
3552 const char *rvalue,
3553 void *data,
3554 void *userdata) {
3555
3556 ExecContext *c = data;
3557 int k;
3558
3559 assert(filename);
3560 assert(lvalue);
3561 assert(rvalue);
3562 assert(data);
3563
3564 k = parse_boolean(rvalue);
3565 if (k < 0) {
3566 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
3567 return 0;
3568 }
3569
3570 c->no_new_privileges = !!k;
3571 c->no_new_privileges_set = true;
3572
3573 return 0;
3574 }
3575
3576 int config_parse_protect_home(
3577 const char* unit,
3578 const char *filename,
3579 unsigned line,
3580 const char *section,
3581 unsigned section_line,
3582 const char *lvalue,
3583 int ltype,
3584 const char *rvalue,
3585 void *data,
3586 void *userdata) {
3587
3588 ExecContext *c = data;
3589 int k;
3590
3591 assert(filename);
3592 assert(lvalue);
3593 assert(rvalue);
3594 assert(data);
3595
3596 /* Our enum shall be a superset of booleans, hence first try
3597 * to parse as boolean, and then as enum */
3598
3599 k = parse_boolean(rvalue);
3600 if (k > 0)
3601 c->protect_home = PROTECT_HOME_YES;
3602 else if (k == 0)
3603 c->protect_home = PROTECT_HOME_NO;
3604 else {
3605 ProtectHome h;
3606
3607 h = protect_home_from_string(rvalue);
3608 if (h < 0) {
3609 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
3610 return 0;
3611 }
3612
3613 c->protect_home = h;
3614 }
3615
3616 return 0;
3617 }
3618
3619 int config_parse_protect_system(
3620 const char* unit,
3621 const char *filename,
3622 unsigned line,
3623 const char *section,
3624 unsigned section_line,
3625 const char *lvalue,
3626 int ltype,
3627 const char *rvalue,
3628 void *data,
3629 void *userdata) {
3630
3631 ExecContext *c = data;
3632 int k;
3633
3634 assert(filename);
3635 assert(lvalue);
3636 assert(rvalue);
3637 assert(data);
3638
3639 /* Our enum shall be a superset of booleans, hence first try
3640 * to parse as boolean, and then as enum */
3641
3642 k = parse_boolean(rvalue);
3643 if (k > 0)
3644 c->protect_system = PROTECT_SYSTEM_YES;
3645 else if (k == 0)
3646 c->protect_system = PROTECT_SYSTEM_NO;
3647 else {
3648 ProtectSystem s;
3649
3650 s = protect_system_from_string(rvalue);
3651 if (s < 0) {
3652 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
3653 return 0;
3654 }
3655
3656 c->protect_system = s;
3657 }
3658
3659 return 0;
3660 }
3661
3662 #define FOLLOW_MAX 8
3663
3664 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
3665 char *id = NULL;
3666 unsigned c = 0;
3667 int fd, r;
3668 FILE *f;
3669
3670 assert(filename);
3671 assert(*filename);
3672 assert(_f);
3673 assert(names);
3674
3675 /* This will update the filename pointer if the loaded file is
3676 * reached by a symlink. The old string will be freed. */
3677
3678 for (;;) {
3679 char *target, *name;
3680
3681 if (c++ >= FOLLOW_MAX)
3682 return -ELOOP;
3683
3684 path_kill_slashes(*filename);
3685
3686 /* Add the file name we are currently looking at to
3687 * the names of this unit, but only if it is a valid
3688 * unit name. */
3689 name = basename(*filename);
3690 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
3691
3692 id = set_get(names, name);
3693 if (!id) {
3694 id = strdup(name);
3695 if (!id)
3696 return -ENOMEM;
3697
3698 r = set_consume(names, id);
3699 if (r < 0)
3700 return r;
3701 }
3702 }
3703
3704 /* Try to open the file name, but don't if its a symlink */
3705 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
3706 if (fd >= 0)
3707 break;
3708
3709 if (errno != ELOOP)
3710 return -errno;
3711
3712 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3713 r = readlink_and_make_absolute(*filename, &target);
3714 if (r < 0)
3715 return r;
3716
3717 free(*filename);
3718 *filename = target;
3719 }
3720
3721 f = fdopen(fd, "re");
3722 if (!f) {
3723 safe_close(fd);
3724 return -errno;
3725 }
3726
3727 *_f = f;
3728 *_final = id;
3729
3730 return 0;
3731 }
3732
3733 static int merge_by_names(Unit **u, Set *names, const char *id) {
3734 char *k;
3735 int r;
3736
3737 assert(u);
3738 assert(*u);
3739 assert(names);
3740
3741 /* Let's try to add in all symlink names we found */
3742 while ((k = set_steal_first(names))) {
3743
3744 /* First try to merge in the other name into our
3745 * unit */
3746 r = unit_merge_by_name(*u, k);
3747 if (r < 0) {
3748 Unit *other;
3749
3750 /* Hmm, we couldn't merge the other unit into
3751 * ours? Then let's try it the other way
3752 * round */
3753
3754 /* If the symlink name we are looking at is unit template, then
3755 we must search for instance of this template */
3756 if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) {
3757 _cleanup_free_ char *instance = NULL;
3758
3759 r = unit_name_replace_instance(k, (*u)->instance, &instance);
3760 if (r < 0)
3761 return r;
3762
3763 other = manager_get_unit((*u)->manager, instance);
3764 } else
3765 other = manager_get_unit((*u)->manager, k);
3766
3767 free(k);
3768
3769 if (other) {
3770 r = unit_merge(other, *u);
3771 if (r >= 0) {
3772 *u = other;
3773 return merge_by_names(u, names, NULL);
3774 }
3775 }
3776
3777 return r;
3778 }
3779
3780 if (id == k)
3781 unit_choose_id(*u, id);
3782
3783 free(k);
3784 }
3785
3786 return 0;
3787 }
3788
3789 static int load_from_path(Unit *u, const char *path) {
3790 _cleanup_set_free_free_ Set *symlink_names = NULL;
3791 _cleanup_fclose_ FILE *f = NULL;
3792 _cleanup_free_ char *filename = NULL;
3793 char *id = NULL;
3794 Unit *merged;
3795 struct stat st;
3796 int r;
3797
3798 assert(u);
3799 assert(path);
3800
3801 symlink_names = set_new(&string_hash_ops);
3802 if (!symlink_names)
3803 return -ENOMEM;
3804
3805 if (path_is_absolute(path)) {
3806
3807 filename = strdup(path);
3808 if (!filename)
3809 return -ENOMEM;
3810
3811 r = open_follow(&filename, &f, symlink_names, &id);
3812 if (r < 0) {
3813 filename = mfree(filename);
3814 if (r != -ENOENT)
3815 return r;
3816 }
3817
3818 } else {
3819 char **p;
3820
3821 STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
3822
3823 /* Instead of opening the path right away, we manually
3824 * follow all symlinks and add their name to our unit
3825 * name set while doing so */
3826 filename = path_make_absolute(path, *p);
3827 if (!filename)
3828 return -ENOMEM;
3829
3830 if (u->manager->unit_path_cache &&
3831 !set_get(u->manager->unit_path_cache, filename))
3832 r = -ENOENT;
3833 else
3834 r = open_follow(&filename, &f, symlink_names, &id);
3835 if (r >= 0)
3836 break;
3837 filename = mfree(filename);
3838
3839 /* ENOENT means that the file is missing or is a dangling symlink.
3840 * ENOTDIR means that one of paths we expect to be is a directory
3841 * is not a directory, we should just ignore that.
3842 * EACCES means that the directory or file permissions are wrong.
3843 */
3844 if (r == -EACCES)
3845 log_debug_errno(r, "Cannot access \"%s\": %m", filename);
3846 else if (!IN_SET(r, -ENOENT, -ENOTDIR))
3847 return r;
3848
3849 /* Empty the symlink names for the next run */
3850 set_clear_free(symlink_names);
3851 }
3852 }
3853
3854 if (!filename)
3855 /* Hmm, no suitable file found? */
3856 return 0;
3857
3858 if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
3859 log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
3860 return -ELOOP;
3861 }
3862
3863 merged = u;
3864 r = merge_by_names(&merged, symlink_names, id);
3865 if (r < 0)
3866 return r;
3867
3868 if (merged != u) {
3869 u->load_state = UNIT_MERGED;
3870 return 0;
3871 }
3872
3873 if (fstat(fileno(f), &st) < 0)
3874 return -errno;
3875
3876 if (null_or_empty(&st)) {
3877 u->load_state = UNIT_MASKED;
3878 u->fragment_mtime = 0;
3879 } else {
3880 u->load_state = UNIT_LOADED;
3881 u->fragment_mtime = timespec_load(&st.st_mtim);
3882
3883 /* Now, parse the file contents */
3884 r = config_parse(u->id, filename, f,
3885 UNIT_VTABLE(u)->sections,
3886 config_item_perf_lookup, load_fragment_gperf_lookup,
3887 false, true, false, u);
3888 if (r < 0)
3889 return r;
3890 }
3891
3892 free(u->fragment_path);
3893 u->fragment_path = filename;
3894 filename = NULL;
3895
3896 if (u->source_path) {
3897 if (stat(u->source_path, &st) >= 0)
3898 u->source_mtime = timespec_load(&st.st_mtim);
3899 else
3900 u->source_mtime = 0;
3901 }
3902
3903 return 0;
3904 }
3905
3906 int unit_load_fragment(Unit *u) {
3907 int r;
3908 Iterator i;
3909 const char *t;
3910
3911 assert(u);
3912 assert(u->load_state == UNIT_STUB);
3913 assert(u->id);
3914
3915 if (u->transient) {
3916 u->load_state = UNIT_LOADED;
3917 return 0;
3918 }
3919
3920 /* First, try to find the unit under its id. We always look
3921 * for unit files in the default directories, to make it easy
3922 * to override things by placing things in /etc/systemd/system */
3923 r = load_from_path(u, u->id);
3924 if (r < 0)
3925 return r;
3926
3927 /* Try to find an alias we can load this with */
3928 if (u->load_state == UNIT_STUB) {
3929 SET_FOREACH(t, u->names, i) {
3930
3931 if (t == u->id)
3932 continue;
3933
3934 r = load_from_path(u, t);
3935 if (r < 0)
3936 return r;
3937
3938 if (u->load_state != UNIT_STUB)
3939 break;
3940 }
3941 }
3942
3943 /* And now, try looking for it under the suggested (originally linked) path */
3944 if (u->load_state == UNIT_STUB && u->fragment_path) {
3945
3946 r = load_from_path(u, u->fragment_path);
3947 if (r < 0)
3948 return r;
3949
3950 if (u->load_state == UNIT_STUB)
3951 /* Hmm, this didn't work? Then let's get rid
3952 * of the fragment path stored for us, so that
3953 * we don't point to an invalid location. */
3954 u->fragment_path = mfree(u->fragment_path);
3955 }
3956
3957 /* Look for a template */
3958 if (u->load_state == UNIT_STUB && u->instance) {
3959 _cleanup_free_ char *k = NULL;
3960
3961 r = unit_name_template(u->id, &k);
3962 if (r < 0)
3963 return r;
3964
3965 r = load_from_path(u, k);
3966 if (r < 0)
3967 return r;
3968
3969 if (u->load_state == UNIT_STUB) {
3970 SET_FOREACH(t, u->names, i) {
3971 _cleanup_free_ char *z = NULL;
3972
3973 if (t == u->id)
3974 continue;
3975
3976 r = unit_name_template(t, &z);
3977 if (r < 0)
3978 return r;
3979
3980 r = load_from_path(u, z);
3981 if (r < 0)
3982 return r;
3983
3984 if (u->load_state != UNIT_STUB)
3985 break;
3986 }
3987 }
3988 }
3989
3990 return 0;
3991 }
3992
3993 void unit_dump_config_items(FILE *f) {
3994 static const struct {
3995 const ConfigParserCallback callback;
3996 const char *rvalue;
3997 } table[] = {
3998 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3999 { config_parse_warn_compat, "NOTSUPPORTED" },
4000 #endif
4001 { config_parse_int, "INTEGER" },
4002 { config_parse_unsigned, "UNSIGNED" },
4003 { config_parse_iec_size, "SIZE" },
4004 { config_parse_iec_uint64, "SIZE" },
4005 { config_parse_si_size, "SIZE" },
4006 { config_parse_bool, "BOOLEAN" },
4007 { config_parse_string, "STRING" },
4008 { config_parse_path, "PATH" },
4009 { config_parse_unit_path_printf, "PATH" },
4010 { config_parse_strv, "STRING [...]" },
4011 { config_parse_exec_nice, "NICE" },
4012 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
4013 { config_parse_exec_io_class, "IOCLASS" },
4014 { config_parse_exec_io_priority, "IOPRIORITY" },
4015 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
4016 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
4017 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
4018 { config_parse_mode, "MODE" },
4019 { config_parse_unit_env_file, "FILE" },
4020 { config_parse_output, "OUTPUT" },
4021 { config_parse_input, "INPUT" },
4022 { config_parse_log_facility, "FACILITY" },
4023 { config_parse_log_level, "LEVEL" },
4024 { config_parse_exec_secure_bits, "SECUREBITS" },
4025 { config_parse_capability_set, "BOUNDINGSET" },
4026 { config_parse_limit, "LIMIT" },
4027 { config_parse_unit_deps, "UNIT [...]" },
4028 { config_parse_exec, "PATH [ARGUMENT [...]]" },
4029 { config_parse_service_type, "SERVICETYPE" },
4030 { config_parse_service_restart, "SERVICERESTART" },
4031 #ifdef HAVE_SYSV_COMPAT
4032 { config_parse_sysv_priority, "SYSVPRIORITY" },
4033 #endif
4034 { config_parse_kill_mode, "KILLMODE" },
4035 { config_parse_signal, "SIGNAL" },
4036 { config_parse_socket_listen, "SOCKET [...]" },
4037 { config_parse_socket_bind, "SOCKETBIND" },
4038 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
4039 { config_parse_sec, "SECONDS" },
4040 { config_parse_nsec, "NANOSECONDS" },
4041 { config_parse_namespace_path_strv, "PATH [...]" },
4042 { config_parse_unit_requires_mounts_for, "PATH [...]" },
4043 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
4044 { config_parse_unit_string_printf, "STRING" },
4045 { config_parse_trigger_unit, "UNIT" },
4046 { config_parse_timer, "TIMER" },
4047 { config_parse_path_spec, "PATH" },
4048 { config_parse_notify_access, "ACCESS" },
4049 { config_parse_ip_tos, "TOS" },
4050 { config_parse_unit_condition_path, "CONDITION" },
4051 { config_parse_unit_condition_string, "CONDITION" },
4052 { config_parse_unit_condition_null, "CONDITION" },
4053 { config_parse_unit_slice, "SLICE" },
4054 { config_parse_documentation, "URL" },
4055 { config_parse_service_timeout, "SECONDS" },
4056 { config_parse_failure_action, "ACTION" },
4057 { config_parse_set_status, "STATUS" },
4058 { config_parse_service_sockets, "SOCKETS" },
4059 { config_parse_environ, "ENVIRON" },
4060 #ifdef HAVE_SECCOMP
4061 { config_parse_syscall_filter, "SYSCALLS" },
4062 { config_parse_syscall_archs, "ARCHS" },
4063 { config_parse_syscall_errno, "ERRNO" },
4064 { config_parse_address_families, "FAMILIES" },
4065 #endif
4066 { config_parse_cpu_shares, "SHARES" },
4067 { config_parse_memory_limit, "LIMIT" },
4068 { config_parse_device_allow, "DEVICE" },
4069 { config_parse_device_policy, "POLICY" },
4070 { config_parse_io_limit, "LIMIT" },
4071 { config_parse_io_weight, "WEIGHT" },
4072 { config_parse_io_device_weight, "DEVICEWEIGHT" },
4073 { config_parse_blockio_bandwidth, "BANDWIDTH" },
4074 { config_parse_blockio_weight, "WEIGHT" },
4075 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
4076 { config_parse_long, "LONG" },
4077 { config_parse_socket_service, "SERVICE" },
4078 #ifdef HAVE_SELINUX
4079 { config_parse_exec_selinux_context, "LABEL" },
4080 #endif
4081 { config_parse_job_mode, "MODE" },
4082 { config_parse_job_mode_isolate, "BOOLEAN" },
4083 { config_parse_personality, "PERSONALITY" },
4084 };
4085
4086 const char *prev = NULL;
4087 const char *i;
4088
4089 assert(f);
4090
4091 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
4092 const char *rvalue = "OTHER", *lvalue;
4093 unsigned j;
4094 size_t prefix_len;
4095 const char *dot;
4096 const ConfigPerfItem *p;
4097
4098 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
4099
4100 dot = strchr(i, '.');
4101 lvalue = dot ? dot + 1 : i;
4102 prefix_len = dot-i;
4103
4104 if (dot)
4105 if (!prev || !strneq(prev, i, prefix_len+1)) {
4106 if (prev)
4107 fputc('\n', f);
4108
4109 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
4110 }
4111
4112 for (j = 0; j < ELEMENTSOF(table); j++)
4113 if (p->parse == table[j].callback) {
4114 rvalue = table[j].rvalue;
4115 break;
4116 }
4117
4118 fprintf(f, "%s=%s\n", lvalue, rvalue);
4119 prev = i;
4120 }
4121 }