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