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