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