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