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