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