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