]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/load-fragment.c
cap-list: add capability_set_{from_string,to_string_alloc}()
[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 int r;
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 (streq(lvalue, "CapabilityBoundingSet"))
1173 initial = CAP_ALL; /* initialized to all bits on */
1174 /* else "AmbientCapabilities" initialized to all bits off */
1175
1176 r = capability_set_from_string(rvalue, &sum);
1177 if (r == -ENOMEM)
1178 return log_oom();
1179 if (r < 0) {
1180 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse word: %s", rvalue);
1181 return 0;
1182 }
1183
1184 sum = invert ? ~sum : sum;
1185
1186 if (sum == 0 || *capability_set == initial)
1187 /* "" or uninitialized data -> replace */
1188 *capability_set = sum;
1189 else
1190 /* previous data -> merge */
1191 *capability_set |= sum;
1192
1193 return 0;
1194 }
1195
1196 int config_parse_limit(
1197 const char *unit,
1198 const char *filename,
1199 unsigned line,
1200 const char *section,
1201 unsigned section_line,
1202 const char *lvalue,
1203 int ltype,
1204 const char *rvalue,
1205 void *data,
1206 void *userdata) {
1207
1208 struct rlimit **rl = data, d = {};
1209 int r;
1210
1211 assert(filename);
1212 assert(lvalue);
1213 assert(rvalue);
1214 assert(data);
1215
1216 r = rlimit_parse(ltype, rvalue, &d);
1217 if (r == -EILSEQ) {
1218 log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue);
1219 return 0;
1220 }
1221 if (r < 0) {
1222 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
1223 return 0;
1224 }
1225
1226 if (rl[ltype])
1227 *rl[ltype] = d;
1228 else {
1229 rl[ltype] = newdup(struct rlimit, &d, 1);
1230 if (!rl[ltype])
1231 return log_oom();
1232 }
1233
1234 return 0;
1235 }
1236
1237 #ifdef HAVE_SYSV_COMPAT
1238 int config_parse_sysv_priority(const char *unit,
1239 const char *filename,
1240 unsigned line,
1241 const char *section,
1242 unsigned section_line,
1243 const char *lvalue,
1244 int ltype,
1245 const char *rvalue,
1246 void *data,
1247 void *userdata) {
1248
1249 int *priority = data;
1250 int i, r;
1251
1252 assert(filename);
1253 assert(lvalue);
1254 assert(rvalue);
1255 assert(data);
1256
1257 r = safe_atoi(rvalue, &i);
1258 if (r < 0 || i < 0) {
1259 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue);
1260 return 0;
1261 }
1262
1263 *priority = (int) i;
1264 return 0;
1265 }
1266 #endif
1267
1268 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
1269 DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1270
1271 int config_parse_exec_mount_flags(
1272 const char *unit,
1273 const char *filename,
1274 unsigned line,
1275 const char *section,
1276 unsigned section_line,
1277 const char *lvalue,
1278 int ltype,
1279 const char *rvalue,
1280 void *data,
1281 void *userdata) {
1282
1283
1284 ExecContext *c = data;
1285 int r;
1286
1287 assert(filename);
1288 assert(lvalue);
1289 assert(rvalue);
1290 assert(data);
1291
1292 r = mount_propagation_flags_from_string(rvalue, &c->mount_flags);
1293 if (r < 0)
1294 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring.", rvalue);
1295
1296 return 0;
1297 }
1298
1299 int config_parse_exec_selinux_context(
1300 const char *unit,
1301 const char *filename,
1302 unsigned line,
1303 const char *section,
1304 unsigned section_line,
1305 const char *lvalue,
1306 int ltype,
1307 const char *rvalue,
1308 void *data,
1309 void *userdata) {
1310
1311 ExecContext *c = data;
1312 Unit *u = userdata;
1313 bool ignore;
1314 char *k;
1315 int r;
1316
1317 assert(filename);
1318 assert(lvalue);
1319 assert(rvalue);
1320 assert(data);
1321
1322 if (isempty(rvalue)) {
1323 c->selinux_context = mfree(c->selinux_context);
1324 c->selinux_context_ignore = false;
1325 return 0;
1326 }
1327
1328 if (rvalue[0] == '-') {
1329 ignore = true;
1330 rvalue++;
1331 } else
1332 ignore = false;
1333
1334 r = unit_full_printf(u, rvalue, &k);
1335 if (r < 0) {
1336 log_syntax(unit, LOG_ERR, filename, line, r,
1337 "Failed to resolve specifiers%s: %m",
1338 ignore ? ", ignoring" : "");
1339 return ignore ? 0 : -ENOEXEC;
1340 }
1341
1342 free(c->selinux_context);
1343 c->selinux_context = k;
1344 c->selinux_context_ignore = ignore;
1345
1346 return 0;
1347 }
1348
1349 int config_parse_exec_apparmor_profile(
1350 const char *unit,
1351 const char *filename,
1352 unsigned line,
1353 const char *section,
1354 unsigned section_line,
1355 const char *lvalue,
1356 int ltype,
1357 const char *rvalue,
1358 void *data,
1359 void *userdata) {
1360
1361 ExecContext *c = data;
1362 Unit *u = userdata;
1363 bool ignore;
1364 char *k;
1365 int r;
1366
1367 assert(filename);
1368 assert(lvalue);
1369 assert(rvalue);
1370 assert(data);
1371
1372 if (isempty(rvalue)) {
1373 c->apparmor_profile = mfree(c->apparmor_profile);
1374 c->apparmor_profile_ignore = false;
1375 return 0;
1376 }
1377
1378 if (rvalue[0] == '-') {
1379 ignore = true;
1380 rvalue++;
1381 } else
1382 ignore = false;
1383
1384 r = unit_full_printf(u, rvalue, &k);
1385 if (r < 0) {
1386 log_syntax(unit, LOG_ERR, filename, line, r,
1387 "Failed to resolve specifiers%s: %m",
1388 ignore ? ", ignoring" : "");
1389 return ignore ? 0 : -ENOEXEC;
1390 }
1391
1392 free(c->apparmor_profile);
1393 c->apparmor_profile = k;
1394 c->apparmor_profile_ignore = ignore;
1395
1396 return 0;
1397 }
1398
1399 int config_parse_exec_smack_process_label(
1400 const char *unit,
1401 const char *filename,
1402 unsigned line,
1403 const char *section,
1404 unsigned section_line,
1405 const char *lvalue,
1406 int ltype,
1407 const char *rvalue,
1408 void *data,
1409 void *userdata) {
1410
1411 ExecContext *c = data;
1412 Unit *u = userdata;
1413 bool ignore;
1414 char *k;
1415 int r;
1416
1417 assert(filename);
1418 assert(lvalue);
1419 assert(rvalue);
1420 assert(data);
1421
1422 if (isempty(rvalue)) {
1423 c->smack_process_label = mfree(c->smack_process_label);
1424 c->smack_process_label_ignore = false;
1425 return 0;
1426 }
1427
1428 if (rvalue[0] == '-') {
1429 ignore = true;
1430 rvalue++;
1431 } else
1432 ignore = false;
1433
1434 r = unit_full_printf(u, rvalue, &k);
1435 if (r < 0) {
1436 log_syntax(unit, LOG_ERR, filename, line, r,
1437 "Failed to resolve specifiers%s: %m",
1438 ignore ? ", ignoring" : "");
1439 return ignore ? 0 : -ENOEXEC;
1440 }
1441
1442 free(c->smack_process_label);
1443 c->smack_process_label = k;
1444 c->smack_process_label_ignore = ignore;
1445
1446 return 0;
1447 }
1448
1449 int config_parse_timer(const char *unit,
1450 const char *filename,
1451 unsigned line,
1452 const char *section,
1453 unsigned section_line,
1454 const char *lvalue,
1455 int ltype,
1456 const char *rvalue,
1457 void *data,
1458 void *userdata) {
1459
1460 Timer *t = data;
1461 usec_t usec = 0;
1462 TimerValue *v;
1463 TimerBase b;
1464 CalendarSpec *c = NULL;
1465 Unit *u = userdata;
1466 _cleanup_free_ char *k = NULL;
1467 int r;
1468
1469 assert(filename);
1470 assert(lvalue);
1471 assert(rvalue);
1472 assert(data);
1473
1474 if (isempty(rvalue)) {
1475 /* Empty assignment resets list */
1476 timer_free_values(t);
1477 return 0;
1478 }
1479
1480 b = timer_base_from_string(lvalue);
1481 if (b < 0) {
1482 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer base, ignoring: %s", lvalue);
1483 return 0;
1484 }
1485
1486 r = unit_full_printf(u, rvalue, &k);
1487 if (r < 0) {
1488 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
1489 return 0;
1490 }
1491
1492 if (b == TIMER_CALENDAR) {
1493 if (calendar_spec_from_string(k, &c) < 0) {
1494 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", k);
1495 return 0;
1496 }
1497 } else {
1498 if (parse_sec(k, &usec) < 0) {
1499 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", k);
1500 return 0;
1501 }
1502 }
1503
1504 v = new0(TimerValue, 1);
1505 if (!v) {
1506 calendar_spec_free(c);
1507 return log_oom();
1508 }
1509
1510 v->base = b;
1511 v->value = usec;
1512 v->calendar_spec = c;
1513
1514 LIST_PREPEND(value, t->values, v);
1515
1516 return 0;
1517 }
1518
1519 int config_parse_trigger_unit(
1520 const char *unit,
1521 const char *filename,
1522 unsigned line,
1523 const char *section,
1524 unsigned section_line,
1525 const char *lvalue,
1526 int ltype,
1527 const char *rvalue,
1528 void *data,
1529 void *userdata) {
1530
1531 _cleanup_free_ char *p = NULL;
1532 Unit *u = data;
1533 UnitType type;
1534 int r;
1535
1536 assert(filename);
1537 assert(lvalue);
1538 assert(rvalue);
1539 assert(data);
1540
1541 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1542 log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
1543 return 0;
1544 }
1545
1546 r = unit_name_printf(u, rvalue, &p);
1547 if (r < 0) {
1548 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1549 return 0;
1550 }
1551
1552 type = unit_name_to_type(p);
1553 if (type < 0) {
1554 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue);
1555 return 0;
1556 }
1557
1558 if (type == u->type) {
1559 log_syntax(unit, LOG_ERR, filename, line, 0, "Trigger cannot be of same type, ignoring: %s", rvalue);
1560 return 0;
1561 }
1562
1563 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
1564 if (r < 0) {
1565 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
1566 return 0;
1567 }
1568
1569 return 0;
1570 }
1571
1572 int config_parse_path_spec(const char *unit,
1573 const char *filename,
1574 unsigned line,
1575 const char *section,
1576 unsigned section_line,
1577 const char *lvalue,
1578 int ltype,
1579 const char *rvalue,
1580 void *data,
1581 void *userdata) {
1582
1583 Path *p = data;
1584 PathSpec *s;
1585 PathType b;
1586 _cleanup_free_ char *k = NULL;
1587 int r;
1588
1589 assert(filename);
1590 assert(lvalue);
1591 assert(rvalue);
1592 assert(data);
1593
1594 if (isempty(rvalue)) {
1595 /* Empty assignment clears list */
1596 path_free_specs(p);
1597 return 0;
1598 }
1599
1600 b = path_type_from_string(lvalue);
1601 if (b < 0) {
1602 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue);
1603 return 0;
1604 }
1605
1606 r = unit_full_printf(UNIT(p), rvalue, &k);
1607 if (r < 0) {
1608 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
1609 return 0;
1610 }
1611
1612 if (!path_is_absolute(k)) {
1613 log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k);
1614 return 0;
1615 }
1616
1617 s = new0(PathSpec, 1);
1618 if (!s)
1619 return log_oom();
1620
1621 s->unit = UNIT(p);
1622 s->path = path_kill_slashes(k);
1623 k = NULL;
1624 s->type = b;
1625 s->inotify_fd = -1;
1626
1627 LIST_PREPEND(spec, p->specs, s);
1628
1629 return 0;
1630 }
1631
1632 int config_parse_socket_service(
1633 const char *unit,
1634 const char *filename,
1635 unsigned line,
1636 const char *section,
1637 unsigned section_line,
1638 const char *lvalue,
1639 int ltype,
1640 const char *rvalue,
1641 void *data,
1642 void *userdata) {
1643
1644 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1645 _cleanup_free_ char *p = NULL;
1646 Socket *s = data;
1647 Unit *x;
1648 int r;
1649
1650 assert(filename);
1651 assert(lvalue);
1652 assert(rvalue);
1653 assert(data);
1654
1655 r = unit_name_printf(UNIT(s), rvalue, &p);
1656 if (r < 0) {
1657 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", rvalue);
1658 return -ENOEXEC;
1659 }
1660
1661 if (!endswith(p, ".service")) {
1662 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service: %s", rvalue);
1663 return -ENOEXEC;
1664 }
1665
1666 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1667 if (r < 0) {
1668 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s: %s", rvalue, bus_error_message(&error, r));
1669 return -ENOEXEC;
1670 }
1671
1672 unit_ref_set(&s->service, x);
1673
1674 return 0;
1675 }
1676
1677 int config_parse_fdname(
1678 const char *unit,
1679 const char *filename,
1680 unsigned line,
1681 const char *section,
1682 unsigned section_line,
1683 const char *lvalue,
1684 int ltype,
1685 const char *rvalue,
1686 void *data,
1687 void *userdata) {
1688
1689 _cleanup_free_ char *p = NULL;
1690 Socket *s = data;
1691 int r;
1692
1693 assert(filename);
1694 assert(lvalue);
1695 assert(rvalue);
1696 assert(data);
1697
1698 if (isempty(rvalue)) {
1699 s->fdname = mfree(s->fdname);
1700 return 0;
1701 }
1702
1703 r = unit_full_printf(UNIT(s), rvalue, &p);
1704 if (r < 0) {
1705 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1706 return 0;
1707 }
1708
1709 if (!fdname_is_valid(p)) {
1710 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
1711 return 0;
1712 }
1713
1714 return free_and_replace(s->fdname, p);
1715 }
1716
1717 int config_parse_service_sockets(
1718 const char *unit,
1719 const char *filename,
1720 unsigned line,
1721 const char *section,
1722 unsigned section_line,
1723 const char *lvalue,
1724 int ltype,
1725 const char *rvalue,
1726 void *data,
1727 void *userdata) {
1728
1729 Service *s = data;
1730 const char *p;
1731 int r;
1732
1733 assert(filename);
1734 assert(lvalue);
1735 assert(rvalue);
1736 assert(data);
1737
1738 p = rvalue;
1739 for (;;) {
1740 _cleanup_free_ char *word = NULL, *k = NULL;
1741
1742 r = extract_first_word(&p, &word, NULL, 0);
1743 if (r == 0)
1744 break;
1745 if (r == -ENOMEM)
1746 return log_oom();
1747 if (r < 0) {
1748 log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
1749 break;
1750 }
1751
1752 r = unit_name_printf(UNIT(s), word, &k);
1753 if (r < 0) {
1754 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1755 continue;
1756 }
1757
1758 if (!endswith(k, ".socket")) {
1759 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
1760 continue;
1761 }
1762
1763 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
1764 if (r < 0)
1765 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
1766
1767 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
1768 if (r < 0)
1769 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
1770 }
1771
1772 return 0;
1773 }
1774
1775 int config_parse_bus_name(
1776 const char *unit,
1777 const char *filename,
1778 unsigned line,
1779 const char *section,
1780 unsigned section_line,
1781 const char *lvalue,
1782 int ltype,
1783 const char *rvalue,
1784 void *data,
1785 void *userdata) {
1786
1787 _cleanup_free_ char *k = NULL;
1788 Unit *u = userdata;
1789 int r;
1790
1791 assert(filename);
1792 assert(lvalue);
1793 assert(rvalue);
1794 assert(u);
1795
1796 r = unit_full_printf(u, rvalue, &k);
1797 if (r < 0) {
1798 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
1799 return 0;
1800 }
1801
1802 if (!service_name_is_valid(k)) {
1803 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k);
1804 return 0;
1805 }
1806
1807 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
1808 }
1809
1810 int config_parse_service_timeout(
1811 const char *unit,
1812 const char *filename,
1813 unsigned line,
1814 const char *section,
1815 unsigned section_line,
1816 const char *lvalue,
1817 int ltype,
1818 const char *rvalue,
1819 void *data,
1820 void *userdata) {
1821
1822 Service *s = userdata;
1823 usec_t usec;
1824 int r;
1825
1826 assert(filename);
1827 assert(lvalue);
1828 assert(rvalue);
1829 assert(s);
1830
1831 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
1832
1833 r = parse_sec(rvalue, &usec);
1834 if (r < 0) {
1835 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
1836 return 0;
1837 }
1838
1839 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1840 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1841 * all other timeouts. */
1842 if (usec <= 0)
1843 usec = USEC_INFINITY;
1844
1845 if (!streq(lvalue, "TimeoutStopSec")) {
1846 s->start_timeout_defined = true;
1847 s->timeout_start_usec = usec;
1848 }
1849
1850 if (!streq(lvalue, "TimeoutStartSec"))
1851 s->timeout_stop_usec = usec;
1852
1853 return 0;
1854 }
1855
1856 int config_parse_sec_fix_0(
1857 const char *unit,
1858 const char *filename,
1859 unsigned line,
1860 const char *section,
1861 unsigned section_line,
1862 const char *lvalue,
1863 int ltype,
1864 const char *rvalue,
1865 void *data,
1866 void *userdata) {
1867
1868 usec_t *usec = data;
1869 int r;
1870
1871 assert(filename);
1872 assert(lvalue);
1873 assert(rvalue);
1874 assert(usec);
1875
1876 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1877 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1878 * timeout. */
1879
1880 r = parse_sec_fix_0(rvalue, usec);
1881 if (r < 0) {
1882 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
1883 return 0;
1884 }
1885
1886 return 0;
1887 }
1888
1889 int config_parse_user_group(
1890 const char *unit,
1891 const char *filename,
1892 unsigned line,
1893 const char *section,
1894 unsigned section_line,
1895 const char *lvalue,
1896 int ltype,
1897 const char *rvalue,
1898 void *data,
1899 void *userdata) {
1900
1901 char **user = data, *n;
1902 Unit *u = userdata;
1903 int r;
1904
1905 assert(filename);
1906 assert(lvalue);
1907 assert(rvalue);
1908 assert(u);
1909
1910 if (isempty(rvalue))
1911 n = NULL;
1912 else {
1913 _cleanup_free_ char *k = NULL;
1914
1915 r = unit_full_printf(u, rvalue, &k);
1916 if (r < 0) {
1917 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue);
1918 return -ENOEXEC;
1919 }
1920
1921 if (!valid_user_group_name_or_id(k)) {
1922 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
1923 return -ENOEXEC;
1924 }
1925
1926 n = k;
1927 k = NULL;
1928 }
1929
1930 free(*user);
1931 *user = n;
1932
1933 return 0;
1934 }
1935
1936 int config_parse_user_group_strv(
1937 const char *unit,
1938 const char *filename,
1939 unsigned line,
1940 const char *section,
1941 unsigned section_line,
1942 const char *lvalue,
1943 int ltype,
1944 const char *rvalue,
1945 void *data,
1946 void *userdata) {
1947
1948 char ***users = data;
1949 Unit *u = userdata;
1950 const char *p;
1951 int r;
1952
1953 assert(filename);
1954 assert(lvalue);
1955 assert(rvalue);
1956 assert(u);
1957
1958 if (isempty(rvalue)) {
1959 char **empty;
1960
1961 empty = new0(char*, 1);
1962 if (!empty)
1963 return log_oom();
1964
1965 strv_free(*users);
1966 *users = empty;
1967
1968 return 0;
1969 }
1970
1971 p = rvalue;
1972 for (;;) {
1973 _cleanup_free_ char *word = NULL, *k = NULL;
1974
1975 r = extract_first_word(&p, &word, NULL, 0);
1976 if (r == 0)
1977 break;
1978 if (r == -ENOMEM)
1979 return log_oom();
1980 if (r < 0) {
1981 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue);
1982 return -ENOEXEC;
1983 }
1984
1985 r = unit_full_printf(u, word, &k);
1986 if (r < 0) {
1987 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word);
1988 return -ENOEXEC;
1989 }
1990
1991 if (!valid_user_group_name_or_id(k)) {
1992 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
1993 return -ENOEXEC;
1994 }
1995
1996 r = strv_push(users, k);
1997 if (r < 0)
1998 return log_oom();
1999
2000 k = NULL;
2001 }
2002
2003 return 0;
2004 }
2005
2006 int config_parse_working_directory(
2007 const char *unit,
2008 const char *filename,
2009 unsigned line,
2010 const char *section,
2011 unsigned section_line,
2012 const char *lvalue,
2013 int ltype,
2014 const char *rvalue,
2015 void *data,
2016 void *userdata) {
2017
2018 ExecContext *c = data;
2019 Unit *u = userdata;
2020 bool missing_ok;
2021 int r;
2022
2023 assert(filename);
2024 assert(lvalue);
2025 assert(rvalue);
2026 assert(c);
2027 assert(u);
2028
2029 if (rvalue[0] == '-') {
2030 missing_ok = true;
2031 rvalue++;
2032 } else
2033 missing_ok = false;
2034
2035 if (streq(rvalue, "~")) {
2036 c->working_directory_home = true;
2037 c->working_directory = mfree(c->working_directory);
2038 } else {
2039 _cleanup_free_ char *k = NULL;
2040
2041 r = unit_full_printf(u, rvalue, &k);
2042 if (r < 0) {
2043 log_syntax(unit, LOG_ERR, filename, line, r,
2044 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2045 rvalue, missing_ok ? ", ignoring" : "");
2046 return missing_ok ? 0 : -ENOEXEC;
2047 }
2048
2049 path_kill_slashes(k);
2050
2051 if (!utf8_is_valid(k)) {
2052 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
2053 return missing_ok ? 0 : -ENOEXEC;
2054 }
2055
2056 if (!path_is_absolute(k)) {
2057 log_syntax(unit, LOG_ERR, filename, line, 0,
2058 "Working directory path '%s' is not absolute%s.",
2059 rvalue, missing_ok ? ", ignoring" : "");
2060 return missing_ok ? 0 : -ENOEXEC;
2061 }
2062
2063 c->working_directory_home = false;
2064 free_and_replace(c->working_directory, k);
2065 }
2066
2067 c->working_directory_missing_ok = missing_ok;
2068 return 0;
2069 }
2070
2071 int config_parse_unit_env_file(const char *unit,
2072 const char *filename,
2073 unsigned line,
2074 const char *section,
2075 unsigned section_line,
2076 const char *lvalue,
2077 int ltype,
2078 const char *rvalue,
2079 void *data,
2080 void *userdata) {
2081
2082 char ***env = data;
2083 Unit *u = userdata;
2084 _cleanup_free_ char *n = NULL;
2085 int r;
2086
2087 assert(filename);
2088 assert(lvalue);
2089 assert(rvalue);
2090 assert(data);
2091
2092 if (isempty(rvalue)) {
2093 /* Empty assignment frees the list */
2094 *env = strv_free(*env);
2095 return 0;
2096 }
2097
2098 r = unit_full_printf(u, rvalue, &n);
2099 if (r < 0) {
2100 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2101 return 0;
2102 }
2103
2104 if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) {
2105 log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n);
2106 return 0;
2107 }
2108
2109 r = strv_extend(env, n);
2110 if (r < 0)
2111 return log_oom();
2112
2113 return 0;
2114 }
2115
2116 int config_parse_environ(const char *unit,
2117 const char *filename,
2118 unsigned line,
2119 const char *section,
2120 unsigned section_line,
2121 const char *lvalue,
2122 int ltype,
2123 const char *rvalue,
2124 void *data,
2125 void *userdata) {
2126
2127 Unit *u = userdata;
2128 char ***env = data;
2129 const char *p;
2130 int r;
2131
2132 assert(filename);
2133 assert(lvalue);
2134 assert(rvalue);
2135 assert(data);
2136
2137 if (isempty(rvalue)) {
2138 /* Empty assignment resets the list */
2139 *env = strv_free(*env);
2140 return 0;
2141 }
2142
2143 for (p = rvalue;; ) {
2144 _cleanup_free_ char *word = NULL, *k = NULL;
2145
2146 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
2147 if (r == 0)
2148 return 0;
2149 if (r == -ENOMEM)
2150 return log_oom();
2151 if (r < 0) {
2152 log_syntax(unit, LOG_WARNING, filename, line, r,
2153 "Invalid syntax, ignoring: %s", rvalue);
2154 return 0;
2155 }
2156
2157 if (u) {
2158 r = unit_full_printf(u, word, &k);
2159 if (r < 0) {
2160 log_syntax(unit, LOG_ERR, filename, line, r,
2161 "Failed to resolve specifiers, ignoring: %s", k);
2162 continue;
2163 }
2164 } else {
2165 k = word;
2166 word = NULL;
2167 }
2168
2169 if (!env_assignment_is_valid(k)) {
2170 log_syntax(unit, LOG_ERR, filename, line, 0,
2171 "Invalid environment assignment, ignoring: %s", k);
2172 continue;
2173 }
2174
2175 r = strv_env_replace(env, k);
2176 if (r < 0)
2177 return log_oom();
2178 k = NULL;
2179 }
2180 }
2181
2182 int config_parse_pass_environ(const char *unit,
2183 const char *filename,
2184 unsigned line,
2185 const char *section,
2186 unsigned section_line,
2187 const char *lvalue,
2188 int ltype,
2189 const char *rvalue,
2190 void *data,
2191 void *userdata) {
2192
2193 const char *whole_rvalue = rvalue;
2194 char*** passenv = data;
2195 _cleanup_strv_free_ char **n = NULL;
2196 size_t nlen = 0, nbufsize = 0;
2197 int r;
2198
2199 assert(filename);
2200 assert(lvalue);
2201 assert(rvalue);
2202 assert(data);
2203
2204 if (isempty(rvalue)) {
2205 /* Empty assignment resets the list */
2206 *passenv = strv_free(*passenv);
2207 return 0;
2208 }
2209
2210 for (;;) {
2211 _cleanup_free_ char *word = NULL;
2212
2213 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
2214 if (r == 0)
2215 break;
2216 if (r == -ENOMEM)
2217 return log_oom();
2218 if (r < 0) {
2219 log_syntax(unit, LOG_ERR, filename, line, r,
2220 "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
2221 break;
2222 }
2223
2224 if (!env_name_is_valid(word)) {
2225 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2226 "Invalid environment name for %s, ignoring: %s", lvalue, word);
2227 continue;
2228 }
2229
2230 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2231 return log_oom();
2232 n[nlen++] = word;
2233 n[nlen] = NULL;
2234 word = NULL;
2235 }
2236
2237 if (n) {
2238 r = strv_extend_strv(passenv, n, true);
2239 if (r < 0)
2240 return r;
2241 }
2242
2243 return 0;
2244 }
2245
2246 int config_parse_ip_tos(const char *unit,
2247 const char *filename,
2248 unsigned line,
2249 const char *section,
2250 unsigned section_line,
2251 const char *lvalue,
2252 int ltype,
2253 const char *rvalue,
2254 void *data,
2255 void *userdata) {
2256
2257 int *ip_tos = data, x;
2258
2259 assert(filename);
2260 assert(lvalue);
2261 assert(rvalue);
2262 assert(data);
2263
2264 x = ip_tos_from_string(rvalue);
2265 if (x < 0) {
2266 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue);
2267 return 0;
2268 }
2269
2270 *ip_tos = x;
2271 return 0;
2272 }
2273
2274 int config_parse_unit_condition_path(
2275 const char *unit,
2276 const char *filename,
2277 unsigned line,
2278 const char *section,
2279 unsigned section_line,
2280 const char *lvalue,
2281 int ltype,
2282 const char *rvalue,
2283 void *data,
2284 void *userdata) {
2285
2286 _cleanup_free_ char *p = NULL;
2287 Condition **list = data, *c;
2288 ConditionType t = ltype;
2289 bool trigger, negate;
2290 Unit *u = userdata;
2291 int r;
2292
2293 assert(filename);
2294 assert(lvalue);
2295 assert(rvalue);
2296 assert(data);
2297
2298 if (isempty(rvalue)) {
2299 /* Empty assignment resets the list */
2300 *list = condition_free_list(*list);
2301 return 0;
2302 }
2303
2304 trigger = rvalue[0] == '|';
2305 if (trigger)
2306 rvalue++;
2307
2308 negate = rvalue[0] == '!';
2309 if (negate)
2310 rvalue++;
2311
2312 r = unit_full_printf(u, rvalue, &p);
2313 if (r < 0) {
2314 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2315 return 0;
2316 }
2317
2318 if (!path_is_absolute(p)) {
2319 log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p);
2320 return 0;
2321 }
2322
2323 c = condition_new(t, p, trigger, negate);
2324 if (!c)
2325 return log_oom();
2326
2327 LIST_PREPEND(conditions, *list, c);
2328 return 0;
2329 }
2330
2331 int config_parse_unit_condition_string(
2332 const char *unit,
2333 const char *filename,
2334 unsigned line,
2335 const char *section,
2336 unsigned section_line,
2337 const char *lvalue,
2338 int ltype,
2339 const char *rvalue,
2340 void *data,
2341 void *userdata) {
2342
2343 _cleanup_free_ char *s = NULL;
2344 Condition **list = data, *c;
2345 ConditionType t = ltype;
2346 bool trigger, negate;
2347 Unit *u = userdata;
2348 int r;
2349
2350 assert(filename);
2351 assert(lvalue);
2352 assert(rvalue);
2353 assert(data);
2354
2355 if (isempty(rvalue)) {
2356 /* Empty assignment resets the list */
2357 *list = condition_free_list(*list);
2358 return 0;
2359 }
2360
2361 trigger = rvalue[0] == '|';
2362 if (trigger)
2363 rvalue++;
2364
2365 negate = rvalue[0] == '!';
2366 if (negate)
2367 rvalue++;
2368
2369 r = unit_full_printf(u, rvalue, &s);
2370 if (r < 0) {
2371 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2372 return 0;
2373 }
2374
2375 c = condition_new(t, s, trigger, negate);
2376 if (!c)
2377 return log_oom();
2378
2379 LIST_PREPEND(conditions, *list, c);
2380 return 0;
2381 }
2382
2383 int config_parse_unit_condition_null(
2384 const char *unit,
2385 const char *filename,
2386 unsigned line,
2387 const char *section,
2388 unsigned section_line,
2389 const char *lvalue,
2390 int ltype,
2391 const char *rvalue,
2392 void *data,
2393 void *userdata) {
2394
2395 Condition **list = data, *c;
2396 bool trigger, negate;
2397 int b;
2398
2399 assert(filename);
2400 assert(lvalue);
2401 assert(rvalue);
2402 assert(data);
2403
2404 if (isempty(rvalue)) {
2405 /* Empty assignment resets the list */
2406 *list = condition_free_list(*list);
2407 return 0;
2408 }
2409
2410 trigger = rvalue[0] == '|';
2411 if (trigger)
2412 rvalue++;
2413
2414 negate = rvalue[0] == '!';
2415 if (negate)
2416 rvalue++;
2417
2418 b = parse_boolean(rvalue);
2419 if (b < 0) {
2420 log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
2421 return 0;
2422 }
2423
2424 if (!b)
2425 negate = !negate;
2426
2427 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2428 if (!c)
2429 return log_oom();
2430
2431 LIST_PREPEND(conditions, *list, c);
2432 return 0;
2433 }
2434
2435 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
2436 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier");
2437
2438 int config_parse_unit_requires_mounts_for(
2439 const char *unit,
2440 const char *filename,
2441 unsigned line,
2442 const char *section,
2443 unsigned section_line,
2444 const char *lvalue,
2445 int ltype,
2446 const char *rvalue,
2447 void *data,
2448 void *userdata) {
2449
2450 Unit *u = userdata;
2451 const char *p;
2452 int r;
2453
2454 assert(filename);
2455 assert(lvalue);
2456 assert(rvalue);
2457 assert(data);
2458
2459 for (p = rvalue;; ) {
2460 _cleanup_free_ char *word = NULL, *resolved = NULL;
2461
2462 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2463 if (r == 0)
2464 return 0;
2465 if (r == -ENOMEM)
2466 return log_oom();
2467 if (r < 0) {
2468 log_syntax(unit, LOG_WARNING, filename, line, r,
2469 "Invalid syntax, ignoring: %s", rvalue);
2470 return 0;
2471 }
2472
2473 if (!utf8_is_valid(word)) {
2474 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
2475 continue;
2476 }
2477
2478 r = unit_full_printf(u, word, &resolved);
2479 if (r < 0) {
2480 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit name \"%s\", ignoring: %m", word);
2481 continue;
2482 }
2483
2484 r = unit_require_mounts_for(u, resolved);
2485 if (r < 0) {
2486 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount \"%s\", ignoring: %m", resolved);
2487 continue;
2488 }
2489 }
2490 }
2491
2492 int config_parse_documentation(const char *unit,
2493 const char *filename,
2494 unsigned line,
2495 const char *section,
2496 unsigned section_line,
2497 const char *lvalue,
2498 int ltype,
2499 const char *rvalue,
2500 void *data,
2501 void *userdata) {
2502
2503 Unit *u = userdata;
2504 int r;
2505 char **a, **b;
2506
2507 assert(filename);
2508 assert(lvalue);
2509 assert(rvalue);
2510 assert(u);
2511
2512 if (isempty(rvalue)) {
2513 /* Empty assignment resets the list */
2514 u->documentation = strv_free(u->documentation);
2515 return 0;
2516 }
2517
2518 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
2519 rvalue, data, userdata);
2520 if (r < 0)
2521 return r;
2522
2523 for (a = b = u->documentation; a && *a; a++) {
2524
2525 if (documentation_url_is_valid(*a))
2526 *(b++) = *a;
2527 else {
2528 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a);
2529 free(*a);
2530 }
2531 }
2532 if (b)
2533 *b = NULL;
2534
2535 return r;
2536 }
2537
2538 #ifdef HAVE_SECCOMP
2539
2540 static int syscall_filter_parse_one(
2541 const char *unit,
2542 const char *filename,
2543 unsigned line,
2544 ExecContext *c,
2545 bool invert,
2546 const char *t,
2547 bool warn) {
2548 int r;
2549
2550 if (t[0] == '@') {
2551 const SyscallFilterSet *set;
2552 const char *i;
2553
2554 set = syscall_filter_set_find(t);
2555 if (!set) {
2556 if (warn)
2557 log_syntax(unit, LOG_WARNING, filename, line, 0, "Don't know system call group, ignoring: %s", t);
2558 return 0;
2559 }
2560
2561 NULSTR_FOREACH(i, set->value) {
2562 r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false);
2563 if (r < 0)
2564 return r;
2565 }
2566 } else {
2567 int id;
2568
2569 id = seccomp_syscall_resolve_name(t);
2570 if (id == __NR_SCMP_ERROR) {
2571 if (warn)
2572 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
2573 return 0;
2574 }
2575
2576 /* If we previously wanted to forbid a syscall and now
2577 * we want to allow it, then remove it from the list
2578 */
2579 if (!invert == c->syscall_whitelist) {
2580 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2581 if (r == 0)
2582 return 0;
2583 if (r < 0)
2584 return log_oom();
2585 } else
2586 (void) set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
2587 }
2588
2589 return 0;
2590 }
2591
2592 int config_parse_syscall_filter(
2593 const char *unit,
2594 const char *filename,
2595 unsigned line,
2596 const char *section,
2597 unsigned section_line,
2598 const char *lvalue,
2599 int ltype,
2600 const char *rvalue,
2601 void *data,
2602 void *userdata) {
2603
2604 ExecContext *c = data;
2605 Unit *u = userdata;
2606 bool invert = false;
2607 const char *p;
2608 int r;
2609
2610 assert(filename);
2611 assert(lvalue);
2612 assert(rvalue);
2613 assert(u);
2614
2615 if (isempty(rvalue)) {
2616 /* Empty assignment resets the list */
2617 c->syscall_filter = set_free(c->syscall_filter);
2618 c->syscall_whitelist = false;
2619 return 0;
2620 }
2621
2622 if (rvalue[0] == '~') {
2623 invert = true;
2624 rvalue++;
2625 }
2626
2627 if (!c->syscall_filter) {
2628 c->syscall_filter = set_new(NULL);
2629 if (!c->syscall_filter)
2630 return log_oom();
2631
2632 if (invert)
2633 /* Allow everything but the ones listed */
2634 c->syscall_whitelist = false;
2635 else {
2636 /* Allow nothing but the ones listed */
2637 c->syscall_whitelist = true;
2638
2639 /* Accept default syscalls if we are on a whitelist */
2640 r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false);
2641 if (r < 0)
2642 return r;
2643 }
2644 }
2645
2646 p = rvalue;
2647 for (;;) {
2648 _cleanup_free_ char *word = NULL;
2649
2650 r = extract_first_word(&p, &word, NULL, 0);
2651 if (r == 0)
2652 break;
2653 if (r == -ENOMEM)
2654 return log_oom();
2655 if (r < 0) {
2656 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
2657 break;
2658 }
2659
2660 r = syscall_filter_parse_one(unit, filename, line, c, invert, word, true);
2661 if (r < 0)
2662 return r;
2663 }
2664
2665 return 0;
2666 }
2667
2668 int config_parse_syscall_archs(
2669 const char *unit,
2670 const char *filename,
2671 unsigned line,
2672 const char *section,
2673 unsigned section_line,
2674 const char *lvalue,
2675 int ltype,
2676 const char *rvalue,
2677 void *data,
2678 void *userdata) {
2679
2680 Set **archs = data;
2681 const char *p;
2682 int r;
2683
2684 if (isempty(rvalue)) {
2685 *archs = set_free(*archs);
2686 return 0;
2687 }
2688
2689 r = set_ensure_allocated(archs, NULL);
2690 if (r < 0)
2691 return log_oom();
2692
2693 for (p = rvalue;;) {
2694 _cleanup_free_ char *word = NULL;
2695 uint32_t a;
2696
2697 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2698 if (r == 0)
2699 return 0;
2700 if (r == -ENOMEM)
2701 return log_oom();
2702 if (r < 0) {
2703 log_syntax(unit, LOG_WARNING, filename, line, r,
2704 "Invalid syntax, ignoring: %s", rvalue);
2705 return 0;
2706 }
2707
2708 r = seccomp_arch_from_string(word, &a);
2709 if (r < 0) {
2710 log_syntax(unit, LOG_ERR, filename, line, r,
2711 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
2712 continue;
2713 }
2714
2715 r = set_put(*archs, UINT32_TO_PTR(a + 1));
2716 if (r < 0)
2717 return log_oom();
2718 }
2719 }
2720
2721 int config_parse_syscall_errno(
2722 const char *unit,
2723 const char *filename,
2724 unsigned line,
2725 const char *section,
2726 unsigned section_line,
2727 const char *lvalue,
2728 int ltype,
2729 const char *rvalue,
2730 void *data,
2731 void *userdata) {
2732
2733 ExecContext *c = data;
2734 int e;
2735
2736 assert(filename);
2737 assert(lvalue);
2738 assert(rvalue);
2739
2740 if (isempty(rvalue)) {
2741 /* Empty assignment resets to KILL */
2742 c->syscall_errno = 0;
2743 return 0;
2744 }
2745
2746 e = errno_from_name(rvalue);
2747 if (e < 0) {
2748 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
2749 return 0;
2750 }
2751
2752 c->syscall_errno = e;
2753 return 0;
2754 }
2755
2756 int config_parse_address_families(
2757 const char *unit,
2758 const char *filename,
2759 unsigned line,
2760 const char *section,
2761 unsigned section_line,
2762 const char *lvalue,
2763 int ltype,
2764 const char *rvalue,
2765 void *data,
2766 void *userdata) {
2767
2768 ExecContext *c = data;
2769 bool invert = false;
2770 const char *p;
2771 int r;
2772
2773 assert(filename);
2774 assert(lvalue);
2775 assert(rvalue);
2776
2777 if (isempty(rvalue)) {
2778 /* Empty assignment resets the list */
2779 c->address_families = set_free(c->address_families);
2780 c->address_families_whitelist = false;
2781 return 0;
2782 }
2783
2784 if (rvalue[0] == '~') {
2785 invert = true;
2786 rvalue++;
2787 }
2788
2789 if (!c->address_families) {
2790 c->address_families = set_new(NULL);
2791 if (!c->address_families)
2792 return log_oom();
2793
2794 c->address_families_whitelist = !invert;
2795 }
2796
2797 for (p = rvalue;;) {
2798 _cleanup_free_ char *word = NULL;
2799 int af;
2800
2801 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2802 if (r == 0)
2803 return 0;
2804 if (r == -ENOMEM)
2805 return log_oom();
2806 if (r < 0) {
2807 log_syntax(unit, LOG_WARNING, filename, line, r,
2808 "Invalid syntax, ignoring: %s", rvalue);
2809 return 0;
2810 }
2811
2812 af = af_from_name(word);
2813 if (af <= 0) {
2814 log_syntax(unit, LOG_ERR, filename, line, 0,
2815 "Failed to parse address family \"%s\", ignoring: %m", word);
2816 continue;
2817 }
2818
2819 /* If we previously wanted to forbid an address family and now
2820 * we want to allow it, then just remove it from the list.
2821 */
2822 if (!invert == c->address_families_whitelist) {
2823 r = set_put(c->address_families, INT_TO_PTR(af));
2824 if (r < 0)
2825 return log_oom();
2826 } else
2827 set_remove(c->address_families, INT_TO_PTR(af));
2828 }
2829 }
2830
2831 int config_parse_restrict_namespaces(
2832 const char *unit,
2833 const char *filename,
2834 unsigned line,
2835 const char *section,
2836 unsigned section_line,
2837 const char *lvalue,
2838 int ltype,
2839 const char *rvalue,
2840 void *data,
2841 void *userdata) {
2842
2843 ExecContext *c = data;
2844 bool invert = false;
2845 int r;
2846
2847 if (isempty(rvalue)) {
2848 /* Reset to the default. */
2849 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
2850 return 0;
2851 }
2852
2853 if (rvalue[0] == '~') {
2854 invert = true;
2855 rvalue++;
2856 }
2857
2858 r = parse_boolean(rvalue);
2859 if (r > 0)
2860 c->restrict_namespaces = 0;
2861 else if (r == 0)
2862 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
2863 else {
2864 /* Not a boolean argument, in this case it's a list of namespace types. */
2865
2866 r = namespace_flag_from_string_many(rvalue, &c->restrict_namespaces);
2867 if (r < 0) {
2868 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
2869 return 0;
2870 }
2871 }
2872
2873 if (invert)
2874 c->restrict_namespaces = (~c->restrict_namespaces) & NAMESPACE_FLAGS_ALL;
2875
2876 return 0;
2877 }
2878 #endif
2879
2880 int config_parse_unit_slice(
2881 const char *unit,
2882 const char *filename,
2883 unsigned line,
2884 const char *section,
2885 unsigned section_line,
2886 const char *lvalue,
2887 int ltype,
2888 const char *rvalue,
2889 void *data,
2890 void *userdata) {
2891
2892 _cleanup_free_ char *k = NULL;
2893 Unit *u = userdata, *slice = NULL;
2894 int r;
2895
2896 assert(filename);
2897 assert(lvalue);
2898 assert(rvalue);
2899 assert(u);
2900
2901 r = unit_name_printf(u, rvalue, &k);
2902 if (r < 0) {
2903 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2904 return 0;
2905 }
2906
2907 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
2908 if (r < 0) {
2909 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k);
2910 return 0;
2911 }
2912
2913 r = unit_set_slice(u, slice);
2914 if (r < 0) {
2915 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id);
2916 return 0;
2917 }
2918
2919 return 0;
2920 }
2921
2922 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2923
2924 int config_parse_cpu_weight(
2925 const char *unit,
2926 const char *filename,
2927 unsigned line,
2928 const char *section,
2929 unsigned section_line,
2930 const char *lvalue,
2931 int ltype,
2932 const char *rvalue,
2933 void *data,
2934 void *userdata) {
2935
2936 uint64_t *weight = data;
2937 int r;
2938
2939 assert(filename);
2940 assert(lvalue);
2941 assert(rvalue);
2942
2943 r = cg_weight_parse(rvalue, weight);
2944 if (r < 0) {
2945 log_syntax(unit, LOG_ERR, filename, line, r, "CPU weight '%s' invalid. Ignoring.", rvalue);
2946 return 0;
2947 }
2948
2949 return 0;
2950 }
2951
2952 int config_parse_cpu_shares(
2953 const char *unit,
2954 const char *filename,
2955 unsigned line,
2956 const char *section,
2957 unsigned section_line,
2958 const char *lvalue,
2959 int ltype,
2960 const char *rvalue,
2961 void *data,
2962 void *userdata) {
2963
2964 uint64_t *shares = data;
2965 int r;
2966
2967 assert(filename);
2968 assert(lvalue);
2969 assert(rvalue);
2970
2971 r = cg_cpu_shares_parse(rvalue, shares);
2972 if (r < 0) {
2973 log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
2974 return 0;
2975 }
2976
2977 return 0;
2978 }
2979
2980 int config_parse_cpu_quota(
2981 const char *unit,
2982 const char *filename,
2983 unsigned line,
2984 const char *section,
2985 unsigned section_line,
2986 const char *lvalue,
2987 int ltype,
2988 const char *rvalue,
2989 void *data,
2990 void *userdata) {
2991
2992 CGroupContext *c = data;
2993 int r;
2994
2995 assert(filename);
2996 assert(lvalue);
2997 assert(rvalue);
2998
2999 if (isempty(rvalue)) {
3000 c->cpu_quota_per_sec_usec = USEC_INFINITY;
3001 return 0;
3002 }
3003
3004 r = parse_percent_unbounded(rvalue);
3005 if (r <= 0) {
3006 log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
3007 return 0;
3008 }
3009
3010 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
3011 return 0;
3012 }
3013
3014 int config_parse_memory_limit(
3015 const char *unit,
3016 const char *filename,
3017 unsigned line,
3018 const char *section,
3019 unsigned section_line,
3020 const char *lvalue,
3021 int ltype,
3022 const char *rvalue,
3023 void *data,
3024 void *userdata) {
3025
3026 CGroupContext *c = data;
3027 uint64_t bytes = CGROUP_LIMIT_MAX;
3028 int r;
3029
3030 if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
3031
3032 r = parse_percent(rvalue);
3033 if (r < 0) {
3034 r = parse_size(rvalue, 1024, &bytes);
3035 if (r < 0) {
3036 log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
3037 return 0;
3038 }
3039 } else
3040 bytes = physical_memory_scale(r, 100U);
3041
3042 if (bytes <= 0 || bytes >= UINT64_MAX) {
3043 log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range. Ignoring.", rvalue);
3044 return 0;
3045 }
3046 }
3047
3048 if (streq(lvalue, "MemoryLow"))
3049 c->memory_low = bytes;
3050 else if (streq(lvalue, "MemoryHigh"))
3051 c->memory_high = bytes;
3052 else if (streq(lvalue, "MemoryMax"))
3053 c->memory_max = bytes;
3054 else if (streq(lvalue, "MemorySwapMax"))
3055 c->memory_swap_max = bytes;
3056 else if (streq(lvalue, "MemoryLimit"))
3057 c->memory_limit = bytes;
3058 else
3059 return -EINVAL;
3060
3061 return 0;
3062 }
3063
3064 int config_parse_tasks_max(
3065 const char *unit,
3066 const char *filename,
3067 unsigned line,
3068 const char *section,
3069 unsigned section_line,
3070 const char *lvalue,
3071 int ltype,
3072 const char *rvalue,
3073 void *data,
3074 void *userdata) {
3075
3076 uint64_t *tasks_max = data, v;
3077 Unit *u = userdata;
3078 int r;
3079
3080 if (isempty(rvalue)) {
3081 *tasks_max = u->manager->default_tasks_max;
3082 return 0;
3083 }
3084
3085 if (streq(rvalue, "infinity")) {
3086 *tasks_max = CGROUP_LIMIT_MAX;
3087 return 0;
3088 }
3089
3090 r = parse_percent(rvalue);
3091 if (r < 0) {
3092 r = safe_atou64(rvalue, &v);
3093 if (r < 0) {
3094 log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
3095 return 0;
3096 }
3097 } else
3098 v = system_tasks_max_scale(r, 100U);
3099
3100 if (v <= 0 || v >= UINT64_MAX) {
3101 log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue);
3102 return 0;
3103 }
3104
3105 *tasks_max = v;
3106 return 0;
3107 }
3108
3109 int config_parse_device_allow(
3110 const char *unit,
3111 const char *filename,
3112 unsigned line,
3113 const char *section,
3114 unsigned section_line,
3115 const char *lvalue,
3116 int ltype,
3117 const char *rvalue,
3118 void *data,
3119 void *userdata) {
3120
3121 _cleanup_free_ char *path = NULL, *t = NULL;
3122 CGroupContext *c = data;
3123 CGroupDeviceAllow *a;
3124 const char *m = NULL;
3125 size_t n;
3126 int r;
3127
3128 if (isempty(rvalue)) {
3129 while (c->device_allow)
3130 cgroup_context_free_device_allow(c, c->device_allow);
3131
3132 return 0;
3133 }
3134
3135 r = unit_full_printf(userdata, rvalue, &t);
3136 if(r < 0) {
3137 log_syntax(unit, LOG_WARNING, filename, line, r,
3138 "Failed to resolve specifiers in %s, ignoring: %m",
3139 rvalue);
3140 }
3141
3142 n = strcspn(t, WHITESPACE);
3143
3144 path = strndup(t, n);
3145 if (!path)
3146 return log_oom();
3147
3148 if (!is_deviceallow_pattern(path)) {
3149 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3150 return 0;
3151 }
3152
3153 m = t + n + strspn(t + n, WHITESPACE);
3154 if (isempty(m))
3155 m = "rwm";
3156
3157 if (!in_charset(m, "rwm")) {
3158 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
3159 return 0;
3160 }
3161
3162 a = new0(CGroupDeviceAllow, 1);
3163 if (!a)
3164 return log_oom();
3165
3166 a->path = path;
3167 path = NULL;
3168 a->r = !!strchr(m, 'r');
3169 a->w = !!strchr(m, 'w');
3170 a->m = !!strchr(m, 'm');
3171
3172 LIST_PREPEND(device_allow, c->device_allow, a);
3173 return 0;
3174 }
3175
3176 int config_parse_io_weight(
3177 const char *unit,
3178 const char *filename,
3179 unsigned line,
3180 const char *section,
3181 unsigned section_line,
3182 const char *lvalue,
3183 int ltype,
3184 const char *rvalue,
3185 void *data,
3186 void *userdata) {
3187
3188 uint64_t *weight = data;
3189 int r;
3190
3191 assert(filename);
3192 assert(lvalue);
3193 assert(rvalue);
3194
3195 r = cg_weight_parse(rvalue, weight);
3196 if (r < 0) {
3197 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue);
3198 return 0;
3199 }
3200
3201 return 0;
3202 }
3203
3204 int config_parse_io_device_weight(
3205 const char *unit,
3206 const char *filename,
3207 unsigned line,
3208 const char *section,
3209 unsigned section_line,
3210 const char *lvalue,
3211 int ltype,
3212 const char *rvalue,
3213 void *data,
3214 void *userdata) {
3215
3216 _cleanup_free_ char *path = NULL;
3217 CGroupIODeviceWeight *w;
3218 CGroupContext *c = data;
3219 const char *weight;
3220 uint64_t u;
3221 size_t n;
3222 int r;
3223
3224 assert(filename);
3225 assert(lvalue);
3226 assert(rvalue);
3227
3228 if (isempty(rvalue)) {
3229 while (c->io_device_weights)
3230 cgroup_context_free_io_device_weight(c, c->io_device_weights);
3231
3232 return 0;
3233 }
3234
3235 n = strcspn(rvalue, WHITESPACE);
3236 weight = rvalue + n;
3237 weight += strspn(weight, WHITESPACE);
3238
3239 if (isempty(weight)) {
3240 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
3241 return 0;
3242 }
3243
3244 path = strndup(rvalue, n);
3245 if (!path)
3246 return log_oom();
3247
3248 if (!path_startswith(path, "/dev")) {
3249 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3250 return 0;
3251 }
3252
3253 r = cg_weight_parse(weight, &u);
3254 if (r < 0) {
3255 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight);
3256 return 0;
3257 }
3258
3259 assert(u != CGROUP_WEIGHT_INVALID);
3260
3261 w = new0(CGroupIODeviceWeight, 1);
3262 if (!w)
3263 return log_oom();
3264
3265 w->path = path;
3266 path = NULL;
3267
3268 w->weight = u;
3269
3270 LIST_PREPEND(device_weights, c->io_device_weights, w);
3271 return 0;
3272 }
3273
3274 int config_parse_io_limit(
3275 const char *unit,
3276 const char *filename,
3277 unsigned line,
3278 const char *section,
3279 unsigned section_line,
3280 const char *lvalue,
3281 int ltype,
3282 const char *rvalue,
3283 void *data,
3284 void *userdata) {
3285
3286 _cleanup_free_ char *path = NULL;
3287 CGroupIODeviceLimit *l = NULL, *t;
3288 CGroupContext *c = data;
3289 CGroupIOLimitType type;
3290 const char *limit;
3291 uint64_t num;
3292 size_t n;
3293 int r;
3294
3295 assert(filename);
3296 assert(lvalue);
3297 assert(rvalue);
3298
3299 type = cgroup_io_limit_type_from_string(lvalue);
3300 assert(type >= 0);
3301
3302 if (isempty(rvalue)) {
3303 LIST_FOREACH(device_limits, l, c->io_device_limits)
3304 l->limits[type] = cgroup_io_limit_defaults[type];
3305 return 0;
3306 }
3307
3308 n = strcspn(rvalue, WHITESPACE);
3309 limit = rvalue + n;
3310 limit += strspn(limit, WHITESPACE);
3311
3312 if (!*limit) {
3313 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3314 return 0;
3315 }
3316
3317 path = strndup(rvalue, n);
3318 if (!path)
3319 return log_oom();
3320
3321 if (!path_startswith(path, "/dev")) {
3322 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3323 return 0;
3324 }
3325
3326 if (streq("infinity", limit)) {
3327 num = CGROUP_LIMIT_MAX;
3328 } else {
3329 r = parse_size(limit, 1000, &num);
3330 if (r < 0 || num <= 0) {
3331 log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue);
3332 return 0;
3333 }
3334 }
3335
3336 LIST_FOREACH(device_limits, t, c->io_device_limits) {
3337 if (path_equal(path, t->path)) {
3338 l = t;
3339 break;
3340 }
3341 }
3342
3343 if (!l) {
3344 CGroupIOLimitType ttype;
3345
3346 l = new0(CGroupIODeviceLimit, 1);
3347 if (!l)
3348 return log_oom();
3349
3350 l->path = path;
3351 path = NULL;
3352 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
3353 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
3354
3355 LIST_PREPEND(device_limits, c->io_device_limits, l);
3356 }
3357
3358 l->limits[type] = num;
3359
3360 return 0;
3361 }
3362
3363 int config_parse_blockio_weight(
3364 const char *unit,
3365 const char *filename,
3366 unsigned line,
3367 const char *section,
3368 unsigned section_line,
3369 const char *lvalue,
3370 int ltype,
3371 const char *rvalue,
3372 void *data,
3373 void *userdata) {
3374
3375 uint64_t *weight = data;
3376 int r;
3377
3378 assert(filename);
3379 assert(lvalue);
3380 assert(rvalue);
3381
3382 r = cg_blkio_weight_parse(rvalue, weight);
3383 if (r < 0) {
3384 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
3385 return 0;
3386 }
3387
3388 return 0;
3389 }
3390
3391 int config_parse_blockio_device_weight(
3392 const char *unit,
3393 const char *filename,
3394 unsigned line,
3395 const char *section,
3396 unsigned section_line,
3397 const char *lvalue,
3398 int ltype,
3399 const char *rvalue,
3400 void *data,
3401 void *userdata) {
3402
3403 _cleanup_free_ char *path = NULL;
3404 CGroupBlockIODeviceWeight *w;
3405 CGroupContext *c = data;
3406 const char *weight;
3407 uint64_t u;
3408 size_t n;
3409 int r;
3410
3411 assert(filename);
3412 assert(lvalue);
3413 assert(rvalue);
3414
3415 if (isempty(rvalue)) {
3416 while (c->blockio_device_weights)
3417 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
3418
3419 return 0;
3420 }
3421
3422 n = strcspn(rvalue, WHITESPACE);
3423 weight = rvalue + n;
3424 weight += strspn(weight, WHITESPACE);
3425
3426 if (isempty(weight)) {
3427 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
3428 return 0;
3429 }
3430
3431 path = strndup(rvalue, n);
3432 if (!path)
3433 return log_oom();
3434
3435 if (!path_startswith(path, "/dev")) {
3436 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3437 return 0;
3438 }
3439
3440 r = cg_blkio_weight_parse(weight, &u);
3441 if (r < 0) {
3442 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
3443 return 0;
3444 }
3445
3446 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
3447
3448 w = new0(CGroupBlockIODeviceWeight, 1);
3449 if (!w)
3450 return log_oom();
3451
3452 w->path = path;
3453 path = NULL;
3454
3455 w->weight = u;
3456
3457 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
3458 return 0;
3459 }
3460
3461 int config_parse_blockio_bandwidth(
3462 const char *unit,
3463 const char *filename,
3464 unsigned line,
3465 const char *section,
3466 unsigned section_line,
3467 const char *lvalue,
3468 int ltype,
3469 const char *rvalue,
3470 void *data,
3471 void *userdata) {
3472
3473 _cleanup_free_ char *path = NULL;
3474 CGroupBlockIODeviceBandwidth *b = NULL, *t;
3475 CGroupContext *c = data;
3476 const char *bandwidth;
3477 uint64_t bytes;
3478 bool read;
3479 size_t n;
3480 int r;
3481
3482 assert(filename);
3483 assert(lvalue);
3484 assert(rvalue);
3485
3486 read = streq("BlockIOReadBandwidth", lvalue);
3487
3488 if (isempty(rvalue)) {
3489 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
3490 b->rbps = CGROUP_LIMIT_MAX;
3491 b->wbps = CGROUP_LIMIT_MAX;
3492 }
3493 return 0;
3494 }
3495
3496 n = strcspn(rvalue, WHITESPACE);
3497 bandwidth = rvalue + n;
3498 bandwidth += strspn(bandwidth, WHITESPACE);
3499
3500 if (!*bandwidth) {
3501 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3502 return 0;
3503 }
3504
3505 path = strndup(rvalue, n);
3506 if (!path)
3507 return log_oom();
3508
3509 if (!path_startswith(path, "/dev")) {
3510 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3511 return 0;
3512 }
3513
3514 r = parse_size(bandwidth, 1000, &bytes);
3515 if (r < 0 || bytes <= 0) {
3516 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
3517 return 0;
3518 }
3519
3520 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
3521 if (path_equal(path, t->path)) {
3522 b = t;
3523 break;
3524 }
3525 }
3526
3527 if (!t) {
3528 b = new0(CGroupBlockIODeviceBandwidth, 1);
3529 if (!b)
3530 return log_oom();
3531
3532 b->path = path;
3533 path = NULL;
3534 b->rbps = CGROUP_LIMIT_MAX;
3535 b->wbps = CGROUP_LIMIT_MAX;
3536
3537 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
3538 }
3539
3540 if (read)
3541 b->rbps = bytes;
3542 else
3543 b->wbps = bytes;
3544
3545 return 0;
3546 }
3547
3548 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
3549
3550 int config_parse_job_mode_isolate(
3551 const char *unit,
3552 const char *filename,
3553 unsigned line,
3554 const char *section,
3555 unsigned section_line,
3556 const char *lvalue,
3557 int ltype,
3558 const char *rvalue,
3559 void *data,
3560 void *userdata) {
3561
3562 JobMode *m = data;
3563 int r;
3564
3565 assert(filename);
3566 assert(lvalue);
3567 assert(rvalue);
3568
3569 r = parse_boolean(rvalue);
3570 if (r < 0) {
3571 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
3572 return 0;
3573 }
3574
3575 *m = r ? JOB_ISOLATE : JOB_REPLACE;
3576 return 0;
3577 }
3578
3579 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
3580
3581 int config_parse_exec_directories(
3582 const char *unit,
3583 const char *filename,
3584 unsigned line,
3585 const char *section,
3586 unsigned section_line,
3587 const char *lvalue,
3588 int ltype,
3589 const char *rvalue,
3590 void *data,
3591 void *userdata) {
3592
3593 char***rt = data;
3594 Unit *u = userdata;
3595 const char *p;
3596 int r;
3597
3598 assert(filename);
3599 assert(lvalue);
3600 assert(rvalue);
3601 assert(data);
3602
3603 if (isempty(rvalue)) {
3604 /* Empty assignment resets the list */
3605 *rt = strv_free(*rt);
3606 return 0;
3607 }
3608
3609 for (p = rvalue;;) {
3610 _cleanup_free_ char *word = NULL, *k = NULL;
3611
3612 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
3613 if (r == 0)
3614 return 0;
3615 if (r == -ENOMEM)
3616 return log_oom();
3617 if (r < 0) {
3618 log_syntax(unit, LOG_WARNING, filename, line, r,
3619 "Invalid syntax, ignoring: %s", rvalue);
3620 return 0;
3621 }
3622
3623 r = unit_full_printf(u, word, &k);
3624 if (r < 0) {
3625 log_syntax(unit, LOG_ERR, filename, line, r,
3626 "Failed to resolve specifiers in \"%s\", ignoring: %m", word);
3627 continue;
3628 }
3629
3630 if (!path_is_safe(k) || path_is_absolute(k)) {
3631 log_syntax(unit, LOG_ERR, filename, line, 0,
3632 "%s is not valid, ignoring assignment: %s", lvalue, rvalue);
3633 continue;
3634 }
3635
3636 r = strv_push(rt, k);
3637 if (r < 0)
3638 return log_oom();
3639 k = NULL;
3640 }
3641 }
3642
3643 int config_parse_set_status(
3644 const char *unit,
3645 const char *filename,
3646 unsigned line,
3647 const char *section,
3648 unsigned section_line,
3649 const char *lvalue,
3650 int ltype,
3651 const char *rvalue,
3652 void *data,
3653 void *userdata) {
3654
3655 size_t l;
3656 const char *word, *state;
3657 int r;
3658 ExitStatusSet *status_set = data;
3659
3660 assert(filename);
3661 assert(lvalue);
3662 assert(rvalue);
3663 assert(data);
3664
3665 /* Empty assignment resets the list */
3666 if (isempty(rvalue)) {
3667 exit_status_set_free(status_set);
3668 return 0;
3669 }
3670
3671 FOREACH_WORD(word, l, rvalue, state) {
3672 _cleanup_free_ char *temp;
3673 int val;
3674 Set **set;
3675
3676 temp = strndup(word, l);
3677 if (!temp)
3678 return log_oom();
3679
3680 r = safe_atoi(temp, &val);
3681 if (r < 0) {
3682 val = signal_from_string_try_harder(temp);
3683
3684 if (val <= 0) {
3685 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
3686 continue;
3687 }
3688 set = &status_set->signal;
3689 } else {
3690 if (val < 0 || val > 255) {
3691 log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
3692 continue;
3693 }
3694 set = &status_set->status;
3695 }
3696
3697 r = set_ensure_allocated(set, NULL);
3698 if (r < 0)
3699 return log_oom();
3700
3701 r = set_put(*set, INT_TO_PTR(val));
3702 if (r < 0) {
3703 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
3704 return r;
3705 }
3706 }
3707 if (!isempty(state))
3708 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3709
3710 return 0;
3711 }
3712
3713 int config_parse_namespace_path_strv(
3714 const char *unit,
3715 const char *filename,
3716 unsigned line,
3717 const char *section,
3718 unsigned section_line,
3719 const char *lvalue,
3720 int ltype,
3721 const char *rvalue,
3722 void *data,
3723 void *userdata) {
3724
3725 Unit *u = userdata;
3726 char*** sv = data;
3727 const char *cur;
3728 int r;
3729
3730 assert(filename);
3731 assert(lvalue);
3732 assert(rvalue);
3733 assert(data);
3734
3735 if (isempty(rvalue)) {
3736 /* Empty assignment resets the list */
3737 *sv = strv_free(*sv);
3738 return 0;
3739 }
3740
3741 cur = rvalue;
3742 for (;;) {
3743 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
3744 const char *w;
3745 bool ignore_enoent = false, shall_prefix = false;
3746
3747 r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
3748 if (r == 0)
3749 break;
3750 if (r == -ENOMEM)
3751 return log_oom();
3752 if (r < 0) {
3753 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
3754 return 0;
3755 }
3756
3757 if (!utf8_is_valid(word)) {
3758 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
3759 continue;
3760 }
3761
3762 w = word;
3763 if (startswith(w, "-")) {
3764 ignore_enoent = true;
3765 w++;
3766 }
3767 if (startswith(w, "+")) {
3768 shall_prefix = true;
3769 w++;
3770 }
3771
3772 r = unit_full_printf(u, w, &resolved);
3773 if (r < 0) {
3774 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word);
3775 continue;
3776 }
3777
3778 if (!path_is_absolute(resolved)) {
3779 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", resolved);
3780 continue;
3781 }
3782
3783 path_kill_slashes(resolved);
3784
3785 joined = strjoin(ignore_enoent ? "-" : "",
3786 shall_prefix ? "+" : "",
3787 resolved);
3788
3789 r = strv_push(sv, joined);
3790 if (r < 0)
3791 return log_oom();
3792
3793 joined = NULL;
3794 }
3795
3796 return 0;
3797 }
3798
3799 int config_parse_bind_paths(
3800 const char *unit,
3801 const char *filename,
3802 unsigned line,
3803 const char *section,
3804 unsigned section_line,
3805 const char *lvalue,
3806 int ltype,
3807 const char *rvalue,
3808 void *data,
3809 void *userdata) {
3810
3811 ExecContext *c = data;
3812 Unit *u = userdata;
3813 const char *p;
3814 int r;
3815
3816 assert(filename);
3817 assert(lvalue);
3818 assert(rvalue);
3819 assert(data);
3820
3821 if (isempty(rvalue)) {
3822 /* Empty assignment resets the list */
3823 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
3824 c->bind_mounts = NULL;
3825 c->n_bind_mounts = 0;
3826 return 0;
3827 }
3828
3829 p = rvalue;
3830 for (;;) {
3831 _cleanup_free_ char *source = NULL, *destination = NULL;
3832 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
3833 char *s = NULL, *d = NULL;
3834 bool rbind = true, ignore_enoent = false;
3835
3836 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
3837 if (r == 0)
3838 break;
3839 if (r == -ENOMEM)
3840 return log_oom();
3841 if (r < 0) {
3842 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
3843 return 0;
3844 }
3845
3846 r = unit_full_printf(u, source, &sresolved);
3847 if (r < 0) {
3848 log_syntax(unit, LOG_ERR, filename, line, r,
3849 "Failed to resolved specifiers in \"%s\", ignoring: %m", source);
3850 return 0;
3851 }
3852
3853 s = sresolved;
3854 if (s[0] == '-') {
3855 ignore_enoent = true;
3856 s++;
3857 }
3858
3859 if (!utf8_is_valid(s)) {
3860 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, s);
3861 return 0;
3862 }
3863 if (!path_is_absolute(s)) {
3864 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute source path, ignoring: %s", s);
3865 return 0;
3866 }
3867
3868 path_kill_slashes(s);
3869
3870 /* Optionally, the destination is specified. */
3871 if (p && p[-1] == ':') {
3872 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
3873 if (r == -ENOMEM)
3874 return log_oom();
3875 if (r < 0) {
3876 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
3877 return 0;
3878 }
3879 if (r == 0) {
3880 log_syntax(unit, LOG_ERR, filename, line, 0, "Missing argument after ':': %s", rvalue);
3881 return 0;
3882 }
3883
3884 r = unit_full_printf(u, destination, &dresolved);
3885 if (r < 0) {
3886 log_syntax(unit, LOG_ERR, filename, line, r,
3887 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination);
3888 return 0;
3889 }
3890
3891 if (!utf8_is_valid(dresolved)) {
3892 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, dresolved);
3893 return 0;
3894 }
3895 if (!path_is_absolute(dresolved)) {
3896 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute destination path, ignoring: %s", dresolved);
3897 return 0;
3898 }
3899
3900 d = path_kill_slashes(dresolved);
3901
3902 /* Optionally, there's also a short option string specified */
3903 if (p && p[-1] == ':') {
3904 _cleanup_free_ char *options = NULL;
3905
3906 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
3907 if (r == -ENOMEM)
3908 return log_oom();
3909 if (r < 0) {
3910 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
3911 return 0;
3912 }
3913
3914 if (isempty(options) || streq(options, "rbind"))
3915 rbind = true;
3916 else if (streq(options, "norbind"))
3917 rbind = false;
3918 else {
3919 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
3920 return 0;
3921 }
3922 }
3923 } else
3924 d = s;
3925
3926 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
3927 &(BindMount) {
3928 .source = s,
3929 .destination = d,
3930 .read_only = !!strstr(lvalue, "ReadOnly"),
3931 .recursive = rbind,
3932 .ignore_enoent = ignore_enoent,
3933 });
3934 if (r < 0)
3935 return log_oom();
3936 }
3937
3938 return 0;
3939 }
3940
3941 int config_parse_no_new_privileges(
3942 const char* unit,
3943 const char *filename,
3944 unsigned line,
3945 const char *section,
3946 unsigned section_line,
3947 const char *lvalue,
3948 int ltype,
3949 const char *rvalue,
3950 void *data,
3951 void *userdata) {
3952
3953 ExecContext *c = data;
3954 int k;
3955
3956 assert(filename);
3957 assert(lvalue);
3958 assert(rvalue);
3959 assert(data);
3960
3961 k = parse_boolean(rvalue);
3962 if (k < 0) {
3963 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
3964 return 0;
3965 }
3966
3967 c->no_new_privileges = k;
3968
3969 return 0;
3970 }
3971
3972 int config_parse_protect_home(
3973 const char* unit,
3974 const char *filename,
3975 unsigned line,
3976 const char *section,
3977 unsigned section_line,
3978 const char *lvalue,
3979 int ltype,
3980 const char *rvalue,
3981 void *data,
3982 void *userdata) {
3983
3984 ExecContext *c = data;
3985 int k;
3986
3987 assert(filename);
3988 assert(lvalue);
3989 assert(rvalue);
3990 assert(data);
3991
3992 /* Our enum shall be a superset of booleans, hence first try
3993 * to parse as boolean, and then as enum */
3994
3995 k = parse_boolean(rvalue);
3996 if (k > 0)
3997 c->protect_home = PROTECT_HOME_YES;
3998 else if (k == 0)
3999 c->protect_home = PROTECT_HOME_NO;
4000 else {
4001 ProtectHome h;
4002
4003 h = protect_home_from_string(rvalue);
4004 if (h < 0) {
4005 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
4006 return 0;
4007 }
4008
4009 c->protect_home = h;
4010 }
4011
4012 return 0;
4013 }
4014
4015 int config_parse_protect_system(
4016 const char* unit,
4017 const char *filename,
4018 unsigned line,
4019 const char *section,
4020 unsigned section_line,
4021 const char *lvalue,
4022 int ltype,
4023 const char *rvalue,
4024 void *data,
4025 void *userdata) {
4026
4027 ExecContext *c = data;
4028 int k;
4029
4030 assert(filename);
4031 assert(lvalue);
4032 assert(rvalue);
4033 assert(data);
4034
4035 /* Our enum shall be a superset of booleans, hence first try
4036 * to parse as boolean, and then as enum */
4037
4038 k = parse_boolean(rvalue);
4039 if (k > 0)
4040 c->protect_system = PROTECT_SYSTEM_YES;
4041 else if (k == 0)
4042 c->protect_system = PROTECT_SYSTEM_NO;
4043 else {
4044 ProtectSystem s;
4045
4046 s = protect_system_from_string(rvalue);
4047 if (s < 0) {
4048 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
4049 return 0;
4050 }
4051
4052 c->protect_system = s;
4053 }
4054
4055 return 0;
4056 }
4057
4058 #define FOLLOW_MAX 8
4059
4060 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
4061 char *id = NULL;
4062 unsigned c = 0;
4063 int fd, r;
4064 FILE *f;
4065
4066 assert(filename);
4067 assert(*filename);
4068 assert(_f);
4069 assert(names);
4070
4071 /* This will update the filename pointer if the loaded file is
4072 * reached by a symlink. The old string will be freed. */
4073
4074 for (;;) {
4075 char *target, *name;
4076
4077 if (c++ >= FOLLOW_MAX)
4078 return -ELOOP;
4079
4080 path_kill_slashes(*filename);
4081
4082 /* Add the file name we are currently looking at to
4083 * the names of this unit, but only if it is a valid
4084 * unit name. */
4085 name = basename(*filename);
4086 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
4087
4088 id = set_get(names, name);
4089 if (!id) {
4090 id = strdup(name);
4091 if (!id)
4092 return -ENOMEM;
4093
4094 r = set_consume(names, id);
4095 if (r < 0)
4096 return r;
4097 }
4098 }
4099
4100 /* Try to open the file name, but don't if its a symlink */
4101 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
4102 if (fd >= 0)
4103 break;
4104
4105 if (errno != ELOOP)
4106 return -errno;
4107
4108 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4109 r = readlink_and_make_absolute(*filename, &target);
4110 if (r < 0)
4111 return r;
4112
4113 free(*filename);
4114 *filename = target;
4115 }
4116
4117 f = fdopen(fd, "re");
4118 if (!f) {
4119 safe_close(fd);
4120 return -errno;
4121 }
4122
4123 *_f = f;
4124 *_final = id;
4125
4126 return 0;
4127 }
4128
4129 static int merge_by_names(Unit **u, Set *names, const char *id) {
4130 char *k;
4131 int r;
4132
4133 assert(u);
4134 assert(*u);
4135 assert(names);
4136
4137 /* Let's try to add in all symlink names we found */
4138 while ((k = set_steal_first(names))) {
4139
4140 /* First try to merge in the other name into our
4141 * unit */
4142 r = unit_merge_by_name(*u, k);
4143 if (r < 0) {
4144 Unit *other;
4145
4146 /* Hmm, we couldn't merge the other unit into
4147 * ours? Then let's try it the other way
4148 * round */
4149
4150 /* If the symlink name we are looking at is unit template, then
4151 we must search for instance of this template */
4152 if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) {
4153 _cleanup_free_ char *instance = NULL;
4154
4155 r = unit_name_replace_instance(k, (*u)->instance, &instance);
4156 if (r < 0)
4157 return r;
4158
4159 other = manager_get_unit((*u)->manager, instance);
4160 } else
4161 other = manager_get_unit((*u)->manager, k);
4162
4163 free(k);
4164
4165 if (other) {
4166 r = unit_merge(other, *u);
4167 if (r >= 0) {
4168 *u = other;
4169 return merge_by_names(u, names, NULL);
4170 }
4171 }
4172
4173 return r;
4174 }
4175
4176 if (id == k)
4177 unit_choose_id(*u, id);
4178
4179 free(k);
4180 }
4181
4182 return 0;
4183 }
4184
4185 static int load_from_path(Unit *u, const char *path) {
4186 _cleanup_set_free_free_ Set *symlink_names = NULL;
4187 _cleanup_fclose_ FILE *f = NULL;
4188 _cleanup_free_ char *filename = NULL;
4189 char *id = NULL;
4190 Unit *merged;
4191 struct stat st;
4192 int r;
4193
4194 assert(u);
4195 assert(path);
4196
4197 symlink_names = set_new(&string_hash_ops);
4198 if (!symlink_names)
4199 return -ENOMEM;
4200
4201 if (path_is_absolute(path)) {
4202
4203 filename = strdup(path);
4204 if (!filename)
4205 return -ENOMEM;
4206
4207 r = open_follow(&filename, &f, symlink_names, &id);
4208 if (r < 0) {
4209 filename = mfree(filename);
4210 if (r != -ENOENT)
4211 return r;
4212 }
4213
4214 } else {
4215 char **p;
4216
4217 STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
4218
4219 /* Instead of opening the path right away, we manually
4220 * follow all symlinks and add their name to our unit
4221 * name set while doing so */
4222 filename = path_make_absolute(path, *p);
4223 if (!filename)
4224 return -ENOMEM;
4225
4226 if (u->manager->unit_path_cache &&
4227 !set_get(u->manager->unit_path_cache, filename))
4228 r = -ENOENT;
4229 else
4230 r = open_follow(&filename, &f, symlink_names, &id);
4231 if (r >= 0)
4232 break;
4233 filename = mfree(filename);
4234
4235 /* ENOENT means that the file is missing or is a dangling symlink.
4236 * ENOTDIR means that one of paths we expect to be is a directory
4237 * is not a directory, we should just ignore that.
4238 * EACCES means that the directory or file permissions are wrong.
4239 */
4240 if (r == -EACCES)
4241 log_debug_errno(r, "Cannot access \"%s\": %m", filename);
4242 else if (!IN_SET(r, -ENOENT, -ENOTDIR))
4243 return r;
4244
4245 /* Empty the symlink names for the next run */
4246 set_clear_free(symlink_names);
4247 }
4248 }
4249
4250 if (!filename)
4251 /* Hmm, no suitable file found? */
4252 return 0;
4253
4254 if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
4255 log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
4256 return -ELOOP;
4257 }
4258
4259 merged = u;
4260 r = merge_by_names(&merged, symlink_names, id);
4261 if (r < 0)
4262 return r;
4263
4264 if (merged != u) {
4265 u->load_state = UNIT_MERGED;
4266 return 0;
4267 }
4268
4269 if (fstat(fileno(f), &st) < 0)
4270 return -errno;
4271
4272 if (null_or_empty(&st)) {
4273 u->load_state = UNIT_MASKED;
4274 u->fragment_mtime = 0;
4275 } else {
4276 u->load_state = UNIT_LOADED;
4277 u->fragment_mtime = timespec_load(&st.st_mtim);
4278
4279 /* Now, parse the file contents */
4280 r = config_parse(u->id, filename, f,
4281 UNIT_VTABLE(u)->sections,
4282 config_item_perf_lookup, load_fragment_gperf_lookup,
4283 false, true, false, u);
4284 if (r < 0)
4285 return r;
4286 }
4287
4288 free(u->fragment_path);
4289 u->fragment_path = filename;
4290 filename = NULL;
4291
4292 if (u->source_path) {
4293 if (stat(u->source_path, &st) >= 0)
4294 u->source_mtime = timespec_load(&st.st_mtim);
4295 else
4296 u->source_mtime = 0;
4297 }
4298
4299 return 0;
4300 }
4301
4302 int unit_load_fragment(Unit *u) {
4303 int r;
4304 Iterator i;
4305 const char *t;
4306
4307 assert(u);
4308 assert(u->load_state == UNIT_STUB);
4309 assert(u->id);
4310
4311 if (u->transient) {
4312 u->load_state = UNIT_LOADED;
4313 return 0;
4314 }
4315
4316 /* First, try to find the unit under its id. We always look
4317 * for unit files in the default directories, to make it easy
4318 * to override things by placing things in /etc/systemd/system */
4319 r = load_from_path(u, u->id);
4320 if (r < 0)
4321 return r;
4322
4323 /* Try to find an alias we can load this with */
4324 if (u->load_state == UNIT_STUB) {
4325 SET_FOREACH(t, u->names, i) {
4326
4327 if (t == u->id)
4328 continue;
4329
4330 r = load_from_path(u, t);
4331 if (r < 0)
4332 return r;
4333
4334 if (u->load_state != UNIT_STUB)
4335 break;
4336 }
4337 }
4338
4339 /* And now, try looking for it under the suggested (originally linked) path */
4340 if (u->load_state == UNIT_STUB && u->fragment_path) {
4341
4342 r = load_from_path(u, u->fragment_path);
4343 if (r < 0)
4344 return r;
4345
4346 if (u->load_state == UNIT_STUB)
4347 /* Hmm, this didn't work? Then let's get rid
4348 * of the fragment path stored for us, so that
4349 * we don't point to an invalid location. */
4350 u->fragment_path = mfree(u->fragment_path);
4351 }
4352
4353 /* Look for a template */
4354 if (u->load_state == UNIT_STUB && u->instance) {
4355 _cleanup_free_ char *k = NULL;
4356
4357 r = unit_name_template(u->id, &k);
4358 if (r < 0)
4359 return r;
4360
4361 r = load_from_path(u, k);
4362 if (r < 0) {
4363 if (r == -ENOEXEC)
4364 log_unit_notice(u, "Unit configuration has fatal error, unit will not be started.");
4365 return r;
4366 }
4367
4368 if (u->load_state == UNIT_STUB) {
4369 SET_FOREACH(t, u->names, i) {
4370 _cleanup_free_ char *z = NULL;
4371
4372 if (t == u->id)
4373 continue;
4374
4375 r = unit_name_template(t, &z);
4376 if (r < 0)
4377 return r;
4378
4379 r = load_from_path(u, z);
4380 if (r < 0)
4381 return r;
4382
4383 if (u->load_state != UNIT_STUB)
4384 break;
4385 }
4386 }
4387 }
4388
4389 return 0;
4390 }
4391
4392 void unit_dump_config_items(FILE *f) {
4393 static const struct {
4394 const ConfigParserCallback callback;
4395 const char *rvalue;
4396 } table[] = {
4397 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
4398 { config_parse_warn_compat, "NOTSUPPORTED" },
4399 #endif
4400 { config_parse_int, "INTEGER" },
4401 { config_parse_unsigned, "UNSIGNED" },
4402 { config_parse_iec_size, "SIZE" },
4403 { config_parse_iec_uint64, "SIZE" },
4404 { config_parse_si_size, "SIZE" },
4405 { config_parse_bool, "BOOLEAN" },
4406 { config_parse_string, "STRING" },
4407 { config_parse_path, "PATH" },
4408 { config_parse_unit_path_printf, "PATH" },
4409 { config_parse_strv, "STRING [...]" },
4410 { config_parse_exec_nice, "NICE" },
4411 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
4412 { config_parse_exec_io_class, "IOCLASS" },
4413 { config_parse_exec_io_priority, "IOPRIORITY" },
4414 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
4415 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
4416 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
4417 { config_parse_mode, "MODE" },
4418 { config_parse_unit_env_file, "FILE" },
4419 { config_parse_exec_output, "OUTPUT" },
4420 { config_parse_exec_input, "INPUT" },
4421 { config_parse_log_facility, "FACILITY" },
4422 { config_parse_log_level, "LEVEL" },
4423 { config_parse_exec_secure_bits, "SECUREBITS" },
4424 { config_parse_capability_set, "BOUNDINGSET" },
4425 { config_parse_limit, "LIMIT" },
4426 { config_parse_unit_deps, "UNIT [...]" },
4427 { config_parse_exec, "PATH [ARGUMENT [...]]" },
4428 { config_parse_service_type, "SERVICETYPE" },
4429 { config_parse_service_restart, "SERVICERESTART" },
4430 #ifdef HAVE_SYSV_COMPAT
4431 { config_parse_sysv_priority, "SYSVPRIORITY" },
4432 #endif
4433 { config_parse_kill_mode, "KILLMODE" },
4434 { config_parse_signal, "SIGNAL" },
4435 { config_parse_socket_listen, "SOCKET [...]" },
4436 { config_parse_socket_bind, "SOCKETBIND" },
4437 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
4438 { config_parse_sec, "SECONDS" },
4439 { config_parse_nsec, "NANOSECONDS" },
4440 { config_parse_namespace_path_strv, "PATH [...]" },
4441 { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
4442 { config_parse_unit_requires_mounts_for, "PATH [...]" },
4443 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
4444 { config_parse_unit_string_printf, "STRING" },
4445 { config_parse_trigger_unit, "UNIT" },
4446 { config_parse_timer, "TIMER" },
4447 { config_parse_path_spec, "PATH" },
4448 { config_parse_notify_access, "ACCESS" },
4449 { config_parse_ip_tos, "TOS" },
4450 { config_parse_unit_condition_path, "CONDITION" },
4451 { config_parse_unit_condition_string, "CONDITION" },
4452 { config_parse_unit_condition_null, "CONDITION" },
4453 { config_parse_unit_slice, "SLICE" },
4454 { config_parse_documentation, "URL" },
4455 { config_parse_service_timeout, "SECONDS" },
4456 { config_parse_emergency_action, "ACTION" },
4457 { config_parse_set_status, "STATUS" },
4458 { config_parse_service_sockets, "SOCKETS" },
4459 { config_parse_environ, "ENVIRON" },
4460 #ifdef HAVE_SECCOMP
4461 { config_parse_syscall_filter, "SYSCALLS" },
4462 { config_parse_syscall_archs, "ARCHS" },
4463 { config_parse_syscall_errno, "ERRNO" },
4464 { config_parse_address_families, "FAMILIES" },
4465 { config_parse_restrict_namespaces, "NAMESPACES" },
4466 #endif
4467 { config_parse_cpu_shares, "SHARES" },
4468 { config_parse_cpu_weight, "WEIGHT" },
4469 { config_parse_memory_limit, "LIMIT" },
4470 { config_parse_device_allow, "DEVICE" },
4471 { config_parse_device_policy, "POLICY" },
4472 { config_parse_io_limit, "LIMIT" },
4473 { config_parse_io_weight, "WEIGHT" },
4474 { config_parse_io_device_weight, "DEVICEWEIGHT" },
4475 { config_parse_blockio_bandwidth, "BANDWIDTH" },
4476 { config_parse_blockio_weight, "WEIGHT" },
4477 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
4478 { config_parse_long, "LONG" },
4479 { config_parse_socket_service, "SERVICE" },
4480 #ifdef HAVE_SELINUX
4481 { config_parse_exec_selinux_context, "LABEL" },
4482 #endif
4483 { config_parse_job_mode, "MODE" },
4484 { config_parse_job_mode_isolate, "BOOLEAN" },
4485 { config_parse_personality, "PERSONALITY" },
4486 };
4487
4488 const char *prev = NULL;
4489 const char *i;
4490
4491 assert(f);
4492
4493 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
4494 const char *rvalue = "OTHER", *lvalue;
4495 unsigned j;
4496 size_t prefix_len;
4497 const char *dot;
4498 const ConfigPerfItem *p;
4499
4500 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
4501
4502 dot = strchr(i, '.');
4503 lvalue = dot ? dot + 1 : i;
4504 prefix_len = dot-i;
4505
4506 if (dot)
4507 if (!prev || !strneq(prev, i, prefix_len+1)) {
4508 if (prev)
4509 fputc('\n', f);
4510
4511 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
4512 }
4513
4514 for (j = 0; j < ELEMENTSOF(table); j++)
4515 if (p->parse == table[j].callback) {
4516 rvalue = table[j].rvalue;
4517 break;
4518 }
4519
4520 fprintf(f, "%s=%s\n", lvalue, rvalue);
4521 prev = i;
4522 }
4523 }