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