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