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