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