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