]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/load-fragment.c
Merge pull request #5976 from fbuihuu/swap-fix
[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(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 if (*usec <= 0)
1880 *usec = USEC_INFINITY;
1881
1882 return 0;
1883 }
1884
1885 int config_parse_user_group(
1886 const char *unit,
1887 const char *filename,
1888 unsigned line,
1889 const char *section,
1890 unsigned section_line,
1891 const char *lvalue,
1892 int ltype,
1893 const char *rvalue,
1894 void *data,
1895 void *userdata) {
1896
1897 char **user = data, *n;
1898 Unit *u = userdata;
1899 int r;
1900
1901 assert(filename);
1902 assert(lvalue);
1903 assert(rvalue);
1904 assert(u);
1905
1906 if (isempty(rvalue))
1907 n = NULL;
1908 else {
1909 _cleanup_free_ char *k = NULL;
1910
1911 r = unit_full_printf(u, rvalue, &k);
1912 if (r < 0) {
1913 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
1914 return 0;
1915 }
1916
1917 if (!valid_user_group_name_or_id(k)) {
1918 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k);
1919 return 0;
1920 }
1921
1922 n = k;
1923 k = NULL;
1924 }
1925
1926 free(*user);
1927 *user = n;
1928
1929 return 0;
1930 }
1931
1932 int config_parse_user_group_strv(
1933 const char *unit,
1934 const char *filename,
1935 unsigned line,
1936 const char *section,
1937 unsigned section_line,
1938 const char *lvalue,
1939 int ltype,
1940 const char *rvalue,
1941 void *data,
1942 void *userdata) {
1943
1944 char ***users = data;
1945 Unit *u = userdata;
1946 const char *p;
1947 int r;
1948
1949 assert(filename);
1950 assert(lvalue);
1951 assert(rvalue);
1952 assert(u);
1953
1954 if (isempty(rvalue)) {
1955 char **empty;
1956
1957 empty = new0(char*, 1);
1958 if (!empty)
1959 return log_oom();
1960
1961 strv_free(*users);
1962 *users = empty;
1963
1964 return 0;
1965 }
1966
1967 p = rvalue;
1968 for (;;) {
1969 _cleanup_free_ char *word = NULL, *k = NULL;
1970
1971 r = extract_first_word(&p, &word, NULL, 0);
1972 if (r == 0)
1973 break;
1974 if (r == -ENOMEM)
1975 return log_oom();
1976 if (r < 0) {
1977 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
1978 break;
1979 }
1980
1981 r = unit_full_printf(u, word, &k);
1982 if (r < 0) {
1983 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word);
1984 continue;
1985 }
1986
1987 if (!valid_user_group_name_or_id(k)) {
1988 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k);
1989 continue;
1990 }
1991
1992 r = strv_push(users, k);
1993 if (r < 0)
1994 return log_oom();
1995
1996 k = NULL;
1997 }
1998
1999 return 0;
2000 }
2001
2002 int config_parse_busname_service(
2003 const char *unit,
2004 const char *filename,
2005 unsigned line,
2006 const char *section,
2007 unsigned section_line,
2008 const char *lvalue,
2009 int ltype,
2010 const char *rvalue,
2011 void *data,
2012 void *userdata) {
2013
2014 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2015 BusName *n = data;
2016 int r;
2017 Unit *x;
2018 _cleanup_free_ char *p = NULL;
2019
2020 assert(filename);
2021 assert(lvalue);
2022 assert(rvalue);
2023 assert(data);
2024
2025 r = unit_name_printf(UNIT(n), rvalue, &p);
2026 if (r < 0) {
2027 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2028 return 0;
2029 }
2030
2031 if (!endswith(p, ".service")) {
2032 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
2033 return 0;
2034 }
2035
2036 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
2037 if (r < 0) {
2038 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
2039 return 0;
2040 }
2041
2042 unit_ref_set(&n->service, x);
2043
2044 return 0;
2045 }
2046
2047 DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world, bus_policy_access, BusPolicyAccess, "Failed to parse bus name policy access");
2048
2049 int config_parse_bus_policy(
2050 const char *unit,
2051 const char *filename,
2052 unsigned line,
2053 const char *section,
2054 unsigned section_line,
2055 const char *lvalue,
2056 int ltype,
2057 const char *rvalue,
2058 void *data,
2059 void *userdata) {
2060
2061 _cleanup_free_ BusNamePolicy *p = NULL;
2062 _cleanup_free_ char *id_str = NULL;
2063 BusName *busname = data;
2064 char *access_str;
2065
2066 assert(filename);
2067 assert(lvalue);
2068 assert(rvalue);
2069 assert(data);
2070
2071 p = new0(BusNamePolicy, 1);
2072 if (!p)
2073 return log_oom();
2074
2075 if (streq(lvalue, "AllowUser"))
2076 p->type = BUSNAME_POLICY_TYPE_USER;
2077 else if (streq(lvalue, "AllowGroup"))
2078 p->type = BUSNAME_POLICY_TYPE_GROUP;
2079 else
2080 assert_not_reached("Unknown lvalue");
2081
2082 id_str = strdup(rvalue);
2083 if (!id_str)
2084 return log_oom();
2085
2086 access_str = strpbrk(id_str, WHITESPACE);
2087 if (!access_str) {
2088 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy value '%s'", rvalue);
2089 return 0;
2090 }
2091
2092 *access_str = '\0';
2093 access_str++;
2094 access_str += strspn(access_str, WHITESPACE);
2095
2096 p->access = bus_policy_access_from_string(access_str);
2097 if (p->access < 0) {
2098 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy access type '%s'", access_str);
2099 return 0;
2100 }
2101
2102 p->name = id_str;
2103 id_str = NULL;
2104
2105 LIST_PREPEND(policy, busname->policy, p);
2106 p = NULL;
2107
2108 return 0;
2109 }
2110
2111 int config_parse_working_directory(
2112 const char *unit,
2113 const char *filename,
2114 unsigned line,
2115 const char *section,
2116 unsigned section_line,
2117 const char *lvalue,
2118 int ltype,
2119 const char *rvalue,
2120 void *data,
2121 void *userdata) {
2122
2123 ExecContext *c = data;
2124 Unit *u = userdata;
2125 bool missing_ok;
2126 int r;
2127
2128 assert(filename);
2129 assert(lvalue);
2130 assert(rvalue);
2131 assert(c);
2132 assert(u);
2133
2134 if (rvalue[0] == '-') {
2135 missing_ok = true;
2136 rvalue++;
2137 } else
2138 missing_ok = false;
2139
2140 if (streq(rvalue, "~")) {
2141 c->working_directory_home = true;
2142 c->working_directory = mfree(c->working_directory);
2143 } else {
2144 _cleanup_free_ char *k = NULL;
2145
2146 r = unit_full_printf(u, rvalue, &k);
2147 if (r < 0) {
2148 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue);
2149 return 0;
2150 }
2151
2152 path_kill_slashes(k);
2153
2154 if (!utf8_is_valid(k)) {
2155 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
2156 return 0;
2157 }
2158
2159 if (!path_is_absolute(k)) {
2160 log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue);
2161 return 0;
2162 }
2163
2164 free_and_replace(c->working_directory, k);
2165
2166 c->working_directory_home = false;
2167 }
2168
2169 c->working_directory_missing_ok = missing_ok;
2170 return 0;
2171 }
2172
2173 int config_parse_unit_env_file(const char *unit,
2174 const char *filename,
2175 unsigned line,
2176 const char *section,
2177 unsigned section_line,
2178 const char *lvalue,
2179 int ltype,
2180 const char *rvalue,
2181 void *data,
2182 void *userdata) {
2183
2184 char ***env = data;
2185 Unit *u = userdata;
2186 _cleanup_free_ char *n = NULL;
2187 int r;
2188
2189 assert(filename);
2190 assert(lvalue);
2191 assert(rvalue);
2192 assert(data);
2193
2194 if (isempty(rvalue)) {
2195 /* Empty assignment frees the list */
2196 *env = strv_free(*env);
2197 return 0;
2198 }
2199
2200 r = unit_full_printf(u, rvalue, &n);
2201 if (r < 0) {
2202 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2203 return 0;
2204 }
2205
2206 if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) {
2207 log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n);
2208 return 0;
2209 }
2210
2211 r = strv_extend(env, n);
2212 if (r < 0)
2213 return log_oom();
2214
2215 return 0;
2216 }
2217
2218 int config_parse_environ(const char *unit,
2219 const char *filename,
2220 unsigned line,
2221 const char *section,
2222 unsigned section_line,
2223 const char *lvalue,
2224 int ltype,
2225 const char *rvalue,
2226 void *data,
2227 void *userdata) {
2228
2229 Unit *u = userdata;
2230 char ***env = data;
2231 const char *p;
2232 int r;
2233
2234 assert(filename);
2235 assert(lvalue);
2236 assert(rvalue);
2237 assert(data);
2238
2239 if (isempty(rvalue)) {
2240 /* Empty assignment resets the list */
2241 *env = strv_free(*env);
2242 return 0;
2243 }
2244
2245 for (p = rvalue;; ) {
2246 _cleanup_free_ char *word = NULL, *k = NULL;
2247
2248 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
2249 if (r == 0)
2250 return 0;
2251 if (r == -ENOMEM)
2252 return log_oom();
2253 if (r < 0) {
2254 log_syntax(unit, LOG_WARNING, filename, line, r,
2255 "Invalid syntax, ignoring: %s", rvalue);
2256 return 0;
2257 }
2258
2259 if (u) {
2260 r = unit_full_printf(u, word, &k);
2261 if (r < 0) {
2262 log_syntax(unit, LOG_ERR, filename, line, r,
2263 "Failed to resolve specifiers, ignoring: %s", k);
2264 continue;
2265 }
2266 } else {
2267 k = word;
2268 word = NULL;
2269 }
2270
2271 if (!env_assignment_is_valid(k)) {
2272 log_syntax(unit, LOG_ERR, filename, line, 0,
2273 "Invalid environment assignment, ignoring: %s", k);
2274 continue;
2275 }
2276
2277 r = strv_env_replace(env, k);
2278 if (r < 0)
2279 return log_oom();
2280 k = NULL;
2281 }
2282 }
2283
2284 int config_parse_pass_environ(const char *unit,
2285 const char *filename,
2286 unsigned line,
2287 const char *section,
2288 unsigned section_line,
2289 const char *lvalue,
2290 int ltype,
2291 const char *rvalue,
2292 void *data,
2293 void *userdata) {
2294
2295 const char *whole_rvalue = rvalue;
2296 char*** passenv = data;
2297 _cleanup_strv_free_ char **n = NULL;
2298 size_t nlen = 0, nbufsize = 0;
2299 int r;
2300
2301 assert(filename);
2302 assert(lvalue);
2303 assert(rvalue);
2304 assert(data);
2305
2306 if (isempty(rvalue)) {
2307 /* Empty assignment resets the list */
2308 *passenv = strv_free(*passenv);
2309 return 0;
2310 }
2311
2312 for (;;) {
2313 _cleanup_free_ char *word = NULL;
2314
2315 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
2316 if (r == 0)
2317 break;
2318 if (r == -ENOMEM)
2319 return log_oom();
2320 if (r < 0) {
2321 log_syntax(unit, LOG_ERR, filename, line, r,
2322 "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
2323 break;
2324 }
2325
2326 if (!env_name_is_valid(word)) {
2327 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2328 "Invalid environment name for %s, ignoring: %s", lvalue, word);
2329 continue;
2330 }
2331
2332 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2333 return log_oom();
2334 n[nlen++] = word;
2335 n[nlen] = NULL;
2336 word = NULL;
2337 }
2338
2339 if (n) {
2340 r = strv_extend_strv(passenv, n, true);
2341 if (r < 0)
2342 return r;
2343 }
2344
2345 return 0;
2346 }
2347
2348 int config_parse_ip_tos(const char *unit,
2349 const char *filename,
2350 unsigned line,
2351 const char *section,
2352 unsigned section_line,
2353 const char *lvalue,
2354 int ltype,
2355 const char *rvalue,
2356 void *data,
2357 void *userdata) {
2358
2359 int *ip_tos = data, x;
2360
2361 assert(filename);
2362 assert(lvalue);
2363 assert(rvalue);
2364 assert(data);
2365
2366 x = ip_tos_from_string(rvalue);
2367 if (x < 0) {
2368 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue);
2369 return 0;
2370 }
2371
2372 *ip_tos = x;
2373 return 0;
2374 }
2375
2376 int config_parse_unit_condition_path(
2377 const char *unit,
2378 const char *filename,
2379 unsigned line,
2380 const char *section,
2381 unsigned section_line,
2382 const char *lvalue,
2383 int ltype,
2384 const char *rvalue,
2385 void *data,
2386 void *userdata) {
2387
2388 _cleanup_free_ char *p = NULL;
2389 Condition **list = data, *c;
2390 ConditionType t = ltype;
2391 bool trigger, negate;
2392 Unit *u = userdata;
2393 int r;
2394
2395 assert(filename);
2396 assert(lvalue);
2397 assert(rvalue);
2398 assert(data);
2399
2400 if (isempty(rvalue)) {
2401 /* Empty assignment resets the list */
2402 *list = condition_free_list(*list);
2403 return 0;
2404 }
2405
2406 trigger = rvalue[0] == '|';
2407 if (trigger)
2408 rvalue++;
2409
2410 negate = rvalue[0] == '!';
2411 if (negate)
2412 rvalue++;
2413
2414 r = unit_full_printf(u, rvalue, &p);
2415 if (r < 0) {
2416 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2417 return 0;
2418 }
2419
2420 if (!path_is_absolute(p)) {
2421 log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p);
2422 return 0;
2423 }
2424
2425 c = condition_new(t, p, trigger, negate);
2426 if (!c)
2427 return log_oom();
2428
2429 LIST_PREPEND(conditions, *list, c);
2430 return 0;
2431 }
2432
2433 int config_parse_unit_condition_string(
2434 const char *unit,
2435 const char *filename,
2436 unsigned line,
2437 const char *section,
2438 unsigned section_line,
2439 const char *lvalue,
2440 int ltype,
2441 const char *rvalue,
2442 void *data,
2443 void *userdata) {
2444
2445 _cleanup_free_ char *s = NULL;
2446 Condition **list = data, *c;
2447 ConditionType t = ltype;
2448 bool trigger, negate;
2449 Unit *u = userdata;
2450 int r;
2451
2452 assert(filename);
2453 assert(lvalue);
2454 assert(rvalue);
2455 assert(data);
2456
2457 if (isempty(rvalue)) {
2458 /* Empty assignment resets the list */
2459 *list = condition_free_list(*list);
2460 return 0;
2461 }
2462
2463 trigger = rvalue[0] == '|';
2464 if (trigger)
2465 rvalue++;
2466
2467 negate = rvalue[0] == '!';
2468 if (negate)
2469 rvalue++;
2470
2471 r = unit_full_printf(u, rvalue, &s);
2472 if (r < 0) {
2473 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2474 return 0;
2475 }
2476
2477 c = condition_new(t, s, trigger, negate);
2478 if (!c)
2479 return log_oom();
2480
2481 LIST_PREPEND(conditions, *list, c);
2482 return 0;
2483 }
2484
2485 int config_parse_unit_condition_null(
2486 const char *unit,
2487 const char *filename,
2488 unsigned line,
2489 const char *section,
2490 unsigned section_line,
2491 const char *lvalue,
2492 int ltype,
2493 const char *rvalue,
2494 void *data,
2495 void *userdata) {
2496
2497 Condition **list = data, *c;
2498 bool trigger, negate;
2499 int b;
2500
2501 assert(filename);
2502 assert(lvalue);
2503 assert(rvalue);
2504 assert(data);
2505
2506 if (isempty(rvalue)) {
2507 /* Empty assignment resets the list */
2508 *list = condition_free_list(*list);
2509 return 0;
2510 }
2511
2512 trigger = rvalue[0] == '|';
2513 if (trigger)
2514 rvalue++;
2515
2516 negate = rvalue[0] == '!';
2517 if (negate)
2518 rvalue++;
2519
2520 b = parse_boolean(rvalue);
2521 if (b < 0) {
2522 log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
2523 return 0;
2524 }
2525
2526 if (!b)
2527 negate = !negate;
2528
2529 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2530 if (!c)
2531 return log_oom();
2532
2533 LIST_PREPEND(conditions, *list, c);
2534 return 0;
2535 }
2536
2537 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
2538 DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier");
2539
2540 int config_parse_unit_requires_mounts_for(
2541 const char *unit,
2542 const char *filename,
2543 unsigned line,
2544 const char *section,
2545 unsigned section_line,
2546 const char *lvalue,
2547 int ltype,
2548 const char *rvalue,
2549 void *data,
2550 void *userdata) {
2551
2552 Unit *u = userdata;
2553 const char *p;
2554 int r;
2555
2556 assert(filename);
2557 assert(lvalue);
2558 assert(rvalue);
2559 assert(data);
2560
2561 for (p = rvalue;; ) {
2562 _cleanup_free_ char *word = NULL, *resolved = NULL;
2563
2564 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2565 if (r == 0)
2566 return 0;
2567 if (r == -ENOMEM)
2568 return log_oom();
2569 if (r < 0) {
2570 log_syntax(unit, LOG_WARNING, filename, line, r,
2571 "Invalid syntax, ignoring: %s", rvalue);
2572 return 0;
2573 }
2574
2575 if (!utf8_is_valid(word)) {
2576 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
2577 continue;
2578 }
2579
2580 r = unit_full_printf(u, word, &resolved);
2581 if (r < 0) {
2582 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit name \"%s\", ignoring: %m", word);
2583 continue;
2584 }
2585
2586 r = unit_require_mounts_for(u, resolved);
2587 if (r < 0) {
2588 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount \"%s\", ignoring: %m", resolved);
2589 continue;
2590 }
2591 }
2592 }
2593
2594 int config_parse_documentation(const char *unit,
2595 const char *filename,
2596 unsigned line,
2597 const char *section,
2598 unsigned section_line,
2599 const char *lvalue,
2600 int ltype,
2601 const char *rvalue,
2602 void *data,
2603 void *userdata) {
2604
2605 Unit *u = userdata;
2606 int r;
2607 char **a, **b;
2608
2609 assert(filename);
2610 assert(lvalue);
2611 assert(rvalue);
2612 assert(u);
2613
2614 if (isempty(rvalue)) {
2615 /* Empty assignment resets the list */
2616 u->documentation = strv_free(u->documentation);
2617 return 0;
2618 }
2619
2620 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
2621 rvalue, data, userdata);
2622 if (r < 0)
2623 return r;
2624
2625 for (a = b = u->documentation; a && *a; a++) {
2626
2627 if (documentation_url_is_valid(*a))
2628 *(b++) = *a;
2629 else {
2630 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a);
2631 free(*a);
2632 }
2633 }
2634 if (b)
2635 *b = NULL;
2636
2637 return r;
2638 }
2639
2640 #ifdef HAVE_SECCOMP
2641
2642 static int syscall_filter_parse_one(
2643 const char *unit,
2644 const char *filename,
2645 unsigned line,
2646 ExecContext *c,
2647 bool invert,
2648 const char *t,
2649 bool warn) {
2650 int r;
2651
2652 if (t[0] == '@') {
2653 const SyscallFilterSet *set;
2654 const char *i;
2655
2656 set = syscall_filter_set_find(t);
2657 if (!set) {
2658 if (warn)
2659 log_syntax(unit, LOG_WARNING, filename, line, 0, "Don't know system call group, ignoring: %s", t);
2660 return 0;
2661 }
2662
2663 NULSTR_FOREACH(i, set->value) {
2664 r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false);
2665 if (r < 0)
2666 return r;
2667 }
2668 } else {
2669 int id;
2670
2671 id = seccomp_syscall_resolve_name(t);
2672 if (id == __NR_SCMP_ERROR) {
2673 if (warn)
2674 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
2675 return 0;
2676 }
2677
2678 /* If we previously wanted to forbid a syscall and now
2679 * we want to allow it, then remove it from the list
2680 */
2681 if (!invert == c->syscall_whitelist) {
2682 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2683 if (r == 0)
2684 return 0;
2685 if (r < 0)
2686 return log_oom();
2687 } else
2688 (void) set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
2689 }
2690
2691 return 0;
2692 }
2693
2694 int config_parse_syscall_filter(
2695 const char *unit,
2696 const char *filename,
2697 unsigned line,
2698 const char *section,
2699 unsigned section_line,
2700 const char *lvalue,
2701 int ltype,
2702 const char *rvalue,
2703 void *data,
2704 void *userdata) {
2705
2706 ExecContext *c = data;
2707 Unit *u = userdata;
2708 bool invert = false;
2709 const char *p;
2710 int r;
2711
2712 assert(filename);
2713 assert(lvalue);
2714 assert(rvalue);
2715 assert(u);
2716
2717 if (isempty(rvalue)) {
2718 /* Empty assignment resets the list */
2719 c->syscall_filter = set_free(c->syscall_filter);
2720 c->syscall_whitelist = false;
2721 return 0;
2722 }
2723
2724 if (rvalue[0] == '~') {
2725 invert = true;
2726 rvalue++;
2727 }
2728
2729 if (!c->syscall_filter) {
2730 c->syscall_filter = set_new(NULL);
2731 if (!c->syscall_filter)
2732 return log_oom();
2733
2734 if (invert)
2735 /* Allow everything but the ones listed */
2736 c->syscall_whitelist = false;
2737 else {
2738 /* Allow nothing but the ones listed */
2739 c->syscall_whitelist = true;
2740
2741 /* Accept default syscalls if we are on a whitelist */
2742 r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false);
2743 if (r < 0)
2744 return r;
2745 }
2746 }
2747
2748 p = rvalue;
2749 for (;;) {
2750 _cleanup_free_ char *word = NULL;
2751
2752 r = extract_first_word(&p, &word, NULL, 0);
2753 if (r == 0)
2754 break;
2755 if (r == -ENOMEM)
2756 return log_oom();
2757 if (r < 0) {
2758 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
2759 break;
2760 }
2761
2762 r = syscall_filter_parse_one(unit, filename, line, c, invert, word, true);
2763 if (r < 0)
2764 return r;
2765 }
2766
2767 return 0;
2768 }
2769
2770 int config_parse_syscall_archs(
2771 const char *unit,
2772 const char *filename,
2773 unsigned line,
2774 const char *section,
2775 unsigned section_line,
2776 const char *lvalue,
2777 int ltype,
2778 const char *rvalue,
2779 void *data,
2780 void *userdata) {
2781
2782 Set **archs = data;
2783 const char *p;
2784 int r;
2785
2786 if (isempty(rvalue)) {
2787 *archs = set_free(*archs);
2788 return 0;
2789 }
2790
2791 r = set_ensure_allocated(archs, NULL);
2792 if (r < 0)
2793 return log_oom();
2794
2795 for (p = rvalue;;) {
2796 _cleanup_free_ char *word = NULL;
2797 uint32_t a;
2798
2799 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2800 if (r == 0)
2801 return 0;
2802 if (r == -ENOMEM)
2803 return log_oom();
2804 if (r < 0) {
2805 log_syntax(unit, LOG_WARNING, filename, line, r,
2806 "Invalid syntax, ignoring: %s", rvalue);
2807 return 0;
2808 }
2809
2810 r = seccomp_arch_from_string(word, &a);
2811 if (r < 0) {
2812 log_syntax(unit, LOG_ERR, filename, line, r,
2813 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
2814 continue;
2815 }
2816
2817 r = set_put(*archs, UINT32_TO_PTR(a + 1));
2818 if (r < 0)
2819 return log_oom();
2820 }
2821 }
2822
2823 int config_parse_syscall_errno(
2824 const char *unit,
2825 const char *filename,
2826 unsigned line,
2827 const char *section,
2828 unsigned section_line,
2829 const char *lvalue,
2830 int ltype,
2831 const char *rvalue,
2832 void *data,
2833 void *userdata) {
2834
2835 ExecContext *c = data;
2836 int e;
2837
2838 assert(filename);
2839 assert(lvalue);
2840 assert(rvalue);
2841
2842 if (isempty(rvalue)) {
2843 /* Empty assignment resets to KILL */
2844 c->syscall_errno = 0;
2845 return 0;
2846 }
2847
2848 e = errno_from_name(rvalue);
2849 if (e < 0) {
2850 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
2851 return 0;
2852 }
2853
2854 c->syscall_errno = e;
2855 return 0;
2856 }
2857
2858 int config_parse_address_families(
2859 const char *unit,
2860 const char *filename,
2861 unsigned line,
2862 const char *section,
2863 unsigned section_line,
2864 const char *lvalue,
2865 int ltype,
2866 const char *rvalue,
2867 void *data,
2868 void *userdata) {
2869
2870 ExecContext *c = data;
2871 bool invert = false;
2872 const char *p;
2873 int r;
2874
2875 assert(filename);
2876 assert(lvalue);
2877 assert(rvalue);
2878
2879 if (isempty(rvalue)) {
2880 /* Empty assignment resets the list */
2881 c->address_families = set_free(c->address_families);
2882 c->address_families_whitelist = false;
2883 return 0;
2884 }
2885
2886 if (rvalue[0] == '~') {
2887 invert = true;
2888 rvalue++;
2889 }
2890
2891 if (!c->address_families) {
2892 c->address_families = set_new(NULL);
2893 if (!c->address_families)
2894 return log_oom();
2895
2896 c->address_families_whitelist = !invert;
2897 }
2898
2899 for (p = rvalue;;) {
2900 _cleanup_free_ char *word = NULL;
2901 int af;
2902
2903 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2904 if (r == 0)
2905 return 0;
2906 if (r == -ENOMEM)
2907 return log_oom();
2908 if (r < 0) {
2909 log_syntax(unit, LOG_WARNING, filename, line, r,
2910 "Invalid syntax, ignoring: %s", rvalue);
2911 return 0;
2912 }
2913
2914 af = af_from_name(word);
2915 if (af <= 0) {
2916 log_syntax(unit, LOG_ERR, filename, line, 0,
2917 "Failed to parse address family \"%s\", ignoring: %m", word);
2918 continue;
2919 }
2920
2921 /* If we previously wanted to forbid an address family and now
2922 * we want to allow it, then just remove it from the list.
2923 */
2924 if (!invert == c->address_families_whitelist) {
2925 r = set_put(c->address_families, INT_TO_PTR(af));
2926 if (r < 0)
2927 return log_oom();
2928 } else
2929 set_remove(c->address_families, INT_TO_PTR(af));
2930 }
2931 }
2932
2933 int config_parse_restrict_namespaces(
2934 const char *unit,
2935 const char *filename,
2936 unsigned line,
2937 const char *section,
2938 unsigned section_line,
2939 const char *lvalue,
2940 int ltype,
2941 const char *rvalue,
2942 void *data,
2943 void *userdata) {
2944
2945 ExecContext *c = data;
2946 bool invert = false;
2947 int r;
2948
2949 if (isempty(rvalue)) {
2950 /* Reset to the default. */
2951 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
2952 return 0;
2953 }
2954
2955 if (rvalue[0] == '~') {
2956 invert = true;
2957 rvalue++;
2958 }
2959
2960 r = parse_boolean(rvalue);
2961 if (r > 0)
2962 c->restrict_namespaces = 0;
2963 else if (r == 0)
2964 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
2965 else {
2966 /* Not a boolean argument, in this case it's a list of namespace types. */
2967
2968 r = namespace_flag_from_string_many(rvalue, &c->restrict_namespaces);
2969 if (r < 0) {
2970 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
2971 return 0;
2972 }
2973 }
2974
2975 if (invert)
2976 c->restrict_namespaces = (~c->restrict_namespaces) & NAMESPACE_FLAGS_ALL;
2977
2978 return 0;
2979 }
2980 #endif
2981
2982 int config_parse_unit_slice(
2983 const char *unit,
2984 const char *filename,
2985 unsigned line,
2986 const char *section,
2987 unsigned section_line,
2988 const char *lvalue,
2989 int ltype,
2990 const char *rvalue,
2991 void *data,
2992 void *userdata) {
2993
2994 _cleanup_free_ char *k = NULL;
2995 Unit *u = userdata, *slice = NULL;
2996 int r;
2997
2998 assert(filename);
2999 assert(lvalue);
3000 assert(rvalue);
3001 assert(u);
3002
3003 r = unit_name_printf(u, rvalue, &k);
3004 if (r < 0) {
3005 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
3006 return 0;
3007 }
3008
3009 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
3010 if (r < 0) {
3011 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k);
3012 return 0;
3013 }
3014
3015 r = unit_set_slice(u, slice);
3016 if (r < 0) {
3017 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id);
3018 return 0;
3019 }
3020
3021 return 0;
3022 }
3023
3024 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
3025
3026 int config_parse_cpu_weight(
3027 const char *unit,
3028 const char *filename,
3029 unsigned line,
3030 const char *section,
3031 unsigned section_line,
3032 const char *lvalue,
3033 int ltype,
3034 const char *rvalue,
3035 void *data,
3036 void *userdata) {
3037
3038 uint64_t *weight = data;
3039 int r;
3040
3041 assert(filename);
3042 assert(lvalue);
3043 assert(rvalue);
3044
3045 r = cg_weight_parse(rvalue, weight);
3046 if (r < 0) {
3047 log_syntax(unit, LOG_ERR, filename, line, r, "CPU weight '%s' invalid. Ignoring.", rvalue);
3048 return 0;
3049 }
3050
3051 return 0;
3052 }
3053
3054 int config_parse_cpu_shares(
3055 const char *unit,
3056 const char *filename,
3057 unsigned line,
3058 const char *section,
3059 unsigned section_line,
3060 const char *lvalue,
3061 int ltype,
3062 const char *rvalue,
3063 void *data,
3064 void *userdata) {
3065
3066 uint64_t *shares = data;
3067 int r;
3068
3069 assert(filename);
3070 assert(lvalue);
3071 assert(rvalue);
3072
3073 r = cg_cpu_shares_parse(rvalue, shares);
3074 if (r < 0) {
3075 log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
3076 return 0;
3077 }
3078
3079 return 0;
3080 }
3081
3082 int config_parse_cpu_quota(
3083 const char *unit,
3084 const char *filename,
3085 unsigned line,
3086 const char *section,
3087 unsigned section_line,
3088 const char *lvalue,
3089 int ltype,
3090 const char *rvalue,
3091 void *data,
3092 void *userdata) {
3093
3094 CGroupContext *c = data;
3095 int r;
3096
3097 assert(filename);
3098 assert(lvalue);
3099 assert(rvalue);
3100
3101 if (isempty(rvalue)) {
3102 c->cpu_quota_per_sec_usec = USEC_INFINITY;
3103 return 0;
3104 }
3105
3106 r = parse_percent_unbounded(rvalue);
3107 if (r <= 0) {
3108 log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
3109 return 0;
3110 }
3111
3112 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
3113 return 0;
3114 }
3115
3116 int config_parse_memory_limit(
3117 const char *unit,
3118 const char *filename,
3119 unsigned line,
3120 const char *section,
3121 unsigned section_line,
3122 const char *lvalue,
3123 int ltype,
3124 const char *rvalue,
3125 void *data,
3126 void *userdata) {
3127
3128 CGroupContext *c = data;
3129 uint64_t bytes = CGROUP_LIMIT_MAX;
3130 int r;
3131
3132 if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
3133
3134 r = parse_percent(rvalue);
3135 if (r < 0) {
3136 r = parse_size(rvalue, 1024, &bytes);
3137 if (r < 0) {
3138 log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
3139 return 0;
3140 }
3141 } else
3142 bytes = physical_memory_scale(r, 100U);
3143
3144 if (bytes <= 0 || bytes >= UINT64_MAX) {
3145 log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range. Ignoring.", rvalue);
3146 return 0;
3147 }
3148 }
3149
3150 if (streq(lvalue, "MemoryLow"))
3151 c->memory_low = bytes;
3152 else if (streq(lvalue, "MemoryHigh"))
3153 c->memory_high = bytes;
3154 else if (streq(lvalue, "MemoryMax"))
3155 c->memory_max = bytes;
3156 else if (streq(lvalue, "MemorySwapMax"))
3157 c->memory_swap_max = bytes;
3158 else if (streq(lvalue, "MemoryLimit"))
3159 c->memory_limit = bytes;
3160 else
3161 return -EINVAL;
3162
3163 return 0;
3164 }
3165
3166 int config_parse_tasks_max(
3167 const char *unit,
3168 const char *filename,
3169 unsigned line,
3170 const char *section,
3171 unsigned section_line,
3172 const char *lvalue,
3173 int ltype,
3174 const char *rvalue,
3175 void *data,
3176 void *userdata) {
3177
3178 uint64_t *tasks_max = data, v;
3179 Unit *u = userdata;
3180 int r;
3181
3182 if (isempty(rvalue)) {
3183 *tasks_max = u->manager->default_tasks_max;
3184 return 0;
3185 }
3186
3187 if (streq(rvalue, "infinity")) {
3188 *tasks_max = CGROUP_LIMIT_MAX;
3189 return 0;
3190 }
3191
3192 r = parse_percent(rvalue);
3193 if (r < 0) {
3194 r = safe_atou64(rvalue, &v);
3195 if (r < 0) {
3196 log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
3197 return 0;
3198 }
3199 } else
3200 v = system_tasks_max_scale(r, 100U);
3201
3202 if (v <= 0 || v >= UINT64_MAX) {
3203 log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue);
3204 return 0;
3205 }
3206
3207 *tasks_max = v;
3208 return 0;
3209 }
3210
3211 int config_parse_device_allow(
3212 const char *unit,
3213 const char *filename,
3214 unsigned line,
3215 const char *section,
3216 unsigned section_line,
3217 const char *lvalue,
3218 int ltype,
3219 const char *rvalue,
3220 void *data,
3221 void *userdata) {
3222
3223 _cleanup_free_ char *path = NULL, *t = NULL;
3224 CGroupContext *c = data;
3225 CGroupDeviceAllow *a;
3226 const char *m = NULL;
3227 size_t n;
3228 int r;
3229
3230 if (isempty(rvalue)) {
3231 while (c->device_allow)
3232 cgroup_context_free_device_allow(c, c->device_allow);
3233
3234 return 0;
3235 }
3236
3237 r = unit_full_printf(userdata, rvalue, &t);
3238 if(r < 0) {
3239 log_syntax(unit, LOG_WARNING, filename, line, r,
3240 "Failed to resolve specifiers in %s, ignoring: %m",
3241 rvalue);
3242 }
3243
3244 n = strcspn(t, WHITESPACE);
3245
3246 path = strndup(t, n);
3247 if (!path)
3248 return log_oom();
3249
3250 if (!is_deviceallow_pattern(path)) {
3251 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3252 return 0;
3253 }
3254
3255 m = t + n + strspn(t + n, WHITESPACE);
3256 if (isempty(m))
3257 m = "rwm";
3258
3259 if (!in_charset(m, "rwm")) {
3260 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
3261 return 0;
3262 }
3263
3264 a = new0(CGroupDeviceAllow, 1);
3265 if (!a)
3266 return log_oom();
3267
3268 a->path = path;
3269 path = NULL;
3270 a->r = !!strchr(m, 'r');
3271 a->w = !!strchr(m, 'w');
3272 a->m = !!strchr(m, 'm');
3273
3274 LIST_PREPEND(device_allow, c->device_allow, a);
3275 return 0;
3276 }
3277
3278 int config_parse_io_weight(
3279 const char *unit,
3280 const char *filename,
3281 unsigned line,
3282 const char *section,
3283 unsigned section_line,
3284 const char *lvalue,
3285 int ltype,
3286 const char *rvalue,
3287 void *data,
3288 void *userdata) {
3289
3290 uint64_t *weight = data;
3291 int r;
3292
3293 assert(filename);
3294 assert(lvalue);
3295 assert(rvalue);
3296
3297 r = cg_weight_parse(rvalue, weight);
3298 if (r < 0) {
3299 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue);
3300 return 0;
3301 }
3302
3303 return 0;
3304 }
3305
3306 int config_parse_io_device_weight(
3307 const char *unit,
3308 const char *filename,
3309 unsigned line,
3310 const char *section,
3311 unsigned section_line,
3312 const char *lvalue,
3313 int ltype,
3314 const char *rvalue,
3315 void *data,
3316 void *userdata) {
3317
3318 _cleanup_free_ char *path = NULL;
3319 CGroupIODeviceWeight *w;
3320 CGroupContext *c = data;
3321 const char *weight;
3322 uint64_t u;
3323 size_t n;
3324 int r;
3325
3326 assert(filename);
3327 assert(lvalue);
3328 assert(rvalue);
3329
3330 if (isempty(rvalue)) {
3331 while (c->io_device_weights)
3332 cgroup_context_free_io_device_weight(c, c->io_device_weights);
3333
3334 return 0;
3335 }
3336
3337 n = strcspn(rvalue, WHITESPACE);
3338 weight = rvalue + n;
3339 weight += strspn(weight, WHITESPACE);
3340
3341 if (isempty(weight)) {
3342 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
3343 return 0;
3344 }
3345
3346 path = strndup(rvalue, n);
3347 if (!path)
3348 return log_oom();
3349
3350 if (!path_startswith(path, "/dev")) {
3351 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3352 return 0;
3353 }
3354
3355 r = cg_weight_parse(weight, &u);
3356 if (r < 0) {
3357 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight);
3358 return 0;
3359 }
3360
3361 assert(u != CGROUP_WEIGHT_INVALID);
3362
3363 w = new0(CGroupIODeviceWeight, 1);
3364 if (!w)
3365 return log_oom();
3366
3367 w->path = path;
3368 path = NULL;
3369
3370 w->weight = u;
3371
3372 LIST_PREPEND(device_weights, c->io_device_weights, w);
3373 return 0;
3374 }
3375
3376 int config_parse_io_limit(
3377 const char *unit,
3378 const char *filename,
3379 unsigned line,
3380 const char *section,
3381 unsigned section_line,
3382 const char *lvalue,
3383 int ltype,
3384 const char *rvalue,
3385 void *data,
3386 void *userdata) {
3387
3388 _cleanup_free_ char *path = NULL;
3389 CGroupIODeviceLimit *l = NULL, *t;
3390 CGroupContext *c = data;
3391 CGroupIOLimitType type;
3392 const char *limit;
3393 uint64_t num;
3394 size_t n;
3395 int r;
3396
3397 assert(filename);
3398 assert(lvalue);
3399 assert(rvalue);
3400
3401 type = cgroup_io_limit_type_from_string(lvalue);
3402 assert(type >= 0);
3403
3404 if (isempty(rvalue)) {
3405 LIST_FOREACH(device_limits, l, c->io_device_limits)
3406 l->limits[type] = cgroup_io_limit_defaults[type];
3407 return 0;
3408 }
3409
3410 n = strcspn(rvalue, WHITESPACE);
3411 limit = rvalue + n;
3412 limit += strspn(limit, WHITESPACE);
3413
3414 if (!*limit) {
3415 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3416 return 0;
3417 }
3418
3419 path = strndup(rvalue, n);
3420 if (!path)
3421 return log_oom();
3422
3423 if (!path_startswith(path, "/dev")) {
3424 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3425 return 0;
3426 }
3427
3428 if (streq("infinity", limit)) {
3429 num = CGROUP_LIMIT_MAX;
3430 } else {
3431 r = parse_size(limit, 1000, &num);
3432 if (r < 0 || num <= 0) {
3433 log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue);
3434 return 0;
3435 }
3436 }
3437
3438 LIST_FOREACH(device_limits, t, c->io_device_limits) {
3439 if (path_equal(path, t->path)) {
3440 l = t;
3441 break;
3442 }
3443 }
3444
3445 if (!l) {
3446 CGroupIOLimitType ttype;
3447
3448 l = new0(CGroupIODeviceLimit, 1);
3449 if (!l)
3450 return log_oom();
3451
3452 l->path = path;
3453 path = NULL;
3454 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
3455 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
3456
3457 LIST_PREPEND(device_limits, c->io_device_limits, l);
3458 }
3459
3460 l->limits[type] = num;
3461
3462 return 0;
3463 }
3464
3465 int config_parse_blockio_weight(
3466 const char *unit,
3467 const char *filename,
3468 unsigned line,
3469 const char *section,
3470 unsigned section_line,
3471 const char *lvalue,
3472 int ltype,
3473 const char *rvalue,
3474 void *data,
3475 void *userdata) {
3476
3477 uint64_t *weight = data;
3478 int r;
3479
3480 assert(filename);
3481 assert(lvalue);
3482 assert(rvalue);
3483
3484 r = cg_blkio_weight_parse(rvalue, weight);
3485 if (r < 0) {
3486 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
3487 return 0;
3488 }
3489
3490 return 0;
3491 }
3492
3493 int config_parse_blockio_device_weight(
3494 const char *unit,
3495 const char *filename,
3496 unsigned line,
3497 const char *section,
3498 unsigned section_line,
3499 const char *lvalue,
3500 int ltype,
3501 const char *rvalue,
3502 void *data,
3503 void *userdata) {
3504
3505 _cleanup_free_ char *path = NULL;
3506 CGroupBlockIODeviceWeight *w;
3507 CGroupContext *c = data;
3508 const char *weight;
3509 uint64_t u;
3510 size_t n;
3511 int r;
3512
3513 assert(filename);
3514 assert(lvalue);
3515 assert(rvalue);
3516
3517 if (isempty(rvalue)) {
3518 while (c->blockio_device_weights)
3519 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
3520
3521 return 0;
3522 }
3523
3524 n = strcspn(rvalue, WHITESPACE);
3525 weight = rvalue + n;
3526 weight += strspn(weight, WHITESPACE);
3527
3528 if (isempty(weight)) {
3529 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
3530 return 0;
3531 }
3532
3533 path = strndup(rvalue, n);
3534 if (!path)
3535 return log_oom();
3536
3537 if (!path_startswith(path, "/dev")) {
3538 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3539 return 0;
3540 }
3541
3542 r = cg_blkio_weight_parse(weight, &u);
3543 if (r < 0) {
3544 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
3545 return 0;
3546 }
3547
3548 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
3549
3550 w = new0(CGroupBlockIODeviceWeight, 1);
3551 if (!w)
3552 return log_oom();
3553
3554 w->path = path;
3555 path = NULL;
3556
3557 w->weight = u;
3558
3559 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
3560 return 0;
3561 }
3562
3563 int config_parse_blockio_bandwidth(
3564 const char *unit,
3565 const char *filename,
3566 unsigned line,
3567 const char *section,
3568 unsigned section_line,
3569 const char *lvalue,
3570 int ltype,
3571 const char *rvalue,
3572 void *data,
3573 void *userdata) {
3574
3575 _cleanup_free_ char *path = NULL;
3576 CGroupBlockIODeviceBandwidth *b = NULL, *t;
3577 CGroupContext *c = data;
3578 const char *bandwidth;
3579 uint64_t bytes;
3580 bool read;
3581 size_t n;
3582 int r;
3583
3584 assert(filename);
3585 assert(lvalue);
3586 assert(rvalue);
3587
3588 read = streq("BlockIOReadBandwidth", lvalue);
3589
3590 if (isempty(rvalue)) {
3591 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
3592 b->rbps = CGROUP_LIMIT_MAX;
3593 b->wbps = CGROUP_LIMIT_MAX;
3594 }
3595 return 0;
3596 }
3597
3598 n = strcspn(rvalue, WHITESPACE);
3599 bandwidth = rvalue + n;
3600 bandwidth += strspn(bandwidth, WHITESPACE);
3601
3602 if (!*bandwidth) {
3603 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3604 return 0;
3605 }
3606
3607 path = strndup(rvalue, n);
3608 if (!path)
3609 return log_oom();
3610
3611 if (!path_startswith(path, "/dev")) {
3612 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3613 return 0;
3614 }
3615
3616 r = parse_size(bandwidth, 1000, &bytes);
3617 if (r < 0 || bytes <= 0) {
3618 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
3619 return 0;
3620 }
3621
3622 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
3623 if (path_equal(path, t->path)) {
3624 b = t;
3625 break;
3626 }
3627 }
3628
3629 if (!t) {
3630 b = new0(CGroupBlockIODeviceBandwidth, 1);
3631 if (!b)
3632 return log_oom();
3633
3634 b->path = path;
3635 path = NULL;
3636 b->rbps = CGROUP_LIMIT_MAX;
3637 b->wbps = CGROUP_LIMIT_MAX;
3638
3639 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
3640 }
3641
3642 if (read)
3643 b->rbps = bytes;
3644 else
3645 b->wbps = bytes;
3646
3647 return 0;
3648 }
3649
3650 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
3651
3652 int config_parse_job_mode_isolate(
3653 const char *unit,
3654 const char *filename,
3655 unsigned line,
3656 const char *section,
3657 unsigned section_line,
3658 const char *lvalue,
3659 int ltype,
3660 const char *rvalue,
3661 void *data,
3662 void *userdata) {
3663
3664 JobMode *m = data;
3665 int r;
3666
3667 assert(filename);
3668 assert(lvalue);
3669 assert(rvalue);
3670
3671 r = parse_boolean(rvalue);
3672 if (r < 0) {
3673 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
3674 return 0;
3675 }
3676
3677 *m = r ? JOB_ISOLATE : JOB_REPLACE;
3678 return 0;
3679 }
3680
3681 int config_parse_runtime_directory(
3682 const char *unit,
3683 const char *filename,
3684 unsigned line,
3685 const char *section,
3686 unsigned section_line,
3687 const char *lvalue,
3688 int ltype,
3689 const char *rvalue,
3690 void *data,
3691 void *userdata) {
3692
3693 char***rt = data;
3694 Unit *u = userdata;
3695 const char *p;
3696 int r;
3697
3698 assert(filename);
3699 assert(lvalue);
3700 assert(rvalue);
3701 assert(data);
3702
3703 if (isempty(rvalue)) {
3704 /* Empty assignment resets the list */
3705 *rt = strv_free(*rt);
3706 return 0;
3707 }
3708
3709 for (p = rvalue;;) {
3710 _cleanup_free_ char *word = NULL, *k = NULL;
3711
3712 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
3713 if (r == 0)
3714 return 0;
3715 if (r == -ENOMEM)
3716 return log_oom();
3717 if (r < 0) {
3718 log_syntax(unit, LOG_WARNING, filename, line, r,
3719 "Invalid syntax, ignoring: %s", rvalue);
3720 return 0;
3721 }
3722
3723 r = unit_full_printf(u, word, &k);
3724 if (r < 0) {
3725 log_syntax(unit, LOG_ERR, filename, line, r,
3726 "Failed to resolve specifiers in \"%s\", ignoring: %m", word);
3727 continue;
3728 }
3729
3730 if (!filename_is_valid(k)) {
3731 log_syntax(unit, LOG_ERR, filename, line, 0,
3732 "Runtime directory is not valid, ignoring assignment: %s", rvalue);
3733 continue;
3734 }
3735
3736 r = strv_push(rt, k);
3737 if (r < 0)
3738 return log_oom();
3739 k = NULL;
3740 }
3741 }
3742
3743 int config_parse_set_status(
3744 const char *unit,
3745 const char *filename,
3746 unsigned line,
3747 const char *section,
3748 unsigned section_line,
3749 const char *lvalue,
3750 int ltype,
3751 const char *rvalue,
3752 void *data,
3753 void *userdata) {
3754
3755 size_t l;
3756 const char *word, *state;
3757 int r;
3758 ExitStatusSet *status_set = data;
3759
3760 assert(filename);
3761 assert(lvalue);
3762 assert(rvalue);
3763 assert(data);
3764
3765 /* Empty assignment resets the list */
3766 if (isempty(rvalue)) {
3767 exit_status_set_free(status_set);
3768 return 0;
3769 }
3770
3771 FOREACH_WORD(word, l, rvalue, state) {
3772 _cleanup_free_ char *temp;
3773 int val;
3774 Set **set;
3775
3776 temp = strndup(word, l);
3777 if (!temp)
3778 return log_oom();
3779
3780 r = safe_atoi(temp, &val);
3781 if (r < 0) {
3782 val = signal_from_string_try_harder(temp);
3783
3784 if (val <= 0) {
3785 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
3786 continue;
3787 }
3788 set = &status_set->signal;
3789 } else {
3790 if (val < 0 || val > 255) {
3791 log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
3792 continue;
3793 }
3794 set = &status_set->status;
3795 }
3796
3797 r = set_ensure_allocated(set, NULL);
3798 if (r < 0)
3799 return log_oom();
3800
3801 r = set_put(*set, INT_TO_PTR(val));
3802 if (r < 0) {
3803 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
3804 return r;
3805 }
3806 }
3807 if (!isempty(state))
3808 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3809
3810 return 0;
3811 }
3812
3813 int config_parse_namespace_path_strv(
3814 const char *unit,
3815 const char *filename,
3816 unsigned line,
3817 const char *section,
3818 unsigned section_line,
3819 const char *lvalue,
3820 int ltype,
3821 const char *rvalue,
3822 void *data,
3823 void *userdata) {
3824
3825 Unit *u = userdata;
3826 char*** sv = data;
3827 const char *cur;
3828 int r;
3829
3830 assert(filename);
3831 assert(lvalue);
3832 assert(rvalue);
3833 assert(data);
3834
3835 if (isempty(rvalue)) {
3836 /* Empty assignment resets the list */
3837 *sv = strv_free(*sv);
3838 return 0;
3839 }
3840
3841 cur = rvalue;
3842 for (;;) {
3843 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
3844 const char *w;
3845 bool ignore_enoent = false, shall_prefix = false;
3846
3847 r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
3848 if (r == 0)
3849 break;
3850 if (r == -ENOMEM)
3851 return log_oom();
3852 if (r < 0) {
3853 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
3854 return 0;
3855 }
3856
3857 if (!utf8_is_valid(word)) {
3858 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
3859 continue;
3860 }
3861
3862 w = word;
3863 if (startswith(w, "-")) {
3864 ignore_enoent = true;
3865 w++;
3866 }
3867 if (startswith(w, "+")) {
3868 shall_prefix = true;
3869 w++;
3870 }
3871
3872 r = unit_full_printf(u, w, &resolved);
3873 if (r < 0) {
3874 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word);
3875 continue;
3876 }
3877
3878 if (!path_is_absolute(resolved)) {
3879 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", resolved);
3880 continue;
3881 }
3882
3883 path_kill_slashes(resolved);
3884
3885 joined = strjoin(ignore_enoent ? "-" : "",
3886 shall_prefix ? "+" : "",
3887 resolved);
3888
3889 r = strv_push(sv, joined);
3890 if (r < 0)
3891 return log_oom();
3892
3893 joined = NULL;
3894 }
3895
3896 return 0;
3897 }
3898
3899 int config_parse_bind_paths(
3900 const char *unit,
3901 const char *filename,
3902 unsigned line,
3903 const char *section,
3904 unsigned section_line,
3905 const char *lvalue,
3906 int ltype,
3907 const char *rvalue,
3908 void *data,
3909 void *userdata) {
3910
3911 ExecContext *c = data;
3912 Unit *u = userdata;
3913 const char *p;
3914 int r;
3915
3916 assert(filename);
3917 assert(lvalue);
3918 assert(rvalue);
3919 assert(data);
3920
3921 if (isempty(rvalue)) {
3922 /* Empty assignment resets the list */
3923 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
3924 c->bind_mounts = NULL;
3925 c->n_bind_mounts = 0;
3926 return 0;
3927 }
3928
3929 p = rvalue;
3930 for (;;) {
3931 _cleanup_free_ char *source = NULL, *destination = NULL;
3932 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
3933 char *s = NULL, *d = NULL;
3934 bool rbind = true, ignore_enoent = false;
3935
3936 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
3937 if (r == 0)
3938 break;
3939 if (r == -ENOMEM)
3940 return log_oom();
3941 if (r < 0) {
3942 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
3943 return 0;
3944 }
3945
3946 r = unit_full_printf(u, source, &sresolved);
3947 if (r < 0) {
3948 log_syntax(unit, LOG_ERR, filename, line, r,
3949 "Failed to resolved specifiers in \"%s\", ignoring: %m", source);
3950 return 0;
3951 }
3952
3953 s = sresolved;
3954 if (s[0] == '-') {
3955 ignore_enoent = true;
3956 s++;
3957 }
3958
3959 if (!utf8_is_valid(s)) {
3960 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, s);
3961 return 0;
3962 }
3963 if (!path_is_absolute(s)) {
3964 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute source path, ignoring: %s", s);
3965 return 0;
3966 }
3967
3968 path_kill_slashes(s);
3969
3970 /* Optionally, the destination is specified. */
3971 if (p && p[-1] == ':') {
3972 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
3973 if (r == -ENOMEM)
3974 return log_oom();
3975 if (r < 0) {
3976 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
3977 return 0;
3978 }
3979 if (r == 0) {
3980 log_syntax(unit, LOG_ERR, filename, line, 0, "Missing argument after ':': %s", rvalue);
3981 return 0;
3982 }
3983
3984 r = unit_full_printf(u, destination, &dresolved);
3985 if (r < 0) {
3986 log_syntax(unit, LOG_ERR, filename, line, r,
3987 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination);
3988 return 0;
3989 }
3990
3991 if (!utf8_is_valid(dresolved)) {
3992 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, dresolved);
3993 return 0;
3994 }
3995 if (!path_is_absolute(dresolved)) {
3996 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute destination path, ignoring: %s", dresolved);
3997 return 0;
3998 }
3999
4000 d = path_kill_slashes(dresolved);
4001
4002 /* Optionally, there's also a short option string specified */
4003 if (p && p[-1] == ':') {
4004 _cleanup_free_ char *options = NULL;
4005
4006 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
4007 if (r == -ENOMEM)
4008 return log_oom();
4009 if (r < 0) {
4010 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
4011 return 0;
4012 }
4013
4014 if (isempty(options) || streq(options, "rbind"))
4015 rbind = true;
4016 else if (streq(options, "norbind"))
4017 rbind = false;
4018 else {
4019 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
4020 return 0;
4021 }
4022 }
4023 } else
4024 d = s;
4025
4026 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
4027 &(BindMount) {
4028 .source = s,
4029 .destination = d,
4030 .read_only = !!strstr(lvalue, "ReadOnly"),
4031 .recursive = rbind,
4032 .ignore_enoent = ignore_enoent,
4033 });
4034 if (r < 0)
4035 return log_oom();
4036 }
4037
4038 return 0;
4039 }
4040
4041 int config_parse_no_new_privileges(
4042 const char* unit,
4043 const char *filename,
4044 unsigned line,
4045 const char *section,
4046 unsigned section_line,
4047 const char *lvalue,
4048 int ltype,
4049 const char *rvalue,
4050 void *data,
4051 void *userdata) {
4052
4053 ExecContext *c = data;
4054 int k;
4055
4056 assert(filename);
4057 assert(lvalue);
4058 assert(rvalue);
4059 assert(data);
4060
4061 k = parse_boolean(rvalue);
4062 if (k < 0) {
4063 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
4064 return 0;
4065 }
4066
4067 c->no_new_privileges = k;
4068
4069 return 0;
4070 }
4071
4072 int config_parse_protect_home(
4073 const char* unit,
4074 const char *filename,
4075 unsigned line,
4076 const char *section,
4077 unsigned section_line,
4078 const char *lvalue,
4079 int ltype,
4080 const char *rvalue,
4081 void *data,
4082 void *userdata) {
4083
4084 ExecContext *c = data;
4085 int k;
4086
4087 assert(filename);
4088 assert(lvalue);
4089 assert(rvalue);
4090 assert(data);
4091
4092 /* Our enum shall be a superset of booleans, hence first try
4093 * to parse as boolean, and then as enum */
4094
4095 k = parse_boolean(rvalue);
4096 if (k > 0)
4097 c->protect_home = PROTECT_HOME_YES;
4098 else if (k == 0)
4099 c->protect_home = PROTECT_HOME_NO;
4100 else {
4101 ProtectHome h;
4102
4103 h = protect_home_from_string(rvalue);
4104 if (h < 0) {
4105 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
4106 return 0;
4107 }
4108
4109 c->protect_home = h;
4110 }
4111
4112 return 0;
4113 }
4114
4115 int config_parse_protect_system(
4116 const char* unit,
4117 const char *filename,
4118 unsigned line,
4119 const char *section,
4120 unsigned section_line,
4121 const char *lvalue,
4122 int ltype,
4123 const char *rvalue,
4124 void *data,
4125 void *userdata) {
4126
4127 ExecContext *c = data;
4128 int k;
4129
4130 assert(filename);
4131 assert(lvalue);
4132 assert(rvalue);
4133 assert(data);
4134
4135 /* Our enum shall be a superset of booleans, hence first try
4136 * to parse as boolean, and then as enum */
4137
4138 k = parse_boolean(rvalue);
4139 if (k > 0)
4140 c->protect_system = PROTECT_SYSTEM_YES;
4141 else if (k == 0)
4142 c->protect_system = PROTECT_SYSTEM_NO;
4143 else {
4144 ProtectSystem s;
4145
4146 s = protect_system_from_string(rvalue);
4147 if (s < 0) {
4148 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
4149 return 0;
4150 }
4151
4152 c->protect_system = s;
4153 }
4154
4155 return 0;
4156 }
4157
4158 #define FOLLOW_MAX 8
4159
4160 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
4161 char *id = NULL;
4162 unsigned c = 0;
4163 int fd, r;
4164 FILE *f;
4165
4166 assert(filename);
4167 assert(*filename);
4168 assert(_f);
4169 assert(names);
4170
4171 /* This will update the filename pointer if the loaded file is
4172 * reached by a symlink. The old string will be freed. */
4173
4174 for (;;) {
4175 char *target, *name;
4176
4177 if (c++ >= FOLLOW_MAX)
4178 return -ELOOP;
4179
4180 path_kill_slashes(*filename);
4181
4182 /* Add the file name we are currently looking at to
4183 * the names of this unit, but only if it is a valid
4184 * unit name. */
4185 name = basename(*filename);
4186 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
4187
4188 id = set_get(names, name);
4189 if (!id) {
4190 id = strdup(name);
4191 if (!id)
4192 return -ENOMEM;
4193
4194 r = set_consume(names, id);
4195 if (r < 0)
4196 return r;
4197 }
4198 }
4199
4200 /* Try to open the file name, but don't if its a symlink */
4201 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
4202 if (fd >= 0)
4203 break;
4204
4205 if (errno != ELOOP)
4206 return -errno;
4207
4208 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
4209 r = readlink_and_make_absolute(*filename, &target);
4210 if (r < 0)
4211 return r;
4212
4213 free(*filename);
4214 *filename = target;
4215 }
4216
4217 f = fdopen(fd, "re");
4218 if (!f) {
4219 safe_close(fd);
4220 return -errno;
4221 }
4222
4223 *_f = f;
4224 *_final = id;
4225
4226 return 0;
4227 }
4228
4229 static int merge_by_names(Unit **u, Set *names, const char *id) {
4230 char *k;
4231 int r;
4232
4233 assert(u);
4234 assert(*u);
4235 assert(names);
4236
4237 /* Let's try to add in all symlink names we found */
4238 while ((k = set_steal_first(names))) {
4239
4240 /* First try to merge in the other name into our
4241 * unit */
4242 r = unit_merge_by_name(*u, k);
4243 if (r < 0) {
4244 Unit *other;
4245
4246 /* Hmm, we couldn't merge the other unit into
4247 * ours? Then let's try it the other way
4248 * round */
4249
4250 /* If the symlink name we are looking at is unit template, then
4251 we must search for instance of this template */
4252 if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) {
4253 _cleanup_free_ char *instance = NULL;
4254
4255 r = unit_name_replace_instance(k, (*u)->instance, &instance);
4256 if (r < 0)
4257 return r;
4258
4259 other = manager_get_unit((*u)->manager, instance);
4260 } else
4261 other = manager_get_unit((*u)->manager, k);
4262
4263 free(k);
4264
4265 if (other) {
4266 r = unit_merge(other, *u);
4267 if (r >= 0) {
4268 *u = other;
4269 return merge_by_names(u, names, NULL);
4270 }
4271 }
4272
4273 return r;
4274 }
4275
4276 if (id == k)
4277 unit_choose_id(*u, id);
4278
4279 free(k);
4280 }
4281
4282 return 0;
4283 }
4284
4285 static int load_from_path(Unit *u, const char *path) {
4286 _cleanup_set_free_free_ Set *symlink_names = NULL;
4287 _cleanup_fclose_ FILE *f = NULL;
4288 _cleanup_free_ char *filename = NULL;
4289 char *id = NULL;
4290 Unit *merged;
4291 struct stat st;
4292 int r;
4293
4294 assert(u);
4295 assert(path);
4296
4297 symlink_names = set_new(&string_hash_ops);
4298 if (!symlink_names)
4299 return -ENOMEM;
4300
4301 if (path_is_absolute(path)) {
4302
4303 filename = strdup(path);
4304 if (!filename)
4305 return -ENOMEM;
4306
4307 r = open_follow(&filename, &f, symlink_names, &id);
4308 if (r < 0) {
4309 filename = mfree(filename);
4310 if (r != -ENOENT)
4311 return r;
4312 }
4313
4314 } else {
4315 char **p;
4316
4317 STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
4318
4319 /* Instead of opening the path right away, we manually
4320 * follow all symlinks and add their name to our unit
4321 * name set while doing so */
4322 filename = path_make_absolute(path, *p);
4323 if (!filename)
4324 return -ENOMEM;
4325
4326 if (u->manager->unit_path_cache &&
4327 !set_get(u->manager->unit_path_cache, filename))
4328 r = -ENOENT;
4329 else
4330 r = open_follow(&filename, &f, symlink_names, &id);
4331 if (r >= 0)
4332 break;
4333 filename = mfree(filename);
4334
4335 /* ENOENT means that the file is missing or is a dangling symlink.
4336 * ENOTDIR means that one of paths we expect to be is a directory
4337 * is not a directory, we should just ignore that.
4338 * EACCES means that the directory or file permissions are wrong.
4339 */
4340 if (r == -EACCES)
4341 log_debug_errno(r, "Cannot access \"%s\": %m", filename);
4342 else if (!IN_SET(r, -ENOENT, -ENOTDIR))
4343 return r;
4344
4345 /* Empty the symlink names for the next run */
4346 set_clear_free(symlink_names);
4347 }
4348 }
4349
4350 if (!filename)
4351 /* Hmm, no suitable file found? */
4352 return 0;
4353
4354 if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
4355 log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
4356 return -ELOOP;
4357 }
4358
4359 merged = u;
4360 r = merge_by_names(&merged, symlink_names, id);
4361 if (r < 0)
4362 return r;
4363
4364 if (merged != u) {
4365 u->load_state = UNIT_MERGED;
4366 return 0;
4367 }
4368
4369 if (fstat(fileno(f), &st) < 0)
4370 return -errno;
4371
4372 if (null_or_empty(&st)) {
4373 u->load_state = UNIT_MASKED;
4374 u->fragment_mtime = 0;
4375 } else {
4376 u->load_state = UNIT_LOADED;
4377 u->fragment_mtime = timespec_load(&st.st_mtim);
4378
4379 /* Now, parse the file contents */
4380 r = config_parse(u->id, filename, f,
4381 UNIT_VTABLE(u)->sections,
4382 config_item_perf_lookup, load_fragment_gperf_lookup,
4383 false, true, false, u);
4384 if (r < 0)
4385 return r;
4386 }
4387
4388 free(u->fragment_path);
4389 u->fragment_path = filename;
4390 filename = NULL;
4391
4392 if (u->source_path) {
4393 if (stat(u->source_path, &st) >= 0)
4394 u->source_mtime = timespec_load(&st.st_mtim);
4395 else
4396 u->source_mtime = 0;
4397 }
4398
4399 return 0;
4400 }
4401
4402 int unit_load_fragment(Unit *u) {
4403 int r;
4404 Iterator i;
4405 const char *t;
4406
4407 assert(u);
4408 assert(u->load_state == UNIT_STUB);
4409 assert(u->id);
4410
4411 if (u->transient) {
4412 u->load_state = UNIT_LOADED;
4413 return 0;
4414 }
4415
4416 /* First, try to find the unit under its id. We always look
4417 * for unit files in the default directories, to make it easy
4418 * to override things by placing things in /etc/systemd/system */
4419 r = load_from_path(u, u->id);
4420 if (r < 0)
4421 return r;
4422
4423 /* Try to find an alias we can load this with */
4424 if (u->load_state == UNIT_STUB) {
4425 SET_FOREACH(t, u->names, i) {
4426
4427 if (t == u->id)
4428 continue;
4429
4430 r = load_from_path(u, t);
4431 if (r < 0)
4432 return r;
4433
4434 if (u->load_state != UNIT_STUB)
4435 break;
4436 }
4437 }
4438
4439 /* And now, try looking for it under the suggested (originally linked) path */
4440 if (u->load_state == UNIT_STUB && u->fragment_path) {
4441
4442 r = load_from_path(u, u->fragment_path);
4443 if (r < 0)
4444 return r;
4445
4446 if (u->load_state == UNIT_STUB)
4447 /* Hmm, this didn't work? Then let's get rid
4448 * of the fragment path stored for us, so that
4449 * we don't point to an invalid location. */
4450 u->fragment_path = mfree(u->fragment_path);
4451 }
4452
4453 /* Look for a template */
4454 if (u->load_state == UNIT_STUB && u->instance) {
4455 _cleanup_free_ char *k = NULL;
4456
4457 r = unit_name_template(u->id, &k);
4458 if (r < 0)
4459 return r;
4460
4461 r = load_from_path(u, k);
4462 if (r < 0)
4463 return r;
4464
4465 if (u->load_state == UNIT_STUB) {
4466 SET_FOREACH(t, u->names, i) {
4467 _cleanup_free_ char *z = NULL;
4468
4469 if (t == u->id)
4470 continue;
4471
4472 r = unit_name_template(t, &z);
4473 if (r < 0)
4474 return r;
4475
4476 r = load_from_path(u, z);
4477 if (r < 0)
4478 return r;
4479
4480 if (u->load_state != UNIT_STUB)
4481 break;
4482 }
4483 }
4484 }
4485
4486 return 0;
4487 }
4488
4489 void unit_dump_config_items(FILE *f) {
4490 static const struct {
4491 const ConfigParserCallback callback;
4492 const char *rvalue;
4493 } table[] = {
4494 #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
4495 { config_parse_warn_compat, "NOTSUPPORTED" },
4496 #endif
4497 { config_parse_int, "INTEGER" },
4498 { config_parse_unsigned, "UNSIGNED" },
4499 { config_parse_iec_size, "SIZE" },
4500 { config_parse_iec_uint64, "SIZE" },
4501 { config_parse_si_size, "SIZE" },
4502 { config_parse_bool, "BOOLEAN" },
4503 { config_parse_string, "STRING" },
4504 { config_parse_path, "PATH" },
4505 { config_parse_unit_path_printf, "PATH" },
4506 { config_parse_strv, "STRING [...]" },
4507 { config_parse_exec_nice, "NICE" },
4508 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
4509 { config_parse_exec_io_class, "IOCLASS" },
4510 { config_parse_exec_io_priority, "IOPRIORITY" },
4511 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
4512 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
4513 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
4514 { config_parse_mode, "MODE" },
4515 { config_parse_unit_env_file, "FILE" },
4516 { config_parse_exec_output, "OUTPUT" },
4517 { config_parse_exec_input, "INPUT" },
4518 { config_parse_log_facility, "FACILITY" },
4519 { config_parse_log_level, "LEVEL" },
4520 { config_parse_exec_secure_bits, "SECUREBITS" },
4521 { config_parse_capability_set, "BOUNDINGSET" },
4522 { config_parse_limit, "LIMIT" },
4523 { config_parse_unit_deps, "UNIT [...]" },
4524 { config_parse_exec, "PATH [ARGUMENT [...]]" },
4525 { config_parse_service_type, "SERVICETYPE" },
4526 { config_parse_service_restart, "SERVICERESTART" },
4527 #ifdef HAVE_SYSV_COMPAT
4528 { config_parse_sysv_priority, "SYSVPRIORITY" },
4529 #endif
4530 { config_parse_kill_mode, "KILLMODE" },
4531 { config_parse_signal, "SIGNAL" },
4532 { config_parse_socket_listen, "SOCKET [...]" },
4533 { config_parse_socket_bind, "SOCKETBIND" },
4534 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
4535 { config_parse_sec, "SECONDS" },
4536 { config_parse_nsec, "NANOSECONDS" },
4537 { config_parse_namespace_path_strv, "PATH [...]" },
4538 { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
4539 { config_parse_unit_requires_mounts_for, "PATH [...]" },
4540 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
4541 { config_parse_unit_string_printf, "STRING" },
4542 { config_parse_trigger_unit, "UNIT" },
4543 { config_parse_timer, "TIMER" },
4544 { config_parse_path_spec, "PATH" },
4545 { config_parse_notify_access, "ACCESS" },
4546 { config_parse_ip_tos, "TOS" },
4547 { config_parse_unit_condition_path, "CONDITION" },
4548 { config_parse_unit_condition_string, "CONDITION" },
4549 { config_parse_unit_condition_null, "CONDITION" },
4550 { config_parse_unit_slice, "SLICE" },
4551 { config_parse_documentation, "URL" },
4552 { config_parse_service_timeout, "SECONDS" },
4553 { config_parse_emergency_action, "ACTION" },
4554 { config_parse_set_status, "STATUS" },
4555 { config_parse_service_sockets, "SOCKETS" },
4556 { config_parse_environ, "ENVIRON" },
4557 #ifdef HAVE_SECCOMP
4558 { config_parse_syscall_filter, "SYSCALLS" },
4559 { config_parse_syscall_archs, "ARCHS" },
4560 { config_parse_syscall_errno, "ERRNO" },
4561 { config_parse_address_families, "FAMILIES" },
4562 { config_parse_restrict_namespaces, "NAMESPACES" },
4563 #endif
4564 { config_parse_cpu_shares, "SHARES" },
4565 { config_parse_cpu_weight, "WEIGHT" },
4566 { config_parse_memory_limit, "LIMIT" },
4567 { config_parse_device_allow, "DEVICE" },
4568 { config_parse_device_policy, "POLICY" },
4569 { config_parse_io_limit, "LIMIT" },
4570 { config_parse_io_weight, "WEIGHT" },
4571 { config_parse_io_device_weight, "DEVICEWEIGHT" },
4572 { config_parse_blockio_bandwidth, "BANDWIDTH" },
4573 { config_parse_blockio_weight, "WEIGHT" },
4574 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
4575 { config_parse_long, "LONG" },
4576 { config_parse_socket_service, "SERVICE" },
4577 #ifdef HAVE_SELINUX
4578 { config_parse_exec_selinux_context, "LABEL" },
4579 #endif
4580 { config_parse_job_mode, "MODE" },
4581 { config_parse_job_mode_isolate, "BOOLEAN" },
4582 { config_parse_personality, "PERSONALITY" },
4583 };
4584
4585 const char *prev = NULL;
4586 const char *i;
4587
4588 assert(f);
4589
4590 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
4591 const char *rvalue = "OTHER", *lvalue;
4592 unsigned j;
4593 size_t prefix_len;
4594 const char *dot;
4595 const ConfigPerfItem *p;
4596
4597 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
4598
4599 dot = strchr(i, '.');
4600 lvalue = dot ? dot + 1 : i;
4601 prefix_len = dot-i;
4602
4603 if (dot)
4604 if (!prev || !strneq(prev, i, prefix_len+1)) {
4605 if (prev)
4606 fputc('\n', f);
4607
4608 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
4609 }
4610
4611 for (j = 0; j < ELEMENTSOF(table); j++)
4612 if (p->parse == table[j].callback) {
4613 rvalue = table[j].rvalue;
4614 break;
4615 }
4616
4617 fprintf(f, "%s=%s\n", lvalue, rvalue);
4618 prev = i;
4619 }
4620 }