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