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