]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/load-fragment.c
core/cgroup: CPUWeight/CPUShares support idle input
[thirdparty/systemd.git] / src / core / load-fragment.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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
16 #include "sd-messages.h"
17
18 #include "af-list.h"
19 #include "all-units.h"
20 #include "alloc-util.h"
21 #include "bpf-firewall.h"
22 #include "bpf-lsm.h"
23 #include "bpf-program.h"
24 #include "bpf-socket-bind.h"
25 #include "bus-error.h"
26 #include "bus-internal.h"
27 #include "bus-util.h"
28 #include "cap-list.h"
29 #include "capability-util.h"
30 #include "cgroup-setup.h"
31 #include "conf-parser.h"
32 #include "core-varlink.h"
33 #include "cpu-set-util.h"
34 #include "creds-util.h"
35 #include "env-util.h"
36 #include "errno-list.h"
37 #include "escape.h"
38 #include "fd-util.h"
39 #include "fileio.h"
40 #include "fs-util.h"
41 #include "hexdecoct.h"
42 #include "io-util.h"
43 #include "ioprio-util.h"
44 #include "ip-protocol-list.h"
45 #include "journal-file.h"
46 #include "limits-util.h"
47 #include "load-fragment.h"
48 #include "log.h"
49 #include "missing_ioprio.h"
50 #include "mountpoint-util.h"
51 #include "nulstr-util.h"
52 #include "parse-helpers.h"
53 #include "parse-util.h"
54 #include "path-util.h"
55 #include "percent-util.h"
56 #include "process-util.h"
57 #if HAVE_SECCOMP
58 #include "seccomp-util.h"
59 #endif
60 #include "securebits-util.h"
61 #include "selinux-util.h"
62 #include "signal-util.h"
63 #include "socket-netlink.h"
64 #include "specifier.h"
65 #include "stat-util.h"
66 #include "string-util.h"
67 #include "strv.h"
68 #include "syslog-util.h"
69 #include "time-util.h"
70 #include "unit-name.h"
71 #include "unit-printf.h"
72 #include "user-util.h"
73 #include "utf8.h"
74 #include "web-util.h"
75
76 static int parse_socket_protocol(const char *s) {
77 int r;
78
79 r = parse_ip_protocol(s);
80 if (r < 0)
81 return r;
82 if (!IN_SET(r, IPPROTO_UDPLITE, IPPROTO_SCTP))
83 return -EPROTONOSUPPORT;
84
85 return r;
86 }
87
88 int parse_crash_chvt(const char *value, int *data) {
89 int b;
90
91 if (safe_atoi(value, data) >= 0)
92 return 0;
93
94 b = parse_boolean(value);
95 if (b < 0)
96 return b;
97
98 if (b > 0)
99 *data = 0; /* switch to where kmsg goes */
100 else
101 *data = -1; /* turn off switching */
102
103 return 0;
104 }
105
106 int parse_confirm_spawn(const char *value, char **console) {
107 char *s;
108 int r;
109
110 r = value ? parse_boolean(value) : 1;
111 if (r == 0) {
112 *console = NULL;
113 return 0;
114 } else if (r > 0) /* on with default tty */
115 s = strdup("/dev/console");
116 else if (is_path(value)) /* on with fully qualified path */
117 s = strdup(value);
118 else /* on with only a tty file name, not a fully qualified path */
119 s = path_join("/dev/", value);
120 if (!s)
121 return -ENOMEM;
122
123 *console = s;
124 return 0;
125 }
126
127 DEFINE_CONFIG_PARSE(config_parse_socket_protocol, parse_socket_protocol, "Failed to parse socket protocol");
128 DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits");
129 DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode");
130 DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
131 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
132 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_proc, protect_proc, ProtectProc, "Failed to parse /proc/ protection mode");
133 DEFINE_CONFIG_PARSE_ENUM(config_parse_proc_subset, proc_subset, ProcSubset, "Failed to parse /proc/ subset mode");
134 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
135 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
136 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
137 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "Failed to parse protect home value");
138 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value");
139 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
140 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
141 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_exit_type, service_exit_type, ServiceExitType, "Failed to parse service exit type");
142 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
143 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode");
144 DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value");
145 DEFINE_CONFIG_PARSE_ENUM(config_parse_oom_policy, oom_policy, OOMPolicy, "Failed to parse OOM policy");
146 DEFINE_CONFIG_PARSE_ENUM(config_parse_managed_oom_preference, managed_oom_preference, ManagedOOMPreference, "Failed to parse ManagedOOMPreference=");
147 DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1, "Failed to parse IP TOS value");
148 DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight, cg_blkio_weight_parse, uint64_t, "Invalid block IO weight");
149 DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Invalid weight");
150 DEFINE_CONFIG_PARSE_PTR(config_parse_cg_cpu_weight, cg_cpu_weight_parse, uint64_t, "Invalid CPU weight");
151 DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
152 DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
153 DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
154 DEFINE_CONFIG_PARSE_ENUM(config_parse_status_unit_format, status_unit_format, StatusUnitFormat, "Failed to parse status unit format");
155 DEFINE_CONFIG_PARSE_ENUM_FULL(config_parse_socket_timestamping, socket_timestamping_from_string_harder, SocketTimestamping, "Failed to parse timestamping precision");
156
157 bool contains_instance_specifier_superset(const char *s) {
158 const char *p, *q;
159 bool percent = false;
160
161 assert(s);
162
163 p = strchr(s, '@');
164 if (!p)
165 return false;
166
167 p++; /* Skip '@' */
168
169 q = strrchr(p, '.');
170 if (!q)
171 q = p + strlen(p);
172
173 /* If the string is just the instance specifier, it's not a superset of the instance. */
174 if (memcmp_nn(p, q - p, "%i", strlen("%i")) == 0)
175 return false;
176
177 /* %i, %n and %N all expand to the instance or a superset of it. */
178 for (; p < q; p++) {
179 if (*p == '%')
180 percent = !percent;
181 else if (percent) {
182 if (IN_SET(*p, 'n', 'N', 'i'))
183 return true;
184 percent = false;
185 }
186 }
187
188 return false;
189 }
190
191 /* `name` is the rendered version of `format` via `unit_printf` or similar functions. */
192 int unit_is_likely_recursive_template_dependency(Unit *u, const char *name, const char *format) {
193 const char *fragment_path;
194 int r;
195
196 assert(u);
197 assert(name);
198
199 /* If a template unit has a direct dependency on itself that includes the unit instance as part of
200 * the template instance via a unit specifier (%i, %n or %N), this will almost certainly lead to
201 * infinite recursion as systemd will keep instantiating new instances of the template unit.
202 * https://github.com/systemd/systemd/issues/17602 shows a good example of how this can happen in
203 * practice. To guard against this, we check for templates that depend on themselves and have the
204 * instantiated unit instance included as part of the template instance of the dependency via a
205 * specifier.
206 *
207 * For example, if systemd-notify@.service depends on systemd-notify@%n.service, this will result in
208 * infinite recursion.
209 */
210
211 if (!unit_name_is_valid(name, UNIT_NAME_INSTANCE))
212 return false;
213
214 if (!unit_name_prefix_equal(u->id, name))
215 return false;
216
217 if (u->type != unit_name_to_type(name))
218 return false;
219
220 r = unit_file_find_fragment(u->manager->unit_id_map, u->manager->unit_name_map, name, &fragment_path, NULL);
221 if (r < 0)
222 return r;
223
224 /* Fragment paths should also be equal as a custom fragment for a specific template instance
225 * wouldn't necessarily lead to infinite recursion. */
226 if (!path_equal_ptr(u->fragment_path, fragment_path))
227 return false;
228
229 if (!contains_instance_specifier_superset(format))
230 return false;
231
232 return true;
233 }
234
235 int config_parse_unit_deps(
236 const char *unit,
237 const char *filename,
238 unsigned line,
239 const char *section,
240 unsigned section_line,
241 const char *lvalue,
242 int ltype,
243 const char *rvalue,
244 void *data,
245 void *userdata) {
246
247 UnitDependency d = ltype;
248 Unit *u = userdata;
249
250 assert(filename);
251 assert(lvalue);
252 assert(rvalue);
253
254 for (const char *p = rvalue;;) {
255 _cleanup_free_ char *word = NULL, *k = NULL;
256 int r;
257
258 r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
259 if (r == 0)
260 return 0;
261 if (r == -ENOMEM)
262 return log_oom();
263 if (r < 0) {
264 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
265 return 0;
266 }
267
268 r = unit_name_printf(u, word, &k);
269 if (r < 0) {
270 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
271 continue;
272 }
273
274 r = unit_is_likely_recursive_template_dependency(u, k, word);
275 if (r < 0) {
276 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to determine if '%s' is a recursive dependency, ignoring: %m", k);
277 continue;
278 }
279 if (r > 0) {
280 log_syntax(unit, LOG_DEBUG, filename, line, 0,
281 "Dropping dependency %s=%s that likely leads to infinite recursion.",
282 unit_dependency_to_string(d), word);
283 continue;
284 }
285
286 r = unit_add_dependency_by_name(u, d, k, true, UNIT_DEPENDENCY_FILE);
287 if (r < 0)
288 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
289 }
290 }
291
292 int config_parse_obsolete_unit_deps(
293 const char *unit,
294 const char *filename,
295 unsigned line,
296 const char *section,
297 unsigned section_line,
298 const char *lvalue,
299 int ltype,
300 const char *rvalue,
301 void *data,
302 void *userdata) {
303
304 log_syntax(unit, LOG_WARNING, filename, line, 0,
305 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue, unit_dependency_to_string(ltype));
306
307 return config_parse_unit_deps(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
308 }
309
310 int config_parse_unit_string_printf(
311 const char *unit,
312 const char *filename,
313 unsigned line,
314 const char *section,
315 unsigned section_line,
316 const char *lvalue,
317 int ltype,
318 const char *rvalue,
319 void *data,
320 void *userdata) {
321
322 _cleanup_free_ char *k = NULL;
323 const Unit *u = userdata;
324 int r;
325
326 assert(filename);
327 assert(lvalue);
328 assert(rvalue);
329 assert(u);
330
331 r = unit_full_printf(u, rvalue, &k);
332 if (r < 0) {
333 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
334 return 0;
335 }
336
337 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
338 }
339
340 int config_parse_unit_strv_printf(
341 const char *unit,
342 const char *filename,
343 unsigned line,
344 const char *section,
345 unsigned section_line,
346 const char *lvalue,
347 int ltype,
348 const char *rvalue,
349 void *data,
350 void *userdata) {
351
352 const Unit *u = userdata;
353 _cleanup_free_ char *k = NULL;
354 int r;
355
356 assert(filename);
357 assert(lvalue);
358 assert(rvalue);
359 assert(u);
360
361 r = unit_full_printf(u, rvalue, &k);
362 if (r < 0) {
363 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
364 return 0;
365 }
366
367 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
368 }
369
370 int config_parse_unit_path_printf(
371 const char *unit,
372 const char *filename,
373 unsigned line,
374 const char *section,
375 unsigned section_line,
376 const char *lvalue,
377 int ltype,
378 const char *rvalue,
379 void *data,
380 void *userdata) {
381
382 _cleanup_free_ char *k = NULL;
383 const Unit *u = userdata;
384 int r;
385 bool fatal = ltype;
386
387 assert(filename);
388 assert(lvalue);
389 assert(rvalue);
390 assert(u);
391
392 r = unit_path_printf(u, rvalue, &k);
393 if (r < 0) {
394 log_syntax(unit, fatal ? LOG_ERR : LOG_WARNING, filename, line, r,
395 "Failed to resolve unit specifiers in '%s'%s: %m",
396 rvalue, fatal ? "" : ", ignoring");
397 return fatal ? -ENOEXEC : 0;
398 }
399
400 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
401 }
402
403 int config_parse_colon_separated_paths(
404 const char *unit,
405 const char *filename,
406 unsigned line,
407 const char *section,
408 unsigned section_line,
409 const char *lvalue,
410 int ltype,
411 const char *rvalue,
412 void *data,
413 void *userdata) {
414 char ***sv = data;
415 const Unit *u = userdata;
416 int r;
417
418 assert(filename);
419 assert(lvalue);
420 assert(rvalue);
421 assert(data);
422
423 if (isempty(rvalue)) {
424 /* Empty assignment resets the list */
425 *sv = strv_free(*sv);
426 return 0;
427 }
428
429 for (const char *p = rvalue;;) {
430 _cleanup_free_ char *word = NULL, *k = NULL;
431
432 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
433 if (r == -ENOMEM)
434 return log_oom();
435 if (r < 0) {
436 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
437 return 0;
438 }
439 if (r == 0)
440 break;
441
442 r = unit_path_printf(u, word, &k);
443 if (r < 0) {
444 log_syntax(unit, LOG_WARNING, filename, line, r,
445 "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
446 return 0;
447 }
448
449 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
450 if (r < 0)
451 return 0;
452
453 r = strv_consume(sv, TAKE_PTR(k));
454 if (r < 0)
455 return log_oom();
456 }
457
458 return 0;
459 }
460
461 int config_parse_unit_path_strv_printf(
462 const char *unit,
463 const char *filename,
464 unsigned line,
465 const char *section,
466 unsigned section_line,
467 const char *lvalue,
468 int ltype,
469 const char *rvalue,
470 void *data,
471 void *userdata) {
472
473 char ***x = data;
474 const Unit *u = userdata;
475 int r;
476
477 assert(filename);
478 assert(lvalue);
479 assert(rvalue);
480 assert(u);
481
482 if (isempty(rvalue)) {
483 *x = strv_free(*x);
484 return 0;
485 }
486
487 for (const char *p = rvalue;;) {
488 _cleanup_free_ char *word = NULL, *k = NULL;
489
490 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
491 if (r == 0)
492 return 0;
493 if (r == -ENOMEM)
494 return log_oom();
495 if (r < 0) {
496 log_syntax(unit, LOG_WARNING, filename, line, r,
497 "Invalid syntax, ignoring: %s", rvalue);
498 return 0;
499 }
500
501 r = unit_path_printf(u, word, &k);
502 if (r < 0) {
503 log_syntax(unit, LOG_WARNING, filename, line, r,
504 "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
505 return 0;
506 }
507
508 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
509 if (r < 0)
510 return 0;
511
512 r = strv_consume(x, TAKE_PTR(k));
513 if (r < 0)
514 return log_oom();
515 }
516 }
517
518 static int patch_var_run(
519 const char *unit,
520 const char *filename,
521 unsigned line,
522 const char *lvalue,
523 char **path) {
524
525 const char *e;
526 char *z;
527
528 e = path_startswith(*path, "/var/run/");
529 if (!e)
530 return 0;
531
532 z = path_join("/run/", e);
533 if (!z)
534 return log_oom();
535
536 log_syntax(unit, LOG_NOTICE, filename, line, 0,
537 "%s= references a path below legacy directory /var/run/, updating %s → %s; "
538 "please update the unit file accordingly.", lvalue, *path, z);
539
540 free_and_replace(*path, z);
541
542 return 1;
543 }
544
545 int config_parse_socket_listen(
546 const char *unit,
547 const char *filename,
548 unsigned line,
549 const char *section,
550 unsigned section_line,
551 const char *lvalue,
552 int ltype,
553 const char *rvalue,
554 void *data,
555 void *userdata) {
556
557 _cleanup_free_ SocketPort *p = NULL;
558 SocketPort *tail;
559 Socket *s;
560 int r;
561
562 assert(filename);
563 assert(lvalue);
564 assert(rvalue);
565 assert(data);
566
567 s = SOCKET(data);
568
569 if (isempty(rvalue)) {
570 /* An empty assignment removes all ports */
571 socket_free_ports(s);
572 return 0;
573 }
574
575 p = new0(SocketPort, 1);
576 if (!p)
577 return log_oom();
578
579 if (ltype != SOCKET_SOCKET) {
580 _cleanup_free_ char *k = NULL;
581
582 r = unit_path_printf(UNIT(s), rvalue, &k);
583 if (r < 0) {
584 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
585 return 0;
586 }
587
588 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
589 if (r < 0)
590 return 0;
591
592 if (ltype == SOCKET_FIFO) {
593 r = patch_var_run(unit, filename, line, lvalue, &k);
594 if (r < 0)
595 return r;
596 }
597
598 free_and_replace(p->path, k);
599 p->type = ltype;
600
601 } else if (streq(lvalue, "ListenNetlink")) {
602 _cleanup_free_ char *k = NULL;
603
604 r = unit_path_printf(UNIT(s), rvalue, &k);
605 if (r < 0) {
606 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
607 return 0;
608 }
609
610 r = socket_address_parse_netlink(&p->address, k);
611 if (r < 0) {
612 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse address value in '%s', ignoring: %m", k);
613 return 0;
614 }
615
616 p->type = SOCKET_SOCKET;
617
618 } else {
619 _cleanup_free_ char *k = NULL;
620
621 r = unit_path_printf(UNIT(s), rvalue, &k);
622 if (r < 0) {
623 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
624 return 0;
625 }
626
627 if (k[0] == '/') { /* Only for AF_UNIX file system sockets… */
628 r = patch_var_run(unit, filename, line, lvalue, &k);
629 if (r < 0)
630 return r;
631 }
632
633 r = socket_address_parse_and_warn(&p->address, k);
634 if (r < 0) {
635 if (r != -EAFNOSUPPORT)
636 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse address value in '%s', ignoring: %m", k);
637 return 0;
638 }
639
640 if (streq(lvalue, "ListenStream"))
641 p->address.type = SOCK_STREAM;
642 else if (streq(lvalue, "ListenDatagram"))
643 p->address.type = SOCK_DGRAM;
644 else {
645 assert(streq(lvalue, "ListenSequentialPacket"));
646 p->address.type = SOCK_SEQPACKET;
647 }
648
649 if (socket_address_family(&p->address) != AF_UNIX && p->address.type == SOCK_SEQPACKET) {
650 log_syntax(unit, LOG_WARNING, filename, line, 0, "Address family not supported, ignoring: %s", rvalue);
651 return 0;
652 }
653
654 p->type = SOCKET_SOCKET;
655 }
656
657 p->fd = -1;
658 p->auxiliary_fds = NULL;
659 p->n_auxiliary_fds = 0;
660 p->socket = s;
661
662 LIST_FIND_TAIL(port, s->ports, tail);
663 LIST_INSERT_AFTER(port, s->ports, tail, p);
664
665 p = NULL;
666
667 return 0;
668 }
669
670 int config_parse_exec_nice(
671 const char *unit,
672 const char *filename,
673 unsigned line,
674 const char *section,
675 unsigned section_line,
676 const char *lvalue,
677 int ltype,
678 const char *rvalue,
679 void *data,
680 void *userdata) {
681
682 ExecContext *c = data;
683 int priority, r;
684
685 assert(filename);
686 assert(lvalue);
687 assert(rvalue);
688 assert(data);
689
690 if (isempty(rvalue)) {
691 c->nice_set = false;
692 return 0;
693 }
694
695 r = parse_nice(rvalue, &priority);
696 if (r < 0) {
697 if (r == -ERANGE)
698 log_syntax(unit, LOG_WARNING, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue);
699 else
700 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse nice priority '%s', ignoring: %m", rvalue);
701 return 0;
702 }
703
704 c->nice = priority;
705 c->nice_set = true;
706
707 return 0;
708 }
709
710 int config_parse_exec_oom_score_adjust(
711 const char* unit,
712 const char *filename,
713 unsigned line,
714 const char *section,
715 unsigned section_line,
716 const char *lvalue,
717 int ltype,
718 const char *rvalue,
719 void *data,
720 void *userdata) {
721
722 ExecContext *c = data;
723 int oa, r;
724
725 assert(filename);
726 assert(lvalue);
727 assert(rvalue);
728 assert(data);
729
730 if (isempty(rvalue)) {
731 c->oom_score_adjust_set = false;
732 return 0;
733 }
734
735 r = parse_oom_score_adjust(rvalue, &oa);
736 if (r < 0) {
737 if (r == -ERANGE)
738 log_syntax(unit, LOG_WARNING, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue);
739 else
740 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse the OOM score adjust value '%s', ignoring: %m", rvalue);
741 return 0;
742 }
743
744 c->oom_score_adjust = oa;
745 c->oom_score_adjust_set = true;
746
747 return 0;
748 }
749
750 int config_parse_exec_coredump_filter(
751 const char* unit,
752 const char *filename,
753 unsigned line,
754 const char *section,
755 unsigned section_line,
756 const char *lvalue,
757 int ltype,
758 const char *rvalue,
759 void *data,
760 void *userdata) {
761
762 ExecContext *c = data;
763 int r;
764
765 assert(filename);
766 assert(lvalue);
767 assert(rvalue);
768 assert(data);
769
770 if (isempty(rvalue)) {
771 c->coredump_filter = 0;
772 c->coredump_filter_set = false;
773 return 0;
774 }
775
776 uint64_t f;
777 r = coredump_filter_mask_from_string(rvalue, &f);
778 if (r < 0) {
779 log_syntax(unit, LOG_WARNING, filename, line, r,
780 "Failed to parse the CoredumpFilter=%s, ignoring: %m", rvalue);
781 return 0;
782 }
783
784 c->coredump_filter |= f;
785 c->oom_score_adjust_set = true;
786 return 0;
787 }
788
789 int config_parse_kill_mode(
790 const char* unit,
791 const char *filename,
792 unsigned line,
793 const char *section,
794 unsigned section_line,
795 const char *lvalue,
796 int ltype,
797 const char *rvalue,
798 void *data,
799 void *userdata) {
800
801 KillMode *k = data, m;
802
803 assert(filename);
804 assert(lvalue);
805 assert(rvalue);
806 assert(data);
807
808 if (isempty(rvalue)) {
809 *k = KILL_CONTROL_GROUP;
810 return 0;
811 }
812
813 m = kill_mode_from_string(rvalue);
814 if (m < 0) {
815 log_syntax(unit, LOG_WARNING, filename, line, m,
816 "Failed to parse kill mode specification, ignoring: %s", rvalue);
817 return 0;
818 }
819
820 if (m == KILL_NONE)
821 log_syntax(unit, LOG_WARNING, filename, line, 0,
822 "Unit configured to use KillMode=none. "
823 "This is unsafe, as it disables systemd's process lifecycle management for the service. "
824 "Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. "
825 "Support for KillMode=none is deprecated and will eventually be removed.");
826
827 *k = m;
828 return 0;
829 }
830
831 int config_parse_exec(
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 ExecCommand **e = data;
844 const Unit *u = userdata;
845 const char *p;
846 bool semicolon;
847 int r;
848
849 assert(filename);
850 assert(lvalue);
851 assert(rvalue);
852 assert(e);
853
854 e += ltype;
855
856 if (isempty(rvalue)) {
857 /* An empty assignment resets the list */
858 *e = exec_command_free_list(*e);
859 return 0;
860 }
861
862 p = rvalue;
863 do {
864 _cleanup_free_ char *path = NULL, *firstword = NULL;
865 ExecCommandFlags flags = 0;
866 bool ignore = false, separate_argv0 = false;
867 _cleanup_free_ ExecCommand *nce = NULL;
868 _cleanup_strv_free_ char **n = NULL;
869 size_t nlen = 0;
870 const char *f;
871
872 semicolon = false;
873
874 r = extract_first_word_and_warn(&p, &firstword, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
875 if (r <= 0)
876 return 0;
877
878 /* A lone ";" is a separator. Let's make sure we don't treat it as an executable name. */
879 if (streq(firstword, ";")) {
880 semicolon = true;
881 continue;
882 }
883
884 f = firstword;
885 for (;;) {
886 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
887 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
888 * argv[0]; if it's prefixed with :, we will not do environment variable substitution;
889 * if it's prefixed with +, it will be run with full privileges and no sandboxing; if
890 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
891 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
892 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
893 * other sandboxing, with some special exceptions for changing UID.
894 *
895 * The idea is that '!!' may be used to write services that can take benefit of systemd's
896 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
897 * privilege dropping within the daemon if the kernel does not offer that. */
898
899 if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE)) {
900 flags |= EXEC_COMMAND_IGNORE_FAILURE;
901 ignore = true;
902 } else if (*f == '@' && !separate_argv0)
903 separate_argv0 = true;
904 else if (*f == ':' && !(flags & EXEC_COMMAND_NO_ENV_EXPAND))
905 flags |= EXEC_COMMAND_NO_ENV_EXPAND;
906 else if (*f == '+' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC)))
907 flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
908 else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC)))
909 flags |= EXEC_COMMAND_NO_SETUID;
910 else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))) {
911 flags &= ~EXEC_COMMAND_NO_SETUID;
912 flags |= EXEC_COMMAND_AMBIENT_MAGIC;
913 } else
914 break;
915 f++;
916 }
917
918 r = unit_path_printf(u, f, &path);
919 if (r < 0) {
920 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
921 "Failed to resolve unit specifiers in '%s'%s: %m",
922 f, ignore ? ", ignoring" : "");
923 return ignore ? 0 : -ENOEXEC;
924 }
925
926 if (isempty(path)) {
927 /* First word is either "-" or "@" with no command. */
928 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
929 "Empty path in command line%s: '%s'",
930 ignore ? ", ignoring" : "", rvalue);
931 return ignore ? 0 : -ENOEXEC;
932 }
933 if (!string_is_safe(path)) {
934 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
935 "Executable name contains special characters%s: %s",
936 ignore ? ", ignoring" : "", path);
937 return ignore ? 0 : -ENOEXEC;
938 }
939 if (endswith(path, "/")) {
940 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
941 "Executable path specifies a directory%s: %s",
942 ignore ? ", ignoring" : "", path);
943 return ignore ? 0 : -ENOEXEC;
944 }
945
946 if (!(path_is_absolute(path) ? path_is_valid(path) : filename_is_valid(path))) {
947 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
948 "Neither a valid executable name nor an absolute path%s: %s",
949 ignore ? ", ignoring" : "", path);
950 return ignore ? 0 : -ENOEXEC;
951 }
952
953 if (!separate_argv0) {
954 char *w = NULL;
955
956 if (!GREEDY_REALLOC0(n, nlen + 2))
957 return log_oom();
958
959 w = strdup(path);
960 if (!w)
961 return log_oom();
962 n[nlen++] = w;
963 n[nlen] = NULL;
964 }
965
966 path_simplify(path);
967
968 while (!isempty(p)) {
969 _cleanup_free_ char *word = NULL, *resolved = NULL;
970
971 /* Check explicitly for an unquoted semicolon as
972 * command separator token. */
973 if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
974 p++;
975 p += strspn(p, WHITESPACE);
976 semicolon = true;
977 break;
978 }
979
980 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
981 * extract_first_word() would return the same for all of those. */
982 if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
983 char *w;
984
985 p += 2;
986 p += strspn(p, WHITESPACE);
987
988 if (!GREEDY_REALLOC0(n, nlen + 2))
989 return log_oom();
990
991 w = strdup(";");
992 if (!w)
993 return log_oom();
994 n[nlen++] = w;
995 n[nlen] = NULL;
996 continue;
997 }
998
999 r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
1000 if (r == 0)
1001 break;
1002 if (r < 0)
1003 return ignore ? 0 : -ENOEXEC;
1004
1005 r = unit_full_printf(u, word, &resolved);
1006 if (r < 0) {
1007 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
1008 "Failed to resolve unit specifiers in %s%s: %m",
1009 word, ignore ? ", ignoring" : "");
1010 return ignore ? 0 : -ENOEXEC;
1011 }
1012
1013 if (!GREEDY_REALLOC(n, nlen + 2))
1014 return log_oom();
1015
1016 n[nlen++] = TAKE_PTR(resolved);
1017 n[nlen] = NULL;
1018 }
1019
1020 if (!n || !n[0]) {
1021 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
1022 "Empty executable name or zeroeth argument%s: %s",
1023 ignore ? ", ignoring" : "", rvalue);
1024 return ignore ? 0 : -ENOEXEC;
1025 }
1026
1027 nce = new0(ExecCommand, 1);
1028 if (!nce)
1029 return log_oom();
1030
1031 nce->argv = TAKE_PTR(n);
1032 nce->path = TAKE_PTR(path);
1033 nce->flags = flags;
1034
1035 exec_command_append_list(e, nce);
1036
1037 /* Do not _cleanup_free_ these. */
1038 nce = NULL;
1039
1040 rvalue = p;
1041 } while (semicolon);
1042
1043 return 0;
1044 }
1045
1046 int config_parse_socket_bindtodevice(
1047 const char* unit,
1048 const char *filename,
1049 unsigned line,
1050 const char *section,
1051 unsigned section_line,
1052 const char *lvalue,
1053 int ltype,
1054 const char *rvalue,
1055 void *data,
1056 void *userdata) {
1057
1058 Socket *s = data;
1059
1060 assert(filename);
1061 assert(lvalue);
1062 assert(rvalue);
1063 assert(data);
1064
1065 if (isempty(rvalue) || streq(rvalue, "*")) {
1066 s->bind_to_device = mfree(s->bind_to_device);
1067 return 0;
1068 }
1069
1070 if (!ifname_valid(rvalue)) {
1071 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", rvalue);
1072 return 0;
1073 }
1074
1075 return free_and_strdup_warn(&s->bind_to_device, rvalue);
1076 }
1077
1078 int config_parse_exec_input(
1079 const char *unit,
1080 const char *filename,
1081 unsigned line,
1082 const char *section,
1083 unsigned section_line,
1084 const char *lvalue,
1085 int ltype,
1086 const char *rvalue,
1087 void *data,
1088 void *userdata) {
1089
1090 ExecContext *c = data;
1091 const Unit *u = userdata;
1092 const char *n;
1093 ExecInput ei;
1094 int r;
1095
1096 assert(data);
1097 assert(filename);
1098 assert(line);
1099 assert(rvalue);
1100
1101 n = startswith(rvalue, "fd:");
1102 if (n) {
1103 _cleanup_free_ char *resolved = NULL;
1104
1105 r = unit_fd_printf(u, n, &resolved);
1106 if (r < 0) {
1107 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", n);
1108 return 0;
1109 }
1110
1111 if (isempty(resolved))
1112 resolved = mfree(resolved);
1113 else if (!fdname_is_valid(resolved)) {
1114 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid file descriptor name, ignoring: %s", resolved);
1115 return 0;
1116 }
1117
1118 free_and_replace(c->stdio_fdname[STDIN_FILENO], resolved);
1119
1120 ei = EXEC_INPUT_NAMED_FD;
1121
1122 } else if ((n = startswith(rvalue, "file:"))) {
1123 _cleanup_free_ char *resolved = NULL;
1124
1125 r = unit_path_printf(u, n, &resolved);
1126 if (r < 0) {
1127 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", n);
1128 return 0;
1129 }
1130
1131 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1132 if (r < 0)
1133 return 0;
1134
1135 free_and_replace(c->stdio_file[STDIN_FILENO], resolved);
1136
1137 ei = EXEC_INPUT_FILE;
1138
1139 } else {
1140 ei = exec_input_from_string(rvalue);
1141 if (ei < 0) {
1142 log_syntax(unit, LOG_WARNING, filename, line, ei, "Failed to parse input specifier, ignoring: %s", rvalue);
1143 return 0;
1144 }
1145 }
1146
1147 c->std_input = ei;
1148 return 0;
1149 }
1150
1151 int config_parse_exec_input_text(
1152 const char *unit,
1153 const char *filename,
1154 unsigned line,
1155 const char *section,
1156 unsigned section_line,
1157 const char *lvalue,
1158 int ltype,
1159 const char *rvalue,
1160 void *data,
1161 void *userdata) {
1162
1163 _cleanup_free_ char *unescaped = NULL, *resolved = NULL;
1164 ExecContext *c = data;
1165 const Unit *u = userdata;
1166 int r;
1167
1168 assert(data);
1169 assert(filename);
1170 assert(line);
1171 assert(rvalue);
1172
1173 if (isempty(rvalue)) {
1174 /* Reset if the empty string is assigned */
1175 c->stdin_data = mfree(c->stdin_data);
1176 c->stdin_data_size = 0;
1177 return 0;
1178 }
1179
1180 ssize_t l = cunescape(rvalue, 0, &unescaped);
1181 if (l < 0) {
1182 log_syntax(unit, LOG_WARNING, filename, line, l,
1183 "Failed to decode C escaped text '%s', ignoring: %m", rvalue);
1184 return 0;
1185 }
1186
1187 r = unit_full_printf_full(u, unescaped, EXEC_STDIN_DATA_MAX, &resolved);
1188 if (r < 0) {
1189 log_syntax(unit, LOG_WARNING, filename, line, r,
1190 "Failed to resolve unit specifiers in '%s', ignoring: %m", unescaped);
1191 return 0;
1192 }
1193
1194 size_t sz = strlen(resolved);
1195 if (c->stdin_data_size + sz + 1 < c->stdin_data_size || /* check for overflow */
1196 c->stdin_data_size + sz + 1 > EXEC_STDIN_DATA_MAX) {
1197 log_syntax(unit, LOG_WARNING, filename, line, 0,
1198 "Standard input data too large (%zu), maximum of %zu permitted, ignoring.",
1199 c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
1200 return 0;
1201 }
1202
1203 void *p = realloc(c->stdin_data, c->stdin_data_size + sz + 1);
1204 if (!p)
1205 return log_oom();
1206
1207 *((char*) mempcpy((char*) p + c->stdin_data_size, resolved, sz)) = '\n';
1208
1209 c->stdin_data = p;
1210 c->stdin_data_size += sz + 1;
1211
1212 return 0;
1213 }
1214
1215 int config_parse_exec_input_data(
1216 const char *unit,
1217 const char *filename,
1218 unsigned line,
1219 const char *section,
1220 unsigned section_line,
1221 const char *lvalue,
1222 int ltype,
1223 const char *rvalue,
1224 void *data,
1225 void *userdata) {
1226
1227 _cleanup_free_ void *p = NULL;
1228 ExecContext *c = data;
1229 size_t sz;
1230 void *q;
1231 int r;
1232
1233 assert(data);
1234 assert(filename);
1235 assert(line);
1236 assert(rvalue);
1237
1238 if (isempty(rvalue)) {
1239 /* Reset if the empty string is assigned */
1240 c->stdin_data = mfree(c->stdin_data);
1241 c->stdin_data_size = 0;
1242 return 0;
1243 }
1244
1245 r = unbase64mem(rvalue, SIZE_MAX, &p, &sz);
1246 if (r < 0) {
1247 log_syntax(unit, LOG_WARNING, filename, line, r,
1248 "Failed to decode base64 data, ignoring: %s", rvalue);
1249 return 0;
1250 }
1251
1252 assert(sz > 0);
1253
1254 if (c->stdin_data_size + sz < c->stdin_data_size || /* check for overflow */
1255 c->stdin_data_size + sz > EXEC_STDIN_DATA_MAX) {
1256 log_syntax(unit, LOG_WARNING, filename, line, 0,
1257 "Standard input data too large (%zu), maximum of %zu permitted, ignoring.",
1258 c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
1259 return 0;
1260 }
1261
1262 q = realloc(c->stdin_data, c->stdin_data_size + sz);
1263 if (!q)
1264 return log_oom();
1265
1266 memcpy((uint8_t*) q + c->stdin_data_size, p, sz);
1267
1268 c->stdin_data = q;
1269 c->stdin_data_size += sz;
1270
1271 return 0;
1272 }
1273
1274 int config_parse_exec_output(
1275 const char *unit,
1276 const char *filename,
1277 unsigned line,
1278 const char *section,
1279 unsigned section_line,
1280 const char *lvalue,
1281 int ltype,
1282 const char *rvalue,
1283 void *data,
1284 void *userdata) {
1285
1286 _cleanup_free_ char *resolved = NULL;
1287 const char *n;
1288 ExecContext *c = data;
1289 const Unit *u = userdata;
1290 bool obsolete = false;
1291 ExecOutput eo;
1292 int r;
1293
1294 assert(data);
1295 assert(filename);
1296 assert(line);
1297 assert(lvalue);
1298 assert(rvalue);
1299
1300 n = startswith(rvalue, "fd:");
1301 if (n) {
1302 r = unit_fd_printf(u, n, &resolved);
1303 if (r < 0) {
1304 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n);
1305 return 0;
1306 }
1307
1308 if (isempty(resolved))
1309 resolved = mfree(resolved);
1310 else if (!fdname_is_valid(resolved)) {
1311 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid file descriptor name, ignoring: %s", resolved);
1312 return 0;
1313 }
1314
1315 eo = EXEC_OUTPUT_NAMED_FD;
1316
1317 } else if (streq(rvalue, "syslog")) {
1318 eo = EXEC_OUTPUT_JOURNAL;
1319 obsolete = true;
1320
1321 } else if (streq(rvalue, "syslog+console")) {
1322 eo = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
1323 obsolete = true;
1324
1325 } else if ((n = startswith(rvalue, "file:"))) {
1326
1327 r = unit_path_printf(u, n, &resolved);
1328 if (r < 0) {
1329 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", n);
1330 return 0;
1331 }
1332
1333 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1334 if (r < 0)
1335 return 0;
1336
1337 eo = EXEC_OUTPUT_FILE;
1338
1339 } else if ((n = startswith(rvalue, "append:"))) {
1340
1341 r = unit_path_printf(u, n, &resolved);
1342 if (r < 0) {
1343 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", n);
1344 return 0;
1345 }
1346
1347 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1348 if (r < 0)
1349 return 0;
1350
1351 eo = EXEC_OUTPUT_FILE_APPEND;
1352
1353 } else if ((n = startswith(rvalue, "truncate:"))) {
1354
1355 r = unit_path_printf(u, n, &resolved);
1356 if (r < 0) {
1357 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", n);
1358 return 0;
1359 }
1360
1361 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1362 if (r < 0)
1363 return 0;
1364
1365 eo = EXEC_OUTPUT_FILE_TRUNCATE;
1366 } else {
1367 eo = exec_output_from_string(rvalue);
1368 if (eo < 0) {
1369 log_syntax(unit, LOG_WARNING, filename, line, eo, "Failed to parse output specifier, ignoring: %s", rvalue);
1370 return 0;
1371 }
1372 }
1373
1374 if (obsolete)
1375 log_syntax(unit, LOG_NOTICE, filename, line, 0,
1376 "Standard output type %s is obsolete, automatically updating to %s. Please update your unit file, and consider removing the setting altogether.",
1377 rvalue, exec_output_to_string(eo));
1378
1379 if (streq(lvalue, "StandardOutput")) {
1380 if (eo == EXEC_OUTPUT_NAMED_FD)
1381 free_and_replace(c->stdio_fdname[STDOUT_FILENO], resolved);
1382 else
1383 free_and_replace(c->stdio_file[STDOUT_FILENO], resolved);
1384
1385 c->std_output = eo;
1386
1387 } else {
1388 assert(streq(lvalue, "StandardError"));
1389
1390 if (eo == EXEC_OUTPUT_NAMED_FD)
1391 free_and_replace(c->stdio_fdname[STDERR_FILENO], resolved);
1392 else
1393 free_and_replace(c->stdio_file[STDERR_FILENO], resolved);
1394
1395 c->std_error = eo;
1396 }
1397
1398 return 0;
1399 }
1400
1401 int config_parse_exec_io_class(const char *unit,
1402 const char *filename,
1403 unsigned line,
1404 const char *section,
1405 unsigned section_line,
1406 const char *lvalue,
1407 int ltype,
1408 const char *rvalue,
1409 void *data,
1410 void *userdata) {
1411
1412 ExecContext *c = data;
1413 int x;
1414
1415 assert(filename);
1416 assert(lvalue);
1417 assert(rvalue);
1418 assert(data);
1419
1420 if (isempty(rvalue)) {
1421 c->ioprio_set = false;
1422 c->ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO;
1423 return 0;
1424 }
1425
1426 x = ioprio_class_from_string(rvalue);
1427 if (x < 0) {
1428 log_syntax(unit, LOG_WARNING, filename, line, x, "Failed to parse IO scheduling class, ignoring: %s", rvalue);
1429 return 0;
1430 }
1431
1432 c->ioprio = ioprio_normalize(ioprio_prio_value(x, ioprio_prio_data(c->ioprio)));
1433 c->ioprio_set = true;
1434
1435 return 0;
1436 }
1437
1438 int config_parse_exec_io_priority(const char *unit,
1439 const char *filename,
1440 unsigned line,
1441 const char *section,
1442 unsigned section_line,
1443 const char *lvalue,
1444 int ltype,
1445 const char *rvalue,
1446 void *data,
1447 void *userdata) {
1448
1449 ExecContext *c = data;
1450 int i, r;
1451
1452 assert(filename);
1453 assert(lvalue);
1454 assert(rvalue);
1455 assert(data);
1456
1457 if (isempty(rvalue)) {
1458 c->ioprio_set = false;
1459 c->ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO;
1460 return 0;
1461 }
1462
1463 r = ioprio_parse_priority(rvalue, &i);
1464 if (r < 0) {
1465 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue);
1466 return 0;
1467 }
1468
1469 c->ioprio = ioprio_normalize(ioprio_prio_value(ioprio_prio_class(c->ioprio), i));
1470 c->ioprio_set = true;
1471
1472 return 0;
1473 }
1474
1475 int config_parse_exec_cpu_sched_policy(const char *unit,
1476 const char *filename,
1477 unsigned line,
1478 const char *section,
1479 unsigned section_line,
1480 const char *lvalue,
1481 int ltype,
1482 const char *rvalue,
1483 void *data,
1484 void *userdata) {
1485
1486 ExecContext *c = data;
1487 int x;
1488
1489 assert(filename);
1490 assert(lvalue);
1491 assert(rvalue);
1492 assert(data);
1493
1494 if (isempty(rvalue)) {
1495 c->cpu_sched_set = false;
1496 c->cpu_sched_policy = SCHED_OTHER;
1497 c->cpu_sched_priority = 0;
1498 return 0;
1499 }
1500
1501 x = sched_policy_from_string(rvalue);
1502 if (x < 0) {
1503 log_syntax(unit, LOG_WARNING, filename, line, x, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
1504 return 0;
1505 }
1506
1507 c->cpu_sched_policy = x;
1508 /* Moving to or from real-time policy? We need to adjust the priority */
1509 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
1510 c->cpu_sched_set = true;
1511
1512 return 0;
1513 }
1514
1515 int config_parse_exec_mount_apivfs(const char *unit,
1516 const char *filename,
1517 unsigned line,
1518 const char *section,
1519 unsigned section_line,
1520 const char *lvalue,
1521 int ltype,
1522 const char *rvalue,
1523 void *data,
1524 void *userdata) {
1525
1526 ExecContext *c = data;
1527 int k;
1528
1529 assert(filename);
1530 assert(lvalue);
1531 assert(rvalue);
1532 assert(data);
1533
1534 if (isempty(rvalue)) {
1535 c->mount_apivfs_set = false;
1536 c->mount_apivfs = false;
1537 return 0;
1538 }
1539
1540 k = parse_boolean(rvalue);
1541 if (k < 0) {
1542 log_syntax(unit, LOG_WARNING, filename, line, k,
1543 "Failed to parse boolean value, ignoring: %s",
1544 rvalue);
1545 return 0;
1546 }
1547
1548 c->mount_apivfs_set = true;
1549 c->mount_apivfs = k;
1550 return 0;
1551 }
1552
1553 int config_parse_numa_mask(const char *unit,
1554 const char *filename,
1555 unsigned line,
1556 const char *section,
1557 unsigned section_line,
1558 const char *lvalue,
1559 int ltype,
1560 const char *rvalue,
1561 void *data,
1562 void *userdata) {
1563 int r;
1564 NUMAPolicy *p = data;
1565
1566 assert(filename);
1567 assert(lvalue);
1568 assert(rvalue);
1569 assert(data);
1570
1571 if (streq(rvalue, "all")) {
1572 r = numa_mask_add_all(&p->nodes);
1573 if (r < 0)
1574 log_syntax(unit, LOG_WARNING, filename, line, r,
1575 "Failed to create NUMA mask representing \"all\" NUMA nodes, ignoring: %m");
1576 } else {
1577 r = parse_cpu_set_extend(rvalue, &p->nodes, true, unit, filename, line, lvalue);
1578 if (r < 0)
1579 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse NUMA node mask, ignoring: %s", rvalue);
1580 }
1581
1582 return 0;
1583 }
1584
1585 int config_parse_exec_cpu_sched_prio(const char *unit,
1586 const char *filename,
1587 unsigned line,
1588 const char *section,
1589 unsigned section_line,
1590 const char *lvalue,
1591 int ltype,
1592 const char *rvalue,
1593 void *data,
1594 void *userdata) {
1595
1596 ExecContext *c = data;
1597 int i, min, max, r;
1598
1599 assert(filename);
1600 assert(lvalue);
1601 assert(rvalue);
1602 assert(data);
1603
1604 r = safe_atoi(rvalue, &i);
1605 if (r < 0) {
1606 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse CPU scheduling priority, ignoring: %s", rvalue);
1607 return 0;
1608 }
1609
1610 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1611 min = sched_get_priority_min(c->cpu_sched_policy);
1612 max = sched_get_priority_max(c->cpu_sched_policy);
1613
1614 if (i < min || i > max) {
1615 log_syntax(unit, LOG_WARNING, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue);
1616 return 0;
1617 }
1618
1619 c->cpu_sched_priority = i;
1620 c->cpu_sched_set = true;
1621
1622 return 0;
1623 }
1624
1625 int config_parse_root_image_options(
1626 const char *unit,
1627 const char *filename,
1628 unsigned line,
1629 const char *section,
1630 unsigned section_line,
1631 const char *lvalue,
1632 int ltype,
1633 const char *rvalue,
1634 void *data,
1635 void *userdata) {
1636
1637 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
1638 _cleanup_strv_free_ char **l = NULL;
1639 ExecContext *c = data;
1640 const Unit *u = userdata;
1641 int r;
1642
1643 assert(filename);
1644 assert(lvalue);
1645 assert(rvalue);
1646 assert(data);
1647
1648 if (isempty(rvalue)) {
1649 c->root_image_options = mount_options_free_all(c->root_image_options);
1650 return 0;
1651 }
1652
1653 r = strv_split_colon_pairs(&l, rvalue);
1654 if (r == -ENOMEM)
1655 return log_oom();
1656 if (r < 0) {
1657 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
1658 return 0;
1659 }
1660
1661 STRV_FOREACH_PAIR(first, second, l) {
1662 MountOptions *o = NULL;
1663 _cleanup_free_ char *mount_options_resolved = NULL;
1664 const char *mount_options = NULL, *partition = "root";
1665 PartitionDesignator partition_designator;
1666
1667 /* Format is either 'root:foo' or 'foo' (root is implied) */
1668 if (!isempty(*second)) {
1669 partition = *first;
1670 mount_options = *second;
1671 } else
1672 mount_options = *first;
1673
1674 partition_designator = partition_designator_from_string(partition);
1675 if (partition_designator < 0) {
1676 log_syntax(unit, LOG_WARNING, filename, line, partition_designator,
1677 "Invalid partition name %s, ignoring", partition);
1678 continue;
1679 }
1680 r = unit_full_printf(u, mount_options, &mount_options_resolved);
1681 if (r < 0) {
1682 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
1683 continue;
1684 }
1685
1686 o = new(MountOptions, 1);
1687 if (!o)
1688 return log_oom();
1689 *o = (MountOptions) {
1690 .partition_designator = partition_designator,
1691 .options = TAKE_PTR(mount_options_resolved),
1692 };
1693 LIST_APPEND(mount_options, options, TAKE_PTR(o));
1694 }
1695
1696 if (options)
1697 LIST_JOIN(mount_options, c->root_image_options, options);
1698 else
1699 /* empty spaces/separators only */
1700 c->root_image_options = mount_options_free_all(c->root_image_options);
1701
1702 return 0;
1703 }
1704
1705 int config_parse_exec_root_hash(
1706 const char *unit,
1707 const char *filename,
1708 unsigned line,
1709 const char *section,
1710 unsigned section_line,
1711 const char *lvalue,
1712 int ltype,
1713 const char *rvalue,
1714 void *data,
1715 void *userdata) {
1716
1717 _cleanup_free_ void *roothash_decoded = NULL;
1718 ExecContext *c = data;
1719 size_t roothash_decoded_size = 0;
1720 int r;
1721
1722 assert(data);
1723 assert(filename);
1724 assert(line);
1725 assert(rvalue);
1726
1727 if (isempty(rvalue)) {
1728 /* Reset if the empty string is assigned */
1729 c->root_hash_path = mfree(c->root_hash_path);
1730 c->root_hash = mfree(c->root_hash);
1731 c->root_hash_size = 0;
1732 return 0;
1733 }
1734
1735 if (path_is_absolute(rvalue)) {
1736 /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1737 _cleanup_free_ char *p = NULL;
1738
1739 p = strdup(rvalue);
1740 if (!p)
1741 return -ENOMEM;
1742
1743 free_and_replace(c->root_hash_path, p);
1744 c->root_hash = mfree(c->root_hash);
1745 c->root_hash_size = 0;
1746 return 0;
1747 }
1748
1749 /* We have a roothash to decode, eg: RootHash=012345789abcdef */
1750 r = unhexmem(rvalue, strlen(rvalue), &roothash_decoded, &roothash_decoded_size);
1751 if (r < 0) {
1752 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode RootHash=, ignoring: %s", rvalue);
1753 return 0;
1754 }
1755 if (roothash_decoded_size < sizeof(sd_id128_t)) {
1756 log_syntax(unit, LOG_WARNING, filename, line, 0, "RootHash= is too short, ignoring: %s", rvalue);
1757 return 0;
1758 }
1759
1760 free_and_replace(c->root_hash, roothash_decoded);
1761 c->root_hash_size = roothash_decoded_size;
1762 c->root_hash_path = mfree(c->root_hash_path);
1763
1764 return 0;
1765 }
1766
1767 int config_parse_exec_root_hash_sig(
1768 const char *unit,
1769 const char *filename,
1770 unsigned line,
1771 const char *section,
1772 unsigned section_line,
1773 const char *lvalue,
1774 int ltype,
1775 const char *rvalue,
1776 void *data,
1777 void *userdata) {
1778
1779 _cleanup_free_ void *roothash_sig_decoded = NULL;
1780 char *value;
1781 ExecContext *c = data;
1782 size_t roothash_sig_decoded_size = 0;
1783 int r;
1784
1785 assert(data);
1786 assert(filename);
1787 assert(line);
1788 assert(rvalue);
1789
1790 if (isempty(rvalue)) {
1791 /* Reset if the empty string is assigned */
1792 c->root_hash_sig_path = mfree(c->root_hash_sig_path);
1793 c->root_hash_sig = mfree(c->root_hash_sig);
1794 c->root_hash_sig_size = 0;
1795 return 0;
1796 }
1797
1798 if (path_is_absolute(rvalue)) {
1799 /* We have the path to a roothash signature to load and decode, eg: RootHashSignature=/foo/bar.roothash.p7s */
1800 _cleanup_free_ char *p = NULL;
1801
1802 p = strdup(rvalue);
1803 if (!p)
1804 return log_oom();
1805
1806 free_and_replace(c->root_hash_sig_path, p);
1807 c->root_hash_sig = mfree(c->root_hash_sig);
1808 c->root_hash_sig_size = 0;
1809 return 0;
1810 }
1811
1812 if (!(value = startswith(rvalue, "base64:"))) {
1813 log_syntax(unit, LOG_WARNING, filename, line, 0,
1814 "Failed to decode RootHashSignature=, not a path but doesn't start with 'base64:', ignoring: %s", rvalue);
1815 return 0;
1816 }
1817
1818 /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
1819 r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
1820 if (r < 0) {
1821 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode RootHashSignature=, ignoring: %s", rvalue);
1822 return 0;
1823 }
1824
1825 free_and_replace(c->root_hash_sig, roothash_sig_decoded);
1826 c->root_hash_sig_size = roothash_sig_decoded_size;
1827 c->root_hash_sig_path = mfree(c->root_hash_sig_path);
1828
1829 return 0;
1830 }
1831
1832 int config_parse_exec_cpu_affinity(
1833 const char *unit,
1834 const char *filename,
1835 unsigned line,
1836 const char *section,
1837 unsigned section_line,
1838 const char *lvalue,
1839 int ltype,
1840 const char *rvalue,
1841 void *data,
1842 void *userdata) {
1843
1844 ExecContext *c = data;
1845 const Unit *u = userdata;
1846 _cleanup_free_ char *k = NULL;
1847 int r;
1848
1849 assert(filename);
1850 assert(lvalue);
1851 assert(rvalue);
1852 assert(data);
1853
1854 if (streq(rvalue, "numa")) {
1855 c->cpu_affinity_from_numa = true;
1856 cpu_set_reset(&c->cpu_set);
1857
1858 return 0;
1859 }
1860
1861 r = unit_full_printf(u, rvalue, &k);
1862 if (r < 0) {
1863 log_syntax(unit, LOG_WARNING, filename, line, r,
1864 "Failed to resolve unit specifiers in '%s', ignoring: %m",
1865 rvalue);
1866 return 0;
1867 }
1868
1869 r = parse_cpu_set_extend(k, &c->cpu_set, true, unit, filename, line, lvalue);
1870 if (r >= 0)
1871 c->cpu_affinity_from_numa = false;
1872
1873 return 0;
1874 }
1875
1876 int config_parse_capability_set(
1877 const char *unit,
1878 const char *filename,
1879 unsigned line,
1880 const char *section,
1881 unsigned section_line,
1882 const char *lvalue,
1883 int ltype,
1884 const char *rvalue,
1885 void *data,
1886 void *userdata) {
1887
1888 uint64_t *capability_set = data;
1889 uint64_t sum = 0, initial = 0;
1890 bool invert = false;
1891 int r;
1892
1893 assert(filename);
1894 assert(lvalue);
1895 assert(rvalue);
1896 assert(data);
1897
1898 if (rvalue[0] == '~') {
1899 invert = true;
1900 rvalue++;
1901 }
1902
1903 if (streq(lvalue, "CapabilityBoundingSet"))
1904 initial = CAP_ALL; /* initialized to all bits on */
1905 /* else "AmbientCapabilities" initialized to all bits off */
1906
1907 r = capability_set_from_string(rvalue, &sum);
1908 if (r < 0) {
1909 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= specifier '%s', ignoring: %m", lvalue, rvalue);
1910 return 0;
1911 }
1912
1913 if (sum == 0 || *capability_set == initial)
1914 /* "", "~" or uninitialized data -> replace */
1915 *capability_set = invert ? ~sum : sum;
1916 else {
1917 /* previous data -> merge */
1918 if (invert)
1919 *capability_set &= ~sum;
1920 else
1921 *capability_set |= sum;
1922 }
1923
1924 return 0;
1925 }
1926
1927 int config_parse_exec_selinux_context(
1928 const char *unit,
1929 const char *filename,
1930 unsigned line,
1931 const char *section,
1932 unsigned section_line,
1933 const char *lvalue,
1934 int ltype,
1935 const char *rvalue,
1936 void *data,
1937 void *userdata) {
1938
1939 ExecContext *c = data;
1940 const Unit *u = userdata;
1941 bool ignore;
1942 char *k;
1943 int r;
1944
1945 assert(filename);
1946 assert(lvalue);
1947 assert(rvalue);
1948 assert(data);
1949
1950 if (isempty(rvalue)) {
1951 c->selinux_context = mfree(c->selinux_context);
1952 c->selinux_context_ignore = false;
1953 return 0;
1954 }
1955
1956 if (rvalue[0] == '-') {
1957 ignore = true;
1958 rvalue++;
1959 } else
1960 ignore = false;
1961
1962 r = unit_full_printf(u, rvalue, &k);
1963 if (r < 0) {
1964 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
1965 "Failed to resolve unit specifiers in '%s'%s: %m",
1966 rvalue, ignore ? ", ignoring" : "");
1967 return ignore ? 0 : -ENOEXEC;
1968 }
1969
1970 free_and_replace(c->selinux_context, k);
1971 c->selinux_context_ignore = ignore;
1972
1973 return 0;
1974 }
1975
1976 int config_parse_exec_apparmor_profile(
1977 const char *unit,
1978 const char *filename,
1979 unsigned line,
1980 const char *section,
1981 unsigned section_line,
1982 const char *lvalue,
1983 int ltype,
1984 const char *rvalue,
1985 void *data,
1986 void *userdata) {
1987
1988 ExecContext *c = data;
1989 const Unit *u = userdata;
1990 bool ignore;
1991 char *k;
1992 int r;
1993
1994 assert(filename);
1995 assert(lvalue);
1996 assert(rvalue);
1997 assert(data);
1998
1999 if (isempty(rvalue)) {
2000 c->apparmor_profile = mfree(c->apparmor_profile);
2001 c->apparmor_profile_ignore = false;
2002 return 0;
2003 }
2004
2005 if (rvalue[0] == '-') {
2006 ignore = true;
2007 rvalue++;
2008 } else
2009 ignore = false;
2010
2011 r = unit_full_printf(u, rvalue, &k);
2012 if (r < 0) {
2013 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
2014 "Failed to resolve unit specifiers in '%s'%s: %m",
2015 rvalue, ignore ? ", ignoring" : "");
2016 return ignore ? 0 : -ENOEXEC;
2017 }
2018
2019 free_and_replace(c->apparmor_profile, k);
2020 c->apparmor_profile_ignore = ignore;
2021
2022 return 0;
2023 }
2024
2025 int config_parse_exec_smack_process_label(
2026 const char *unit,
2027 const char *filename,
2028 unsigned line,
2029 const char *section,
2030 unsigned section_line,
2031 const char *lvalue,
2032 int ltype,
2033 const char *rvalue,
2034 void *data,
2035 void *userdata) {
2036
2037 ExecContext *c = data;
2038 const Unit *u = userdata;
2039 bool ignore;
2040 char *k;
2041 int r;
2042
2043 assert(filename);
2044 assert(lvalue);
2045 assert(rvalue);
2046 assert(data);
2047
2048 if (isempty(rvalue)) {
2049 c->smack_process_label = mfree(c->smack_process_label);
2050 c->smack_process_label_ignore = false;
2051 return 0;
2052 }
2053
2054 if (rvalue[0] == '-') {
2055 ignore = true;
2056 rvalue++;
2057 } else
2058 ignore = false;
2059
2060 r = unit_full_printf(u, rvalue, &k);
2061 if (r < 0) {
2062 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
2063 "Failed to resolve unit specifiers in '%s'%s: %m",
2064 rvalue, ignore ? ", ignoring" : "");
2065 return ignore ? 0 : -ENOEXEC;
2066 }
2067
2068 free_and_replace(c->smack_process_label, k);
2069 c->smack_process_label_ignore = ignore;
2070
2071 return 0;
2072 }
2073
2074 int config_parse_timer(
2075 const char *unit,
2076 const char *filename,
2077 unsigned line,
2078 const char *section,
2079 unsigned section_line,
2080 const char *lvalue,
2081 int ltype,
2082 const char *rvalue,
2083 void *data,
2084 void *userdata) {
2085
2086 _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
2087 _cleanup_free_ char *k = NULL;
2088 const Unit *u = userdata;
2089 Timer *t = data;
2090 usec_t usec = 0;
2091 TimerValue *v;
2092 int r;
2093
2094 assert(filename);
2095 assert(lvalue);
2096 assert(rvalue);
2097 assert(data);
2098
2099 if (isempty(rvalue)) {
2100 /* Empty assignment resets list */
2101 timer_free_values(t);
2102 return 0;
2103 }
2104
2105 r = unit_full_printf(u, rvalue, &k);
2106 if (r < 0) {
2107 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2108 return 0;
2109 }
2110
2111 if (ltype == TIMER_CALENDAR) {
2112 r = calendar_spec_from_string(k, &c);
2113 if (r < 0) {
2114 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse calendar specification, ignoring: %s", k);
2115 return 0;
2116 }
2117 } else {
2118 r = parse_sec(k, &usec);
2119 if (r < 0) {
2120 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse timer value, ignoring: %s", k);
2121 return 0;
2122 }
2123 }
2124
2125 v = new(TimerValue, 1);
2126 if (!v)
2127 return log_oom();
2128
2129 *v = (TimerValue) {
2130 .base = ltype,
2131 .value = usec,
2132 .calendar_spec = TAKE_PTR(c),
2133 };
2134
2135 LIST_PREPEND(value, t->values, v);
2136
2137 return 0;
2138 }
2139
2140 int config_parse_trigger_unit(
2141 const char *unit,
2142 const char *filename,
2143 unsigned line,
2144 const char *section,
2145 unsigned section_line,
2146 const char *lvalue,
2147 int ltype,
2148 const char *rvalue,
2149 void *data,
2150 void *userdata) {
2151
2152 _cleanup_free_ char *p = NULL;
2153 Unit *u = data;
2154 UnitType type;
2155 int r;
2156
2157 assert(filename);
2158 assert(lvalue);
2159 assert(rvalue);
2160 assert(data);
2161
2162 if (UNIT_TRIGGER(u)) {
2163 log_syntax(unit, LOG_WARNING, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
2164 return 0;
2165 }
2166
2167 r = unit_name_printf(u, rvalue, &p);
2168 if (r < 0) {
2169 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2170 return 0;
2171 }
2172
2173 type = unit_name_to_type(p);
2174 if (type < 0) {
2175 log_syntax(unit, LOG_WARNING, filename, line, type, "Unit type not valid, ignoring: %s", rvalue);
2176 return 0;
2177 }
2178 if (unit_has_name(u, p)) {
2179 log_syntax(unit, LOG_WARNING, filename, line, 0, "Units cannot trigger themselves, ignoring: %s", rvalue);
2180 return 0;
2181 }
2182
2183 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, true, UNIT_DEPENDENCY_FILE);
2184 if (r < 0) {
2185 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
2186 return 0;
2187 }
2188
2189 return 0;
2190 }
2191
2192 int config_parse_path_spec(const char *unit,
2193 const char *filename,
2194 unsigned line,
2195 const char *section,
2196 unsigned section_line,
2197 const char *lvalue,
2198 int ltype,
2199 const char *rvalue,
2200 void *data,
2201 void *userdata) {
2202
2203 Path *p = data;
2204 PathSpec *s;
2205 PathType b;
2206 _cleanup_free_ char *k = NULL;
2207 int r;
2208
2209 assert(filename);
2210 assert(lvalue);
2211 assert(rvalue);
2212 assert(data);
2213
2214 if (isempty(rvalue)) {
2215 /* Empty assignment clears list */
2216 path_free_specs(p);
2217 return 0;
2218 }
2219
2220 b = path_type_from_string(lvalue);
2221 if (b < 0) {
2222 log_syntax(unit, LOG_WARNING, filename, line, b, "Failed to parse path type, ignoring: %s", lvalue);
2223 return 0;
2224 }
2225
2226 r = unit_path_printf(UNIT(p), rvalue, &k);
2227 if (r < 0) {
2228 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2229 return 0;
2230 }
2231
2232 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2233 if (r < 0)
2234 return 0;
2235
2236 s = new0(PathSpec, 1);
2237 if (!s)
2238 return log_oom();
2239
2240 s->unit = UNIT(p);
2241 s->path = TAKE_PTR(k);
2242 s->type = b;
2243 s->inotify_fd = -1;
2244
2245 LIST_PREPEND(spec, p->specs, s);
2246
2247 return 0;
2248 }
2249
2250 int config_parse_socket_service(
2251 const char *unit,
2252 const char *filename,
2253 unsigned line,
2254 const char *section,
2255 unsigned section_line,
2256 const char *lvalue,
2257 int ltype,
2258 const char *rvalue,
2259 void *data,
2260 void *userdata) {
2261
2262 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2263 _cleanup_free_ char *p = NULL;
2264 Socket *s = data;
2265 Unit *x;
2266 int r;
2267
2268 assert(filename);
2269 assert(lvalue);
2270 assert(rvalue);
2271 assert(data);
2272
2273 r = unit_name_printf(UNIT(s), rvalue, &p);
2274 if (r < 0) {
2275 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2276 return 0;
2277 }
2278
2279 if (!endswith(p, ".service")) {
2280 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
2281 return 0;
2282 }
2283
2284 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
2285 if (r < 0) {
2286 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
2287 return 0;
2288 }
2289
2290 unit_ref_set(&s->service, UNIT(s), x);
2291
2292 return 0;
2293 }
2294
2295 int config_parse_fdname(
2296 const char *unit,
2297 const char *filename,
2298 unsigned line,
2299 const char *section,
2300 unsigned section_line,
2301 const char *lvalue,
2302 int ltype,
2303 const char *rvalue,
2304 void *data,
2305 void *userdata) {
2306
2307 _cleanup_free_ char *p = NULL;
2308 Socket *s = data;
2309 int r;
2310
2311 assert(filename);
2312 assert(lvalue);
2313 assert(rvalue);
2314 assert(data);
2315
2316 if (isempty(rvalue)) {
2317 s->fdname = mfree(s->fdname);
2318 return 0;
2319 }
2320
2321 r = unit_fd_printf(UNIT(s), rvalue, &p);
2322 if (r < 0) {
2323 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2324 return 0;
2325 }
2326
2327 if (!fdname_is_valid(p)) {
2328 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
2329 return 0;
2330 }
2331
2332 return free_and_replace(s->fdname, p);
2333 }
2334
2335 int config_parse_service_sockets(
2336 const char *unit,
2337 const char *filename,
2338 unsigned line,
2339 const char *section,
2340 unsigned section_line,
2341 const char *lvalue,
2342 int ltype,
2343 const char *rvalue,
2344 void *data,
2345 void *userdata) {
2346
2347 Service *s = data;
2348 int r;
2349
2350 assert(filename);
2351 assert(lvalue);
2352 assert(rvalue);
2353 assert(data);
2354
2355 for (const char *p = rvalue;;) {
2356 _cleanup_free_ char *word = NULL, *k = NULL;
2357
2358 r = extract_first_word(&p, &word, NULL, 0);
2359 if (r == -ENOMEM)
2360 return log_oom();
2361 if (r < 0) {
2362 log_syntax(unit, LOG_WARNING, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
2363 return 0;
2364 }
2365 if (r == 0)
2366 return 0;
2367
2368 r = unit_name_printf(UNIT(s), word, &k);
2369 if (r < 0) {
2370 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
2371 continue;
2372 }
2373
2374 if (!endswith(k, ".socket")) {
2375 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
2376 continue;
2377 }
2378
2379 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, true, UNIT_DEPENDENCY_FILE);
2380 if (r < 0)
2381 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
2382
2383 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, true, UNIT_DEPENDENCY_FILE);
2384 if (r < 0)
2385 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
2386 }
2387 }
2388
2389 int config_parse_bus_name(
2390 const char *unit,
2391 const char *filename,
2392 unsigned line,
2393 const char *section,
2394 unsigned section_line,
2395 const char *lvalue,
2396 int ltype,
2397 const char *rvalue,
2398 void *data,
2399 void *userdata) {
2400
2401 _cleanup_free_ char *k = NULL;
2402 const Unit *u = userdata;
2403 int r;
2404
2405 assert(filename);
2406 assert(lvalue);
2407 assert(rvalue);
2408 assert(u);
2409
2410 r = unit_full_printf_full(u, rvalue, SD_BUS_MAXIMUM_NAME_LENGTH, &k);
2411 if (r < 0) {
2412 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2413 return 0;
2414 }
2415
2416 if (!sd_bus_service_name_is_valid(k)) {
2417 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid bus name, ignoring: %s", k);
2418 return 0;
2419 }
2420
2421 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
2422 }
2423
2424 int config_parse_service_timeout(
2425 const char *unit,
2426 const char *filename,
2427 unsigned line,
2428 const char *section,
2429 unsigned section_line,
2430 const char *lvalue,
2431 int ltype,
2432 const char *rvalue,
2433 void *data,
2434 void *userdata) {
2435
2436 Service *s = userdata;
2437 usec_t usec;
2438 int r;
2439
2440 assert(filename);
2441 assert(lvalue);
2442 assert(rvalue);
2443 assert(s);
2444
2445 /* This is called for two cases: TimeoutSec= and TimeoutStartSec=. */
2446
2447 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
2448 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
2449 * all other timeouts. */
2450 r = parse_sec_fix_0(rvalue, &usec);
2451 if (r < 0) {
2452 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
2453 return 0;
2454 }
2455
2456 s->start_timeout_defined = true;
2457 s->timeout_start_usec = usec;
2458
2459 if (streq(lvalue, "TimeoutSec"))
2460 s->timeout_stop_usec = usec;
2461
2462 return 0;
2463 }
2464
2465 int config_parse_timeout_abort(
2466 const char *unit,
2467 const char *filename,
2468 unsigned line,
2469 const char *section,
2470 unsigned section_line,
2471 const char *lvalue,
2472 int ltype,
2473 const char *rvalue,
2474 void *data,
2475 void *userdata) {
2476
2477 usec_t *ret = data;
2478 int r;
2479
2480 assert(filename);
2481 assert(lvalue);
2482 assert(rvalue);
2483 assert(ret);
2484
2485 /* Note: apart from setting the arg, this returns an extra bit of information in the return value. */
2486
2487 if (isempty(rvalue)) {
2488 *ret = 0;
2489 return 0; /* "not set" */
2490 }
2491
2492 r = parse_sec(rvalue, ret);
2493 if (r < 0)
2494 return log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= setting, ignoring: %s", lvalue, rvalue);
2495
2496 return 1; /* "set" */
2497 }
2498
2499 int config_parse_service_timeout_abort(
2500 const char *unit,
2501 const char *filename,
2502 unsigned line,
2503 const char *section,
2504 unsigned section_line,
2505 const char *lvalue,
2506 int ltype,
2507 const char *rvalue,
2508 void *data,
2509 void *userdata) {
2510
2511 Service *s = userdata;
2512 int r;
2513
2514 assert(s);
2515
2516 r = config_parse_timeout_abort(unit, filename, line, section, section_line, lvalue, ltype, rvalue,
2517 &s->timeout_abort_usec, s);
2518 if (r >= 0)
2519 s->timeout_abort_set = r;
2520 return 0;
2521 }
2522
2523 int config_parse_sec_fix_0(
2524 const char *unit,
2525 const char *filename,
2526 unsigned line,
2527 const char *section,
2528 unsigned section_line,
2529 const char *lvalue,
2530 int ltype,
2531 const char *rvalue,
2532 void *data,
2533 void *userdata) {
2534
2535 usec_t *usec = data;
2536 int r;
2537
2538 assert(filename);
2539 assert(lvalue);
2540 assert(rvalue);
2541 assert(usec);
2542
2543 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
2544 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
2545 * timeout. */
2546
2547 r = parse_sec_fix_0(rvalue, usec);
2548 if (r < 0)
2549 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
2550
2551 return 0;
2552 }
2553
2554 int config_parse_user_group_compat(
2555 const char *unit,
2556 const char *filename,
2557 unsigned line,
2558 const char *section,
2559 unsigned section_line,
2560 const char *lvalue,
2561 int ltype,
2562 const char *rvalue,
2563 void *data,
2564 void *userdata) {
2565
2566 _cleanup_free_ char *k = NULL;
2567 char **user = data;
2568 const Unit *u = userdata;
2569 int r;
2570
2571 assert(filename);
2572 assert(lvalue);
2573 assert(rvalue);
2574 assert(u);
2575
2576 if (isempty(rvalue)) {
2577 *user = mfree(*user);
2578 return 0;
2579 }
2580
2581 r = unit_full_printf(u, rvalue, &k);
2582 if (r < 0) {
2583 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue);
2584 return -ENOEXEC;
2585 }
2586
2587 if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) {
2588 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2589 return -ENOEXEC;
2590 }
2591
2592 if (strstr(lvalue, "User") && streq(k, NOBODY_USER_NAME))
2593 log_struct(LOG_NOTICE,
2594 "MESSAGE=%s:%u: Special user %s configured, this is not safe!", filename, line, k,
2595 "UNIT=%s", unit,
2596 "MESSAGE_ID=" SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR,
2597 "OFFENDING_USER=%s", k,
2598 "CONFIG_FILE=%s", filename,
2599 "CONFIG_LINE=%u", line);
2600
2601 return free_and_replace(*user, k);
2602 }
2603
2604 int config_parse_user_group_strv_compat(
2605 const char *unit,
2606 const char *filename,
2607 unsigned line,
2608 const char *section,
2609 unsigned section_line,
2610 const char *lvalue,
2611 int ltype,
2612 const char *rvalue,
2613 void *data,
2614 void *userdata) {
2615
2616 char ***users = data;
2617 const Unit *u = userdata;
2618 int r;
2619
2620 assert(filename);
2621 assert(lvalue);
2622 assert(rvalue);
2623 assert(u);
2624
2625 if (isempty(rvalue)) {
2626 *users = strv_free(*users);
2627 return 0;
2628 }
2629
2630 for (const char *p = rvalue;;) {
2631 _cleanup_free_ char *word = NULL, *k = NULL;
2632
2633 r = extract_first_word(&p, &word, NULL, 0);
2634 if (r == -ENOMEM)
2635 return log_oom();
2636 if (r < 0) {
2637 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue);
2638 return -ENOEXEC;
2639 }
2640 if (r == 0)
2641 return 0;
2642
2643 r = unit_full_printf(u, word, &k);
2644 if (r < 0) {
2645 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word);
2646 return -ENOEXEC;
2647 }
2648
2649 if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) {
2650 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2651 return -ENOEXEC;
2652 }
2653
2654 r = strv_push(users, k);
2655 if (r < 0)
2656 return log_oom();
2657
2658 k = NULL;
2659 }
2660 }
2661
2662 int config_parse_working_directory(
2663 const char *unit,
2664 const char *filename,
2665 unsigned line,
2666 const char *section,
2667 unsigned section_line,
2668 const char *lvalue,
2669 int ltype,
2670 const char *rvalue,
2671 void *data,
2672 void *userdata) {
2673
2674 ExecContext *c = data;
2675 const Unit *u = userdata;
2676 bool missing_ok;
2677 int r;
2678
2679 assert(filename);
2680 assert(lvalue);
2681 assert(rvalue);
2682 assert(c);
2683 assert(u);
2684
2685 if (isempty(rvalue)) {
2686 c->working_directory_home = false;
2687 c->working_directory = mfree(c->working_directory);
2688 return 0;
2689 }
2690
2691 if (rvalue[0] == '-') {
2692 missing_ok = true;
2693 rvalue++;
2694 } else
2695 missing_ok = false;
2696
2697 if (streq(rvalue, "~")) {
2698 c->working_directory_home = true;
2699 c->working_directory = mfree(c->working_directory);
2700 } else {
2701 _cleanup_free_ char *k = NULL;
2702
2703 r = unit_path_printf(u, rvalue, &k);
2704 if (r < 0) {
2705 log_syntax(unit, missing_ok ? LOG_WARNING : LOG_ERR, filename, line, r,
2706 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2707 rvalue, missing_ok ? ", ignoring" : "");
2708 return missing_ok ? 0 : -ENOEXEC;
2709 }
2710
2711 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE | (missing_ok ? 0 : PATH_CHECK_FATAL), unit, filename, line, lvalue);
2712 if (r < 0)
2713 return missing_ok ? 0 : -ENOEXEC;
2714
2715 c->working_directory_home = false;
2716 free_and_replace(c->working_directory, k);
2717 }
2718
2719 c->working_directory_missing_ok = missing_ok;
2720 return 0;
2721 }
2722
2723 int config_parse_unit_env_file(const char *unit,
2724 const char *filename,
2725 unsigned line,
2726 const char *section,
2727 unsigned section_line,
2728 const char *lvalue,
2729 int ltype,
2730 const char *rvalue,
2731 void *data,
2732 void *userdata) {
2733
2734 char ***env = data;
2735 const Unit *u = userdata;
2736 _cleanup_free_ char *n = NULL;
2737 int r;
2738
2739 assert(filename);
2740 assert(lvalue);
2741 assert(rvalue);
2742 assert(data);
2743
2744 if (isempty(rvalue)) {
2745 /* Empty assignment frees the list */
2746 *env = strv_free(*env);
2747 return 0;
2748 }
2749
2750 r = unit_full_printf_full(u, rvalue, PATH_MAX, &n);
2751 if (r < 0) {
2752 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2753 return 0;
2754 }
2755
2756 r = path_simplify_and_warn(n[0] == '-' ? n + 1 : n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2757 if (r < 0)
2758 return 0;
2759
2760 r = strv_push(env, n);
2761 if (r < 0)
2762 return log_oom();
2763
2764 n = NULL;
2765
2766 return 0;
2767 }
2768
2769 int config_parse_environ(
2770 const char *unit,
2771 const char *filename,
2772 unsigned line,
2773 const char *section,
2774 unsigned section_line,
2775 const char *lvalue,
2776 int ltype,
2777 const char *rvalue,
2778 void *data,
2779 void *userdata) {
2780
2781 const Unit *u = userdata;
2782 char ***env = data;
2783 int r;
2784
2785 assert(filename);
2786 assert(lvalue);
2787 assert(rvalue);
2788 assert(data);
2789
2790 if (isempty(rvalue)) {
2791 /* Empty assignment resets the list */
2792 *env = strv_free(*env);
2793 return 0;
2794 }
2795
2796 for (const char *p = rvalue;; ) {
2797 _cleanup_free_ char *word = NULL, *resolved = NULL;
2798
2799 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
2800 if (r == -ENOMEM)
2801 return log_oom();
2802 if (r < 0) {
2803 log_syntax(unit, LOG_WARNING, filename, line, r,
2804 "Invalid syntax, ignoring: %s", rvalue);
2805 return 0;
2806 }
2807 if (r == 0)
2808 return 0;
2809
2810 if (u)
2811 r = unit_env_printf(u, word, &resolved);
2812 else
2813 r = specifier_printf(word, sc_arg_max(), system_and_tmp_specifier_table, NULL, NULL, &resolved);
2814 if (r < 0) {
2815 log_syntax(unit, LOG_WARNING, filename, line, r,
2816 "Failed to resolve specifiers in %s, ignoring: %m", word);
2817 continue;
2818 }
2819
2820 if (!env_assignment_is_valid(resolved)) {
2821 log_syntax(unit, LOG_WARNING, filename, line, 0,
2822 "Invalid environment assignment, ignoring: %s", resolved);
2823 continue;
2824 }
2825
2826 r = strv_env_replace_consume(env, TAKE_PTR(resolved));
2827 if (r < 0)
2828 return log_error_errno(r, "Failed to update environment: %m");
2829 }
2830 }
2831
2832 int config_parse_pass_environ(
2833 const char *unit,
2834 const char *filename,
2835 unsigned line,
2836 const char *section,
2837 unsigned section_line,
2838 const char *lvalue,
2839 int ltype,
2840 const char *rvalue,
2841 void *data,
2842 void *userdata) {
2843
2844 _cleanup_strv_free_ char **n = NULL;
2845 const Unit *u = userdata;
2846 char*** passenv = data;
2847 size_t nlen = 0;
2848 int r;
2849
2850 assert(filename);
2851 assert(lvalue);
2852 assert(rvalue);
2853 assert(data);
2854
2855 if (isempty(rvalue)) {
2856 /* Empty assignment resets the list */
2857 *passenv = strv_free(*passenv);
2858 return 0;
2859 }
2860
2861 for (const char *p = rvalue;;) {
2862 _cleanup_free_ char *word = NULL, *k = NULL;
2863
2864 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
2865 if (r == -ENOMEM)
2866 return log_oom();
2867 if (r < 0) {
2868 log_syntax(unit, LOG_WARNING, filename, line, r,
2869 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
2870 break;
2871 }
2872 if (r == 0)
2873 break;
2874
2875 if (u) {
2876 r = unit_env_printf(u, word, &k);
2877 if (r < 0) {
2878 log_syntax(unit, LOG_WARNING, filename, line, r,
2879 "Failed to resolve specifiers in %s, ignoring: %m", word);
2880 continue;
2881 }
2882 } else
2883 k = TAKE_PTR(word);
2884
2885 if (!env_name_is_valid(k)) {
2886 log_syntax(unit, LOG_WARNING, filename, line, 0,
2887 "Invalid environment name for %s, ignoring: %s", lvalue, k);
2888 continue;
2889 }
2890
2891 if (!GREEDY_REALLOC(n, nlen + 2))
2892 return log_oom();
2893
2894 n[nlen++] = TAKE_PTR(k);
2895 n[nlen] = NULL;
2896 }
2897
2898 if (n) {
2899 r = strv_extend_strv(passenv, n, true);
2900 if (r < 0)
2901 return log_oom();
2902 }
2903
2904 return 0;
2905 }
2906
2907 int config_parse_unset_environ(
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 _cleanup_strv_free_ char **n = NULL;
2920 char*** unsetenv = data;
2921 const Unit *u = userdata;
2922 size_t nlen = 0;
2923 int r;
2924
2925 assert(filename);
2926 assert(lvalue);
2927 assert(rvalue);
2928 assert(data);
2929
2930 if (isempty(rvalue)) {
2931 /* Empty assignment resets the list */
2932 *unsetenv = strv_free(*unsetenv);
2933 return 0;
2934 }
2935
2936 for (const char *p = rvalue;;) {
2937 _cleanup_free_ char *word = NULL, *k = NULL;
2938
2939 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
2940 if (r == -ENOMEM)
2941 return log_oom();
2942 if (r < 0) {
2943 log_syntax(unit, LOG_WARNING, filename, line, r,
2944 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
2945 break;
2946 }
2947 if (r == 0)
2948 break;
2949
2950 if (u) {
2951 r = unit_env_printf(u, word, &k);
2952 if (r < 0) {
2953 log_syntax(unit, LOG_WARNING, filename, line, r,
2954 "Failed to resolve unit specifiers in %s, ignoring: %m", word);
2955 continue;
2956 }
2957 } else
2958 k = TAKE_PTR(word);
2959
2960 if (!env_assignment_is_valid(k) && !env_name_is_valid(k)) {
2961 log_syntax(unit, LOG_WARNING, filename, line, 0,
2962 "Invalid environment name or assignment %s, ignoring: %s", lvalue, k);
2963 continue;
2964 }
2965
2966 if (!GREEDY_REALLOC(n, nlen + 2))
2967 return log_oom();
2968
2969 n[nlen++] = TAKE_PTR(k);
2970 n[nlen] = NULL;
2971 }
2972
2973 if (n) {
2974 r = strv_extend_strv(unsetenv, n, true);
2975 if (r < 0)
2976 return log_oom();
2977 }
2978
2979 return 0;
2980 }
2981
2982 int config_parse_log_extra_fields(
2983 const char *unit,
2984 const char *filename,
2985 unsigned line,
2986 const char *section,
2987 unsigned section_line,
2988 const char *lvalue,
2989 int ltype,
2990 const char *rvalue,
2991 void *data,
2992 void *userdata) {
2993
2994 ExecContext *c = data;
2995 const Unit *u = userdata;
2996 int r;
2997
2998 assert(filename);
2999 assert(lvalue);
3000 assert(rvalue);
3001 assert(c);
3002
3003 if (isempty(rvalue)) {
3004 exec_context_free_log_extra_fields(c);
3005 return 0;
3006 }
3007
3008 for (const char *p = rvalue;;) {
3009 _cleanup_free_ char *word = NULL, *k = NULL;
3010 struct iovec *t;
3011 const char *eq;
3012
3013 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
3014 if (r == -ENOMEM)
3015 return log_oom();
3016 if (r < 0) {
3017 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
3018 return 0;
3019 }
3020 if (r == 0)
3021 return 0;
3022
3023 r = unit_full_printf(u, word, &k);
3024 if (r < 0) {
3025 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word);
3026 continue;
3027 }
3028
3029 eq = strchr(k, '=');
3030 if (!eq) {
3031 log_syntax(unit, LOG_WARNING, filename, line, 0, "Log field lacks '=' character, ignoring: %s", k);
3032 continue;
3033 }
3034
3035 if (!journal_field_valid(k, eq-k, false)) {
3036 log_syntax(unit, LOG_WARNING, filename, line, 0, "Log field name is invalid, ignoring: %s", k);
3037 continue;
3038 }
3039
3040 t = reallocarray(c->log_extra_fields, c->n_log_extra_fields+1, sizeof(struct iovec));
3041 if (!t)
3042 return log_oom();
3043
3044 c->log_extra_fields = t;
3045 c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE_STRING(k);
3046
3047 k = NULL;
3048 }
3049 }
3050
3051 int config_parse_log_namespace(
3052 const char *unit,
3053 const char *filename,
3054 unsigned line,
3055 const char *section,
3056 unsigned section_line,
3057 const char *lvalue,
3058 int ltype,
3059 const char *rvalue,
3060 void *data,
3061 void *userdata) {
3062
3063 _cleanup_free_ char *k = NULL;
3064 ExecContext *c = data;
3065 const Unit *u = userdata;
3066 int r;
3067
3068 assert(filename);
3069 assert(lvalue);
3070 assert(rvalue);
3071 assert(c);
3072
3073 if (isempty(rvalue)) {
3074 c->log_namespace = mfree(c->log_namespace);
3075 return 0;
3076 }
3077
3078 r = unit_full_printf_full(u, rvalue, NAME_MAX, &k);
3079 if (r < 0) {
3080 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
3081 return 0;
3082 }
3083
3084 if (!log_namespace_name_valid(k)) {
3085 log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified log namespace name is not valid, ignoring: %s", k);
3086 return 0;
3087 }
3088
3089 free_and_replace(c->log_namespace, k);
3090 return 0;
3091 }
3092
3093 int config_parse_unit_condition_path(
3094 const char *unit,
3095 const char *filename,
3096 unsigned line,
3097 const char *section,
3098 unsigned section_line,
3099 const char *lvalue,
3100 int ltype,
3101 const char *rvalue,
3102 void *data,
3103 void *userdata) {
3104
3105 _cleanup_free_ char *p = NULL;
3106 Condition **list = data, *c;
3107 ConditionType t = ltype;
3108 bool trigger, negate;
3109 const Unit *u = userdata;
3110 int r;
3111
3112 assert(filename);
3113 assert(lvalue);
3114 assert(rvalue);
3115 assert(data);
3116
3117 if (isempty(rvalue)) {
3118 /* Empty assignment resets the list */
3119 *list = condition_free_list(*list);
3120 return 0;
3121 }
3122
3123 trigger = rvalue[0] == '|';
3124 if (trigger)
3125 rvalue++;
3126
3127 negate = rvalue[0] == '!';
3128 if (negate)
3129 rvalue++;
3130
3131 r = unit_path_printf(u, rvalue, &p);
3132 if (r < 0) {
3133 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
3134 return 0;
3135 }
3136
3137 r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
3138 if (r < 0)
3139 return 0;
3140
3141 c = condition_new(t, p, trigger, negate);
3142 if (!c)
3143 return log_oom();
3144
3145 LIST_PREPEND(conditions, *list, c);
3146 return 0;
3147 }
3148
3149 int config_parse_unit_condition_string(
3150 const char *unit,
3151 const char *filename,
3152 unsigned line,
3153 const char *section,
3154 unsigned section_line,
3155 const char *lvalue,
3156 int ltype,
3157 const char *rvalue,
3158 void *data,
3159 void *userdata) {
3160
3161 _cleanup_free_ char *s = NULL;
3162 Condition **list = data, *c;
3163 ConditionType t = ltype;
3164 bool trigger, negate;
3165 const Unit *u = userdata;
3166 int r;
3167
3168 assert(filename);
3169 assert(lvalue);
3170 assert(rvalue);
3171 assert(data);
3172
3173 if (isempty(rvalue)) {
3174 /* Empty assignment resets the list */
3175 *list = condition_free_list(*list);
3176 return 0;
3177 }
3178
3179 trigger = *rvalue == '|';
3180 if (trigger)
3181 rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
3182
3183 negate = *rvalue == '!';
3184 if (negate)
3185 rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
3186
3187 r = unit_full_printf(u, rvalue, &s);
3188 if (r < 0) {
3189 log_syntax(unit, LOG_WARNING, filename, line, r,
3190 "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
3191 return 0;
3192 }
3193
3194 c = condition_new(t, s, trigger, negate);
3195 if (!c)
3196 return log_oom();
3197
3198 LIST_PREPEND(conditions, *list, c);
3199 return 0;
3200 }
3201
3202 int config_parse_unit_requires_mounts_for(
3203 const char *unit,
3204 const char *filename,
3205 unsigned line,
3206 const char *section,
3207 unsigned section_line,
3208 const char *lvalue,
3209 int ltype,
3210 const char *rvalue,
3211 void *data,
3212 void *userdata) {
3213
3214 Unit *u = userdata;
3215 int r;
3216
3217 assert(filename);
3218 assert(lvalue);
3219 assert(rvalue);
3220 assert(data);
3221
3222 for (const char *p = rvalue;;) {
3223 _cleanup_free_ char *word = NULL, *resolved = NULL;
3224
3225 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3226 if (r == -ENOMEM)
3227 return log_oom();
3228 if (r < 0) {
3229 log_syntax(unit, LOG_WARNING, filename, line, r,
3230 "Invalid syntax, ignoring: %s", rvalue);
3231 return 0;
3232 }
3233 if (r == 0)
3234 return 0;
3235
3236 r = unit_path_printf(u, word, &resolved);
3237 if (r < 0) {
3238 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
3239 continue;
3240 }
3241
3242 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
3243 if (r < 0)
3244 continue;
3245
3246 r = unit_require_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE);
3247 if (r < 0) {
3248 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add required mount '%s', ignoring: %m", resolved);
3249 continue;
3250 }
3251 }
3252 }
3253
3254 int config_parse_documentation(
3255 const char *unit,
3256 const char *filename,
3257 unsigned line,
3258 const char *section,
3259 unsigned section_line,
3260 const char *lvalue,
3261 int ltype,
3262 const char *rvalue,
3263 void *data,
3264 void *userdata) {
3265
3266 Unit *u = userdata;
3267 int r;
3268 char **a, **b;
3269
3270 assert(filename);
3271 assert(lvalue);
3272 assert(rvalue);
3273 assert(u);
3274
3275 if (isempty(rvalue)) {
3276 /* Empty assignment resets the list */
3277 u->documentation = strv_free(u->documentation);
3278 return 0;
3279 }
3280
3281 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
3282 rvalue, data, userdata);
3283 if (r < 0)
3284 return r;
3285
3286 for (a = b = u->documentation; a && *a; a++) {
3287
3288 if (documentation_url_is_valid(*a))
3289 *(b++) = *a;
3290 else {
3291 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid URL, ignoring: %s", *a);
3292 free(*a);
3293 }
3294 }
3295 if (b)
3296 *b = NULL;
3297
3298 return 0;
3299 }
3300
3301 #if HAVE_SECCOMP
3302 int config_parse_syscall_filter(
3303 const char *unit,
3304 const char *filename,
3305 unsigned line,
3306 const char *section,
3307 unsigned section_line,
3308 const char *lvalue,
3309 int ltype,
3310 const char *rvalue,
3311 void *data,
3312 void *userdata) {
3313
3314 ExecContext *c = data;
3315 _unused_ const Unit *u = userdata;
3316 bool invert = false;
3317 int r;
3318
3319 assert(filename);
3320 assert(lvalue);
3321 assert(rvalue);
3322 assert(u);
3323
3324 if (isempty(rvalue)) {
3325 /* Empty assignment resets the list */
3326 c->syscall_filter = hashmap_free(c->syscall_filter);
3327 c->syscall_allow_list = false;
3328 return 0;
3329 }
3330
3331 if (rvalue[0] == '~') {
3332 invert = true;
3333 rvalue++;
3334 }
3335
3336 if (!c->syscall_filter) {
3337 c->syscall_filter = hashmap_new(NULL);
3338 if (!c->syscall_filter)
3339 return log_oom();
3340
3341 if (invert)
3342 /* Allow everything but the ones listed */
3343 c->syscall_allow_list = false;
3344 else {
3345 /* Allow nothing but the ones listed */
3346 c->syscall_allow_list = true;
3347
3348 /* Accept default syscalls if we are on an allow_list */
3349 r = seccomp_parse_syscall_filter(
3350 "@default", -1, c->syscall_filter,
3351 SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_ALLOW_LIST,
3352 unit,
3353 NULL, 0);
3354 if (r < 0)
3355 return r;
3356 }
3357 }
3358
3359 for (const char *p = rvalue;;) {
3360 _cleanup_free_ char *word = NULL, *name = NULL;
3361 int num;
3362
3363 r = extract_first_word(&p, &word, NULL, 0);
3364 if (r == -ENOMEM)
3365 return log_oom();
3366 if (r < 0) {
3367 log_syntax(unit, LOG_WARNING, filename, line, r,
3368 "Invalid syntax, ignoring: %s", rvalue);
3369 return 0;
3370 }
3371 if (r == 0)
3372 return 0;
3373
3374 r = parse_syscall_and_errno(word, &name, &num);
3375 if (r < 0) {
3376 log_syntax(unit, LOG_WARNING, filename, line, r,
3377 "Failed to parse syscall:errno, ignoring: %s", word);
3378 continue;
3379 }
3380 if (!invert && num >= 0) {
3381 log_syntax(unit, LOG_WARNING, filename, line, 0,
3382 "Allow-listed system calls cannot take error number, ignoring: %s", word);
3383 continue;
3384 }
3385
3386 r = seccomp_parse_syscall_filter(
3387 name, num, c->syscall_filter,
3388 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
3389 (invert ? SECCOMP_PARSE_INVERT : 0)|
3390 (c->syscall_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
3391 unit, filename, line);
3392 if (r < 0)
3393 return r;
3394 }
3395 }
3396
3397 int config_parse_syscall_log(
3398 const char *unit,
3399 const char *filename,
3400 unsigned line,
3401 const char *section,
3402 unsigned section_line,
3403 const char *lvalue,
3404 int ltype,
3405 const char *rvalue,
3406 void *data,
3407 void *userdata) {
3408
3409 ExecContext *c = data;
3410 _unused_ const Unit *u = userdata;
3411 bool invert = false;
3412 const char *p;
3413 int r;
3414
3415 assert(filename);
3416 assert(lvalue);
3417 assert(rvalue);
3418 assert(u);
3419
3420 if (isempty(rvalue)) {
3421 /* Empty assignment resets the list */
3422 c->syscall_log = hashmap_free(c->syscall_log);
3423 c->syscall_log_allow_list = false;
3424 return 0;
3425 }
3426
3427 if (rvalue[0] == '~') {
3428 invert = true;
3429 rvalue++;
3430 }
3431
3432 if (!c->syscall_log) {
3433 c->syscall_log = hashmap_new(NULL);
3434 if (!c->syscall_log)
3435 return log_oom();
3436
3437 if (invert)
3438 /* Log everything but the ones listed */
3439 c->syscall_log_allow_list = false;
3440 else
3441 /* Log nothing but the ones listed */
3442 c->syscall_log_allow_list = true;
3443 }
3444
3445 p = rvalue;
3446 for (;;) {
3447 _cleanup_free_ char *word = NULL;
3448
3449 r = extract_first_word(&p, &word, NULL, 0);
3450 if (r == -ENOMEM)
3451 return log_oom();
3452 if (r < 0) {
3453 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
3454 return 0;
3455 }
3456 if (r == 0)
3457 return 0;
3458
3459 r = seccomp_parse_syscall_filter(
3460 word, -1, c->syscall_log,
3461 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
3462 (invert ? SECCOMP_PARSE_INVERT : 0)|
3463 (c->syscall_log_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
3464 unit, filename, line);
3465 if (r < 0)
3466 return r;
3467 }
3468 }
3469
3470 int config_parse_syscall_archs(
3471 const char *unit,
3472 const char *filename,
3473 unsigned line,
3474 const char *section,
3475 unsigned section_line,
3476 const char *lvalue,
3477 int ltype,
3478 const char *rvalue,
3479 void *data,
3480 void *userdata) {
3481
3482 Set **archs = data;
3483 int r;
3484
3485 if (isempty(rvalue)) {
3486 *archs = set_free(*archs);
3487 return 0;
3488 }
3489
3490 for (const char *p = rvalue;;) {
3491 _cleanup_free_ char *word = NULL;
3492 uint32_t a;
3493
3494 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3495 if (r == -ENOMEM)
3496 return log_oom();
3497 if (r < 0) {
3498 log_syntax(unit, LOG_WARNING, filename, line, r,
3499 "Invalid syntax, ignoring: %s", rvalue);
3500 return 0;
3501 }
3502 if (r == 0)
3503 return 0;
3504
3505 r = seccomp_arch_from_string(word, &a);
3506 if (r < 0) {
3507 log_syntax(unit, LOG_WARNING, filename, line, r,
3508 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
3509 continue;
3510 }
3511
3512 r = set_ensure_put(archs, NULL, UINT32_TO_PTR(a + 1));
3513 if (r < 0)
3514 return log_oom();
3515 }
3516 }
3517
3518 int config_parse_syscall_errno(
3519 const char *unit,
3520 const char *filename,
3521 unsigned line,
3522 const char *section,
3523 unsigned section_line,
3524 const char *lvalue,
3525 int ltype,
3526 const char *rvalue,
3527 void *data,
3528 void *userdata) {
3529
3530 ExecContext *c = data;
3531 int e;
3532
3533 assert(filename);
3534 assert(lvalue);
3535 assert(rvalue);
3536
3537 if (isempty(rvalue) || streq(rvalue, "kill")) {
3538 /* Empty assignment resets to KILL */
3539 c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
3540 return 0;
3541 }
3542
3543 e = parse_errno(rvalue);
3544 if (e < 0) {
3545 log_syntax(unit, LOG_WARNING, filename, line, e, "Failed to parse error number, ignoring: %s", rvalue);
3546 return 0;
3547 }
3548 if (e == 0) {
3549 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid error number, ignoring: %s", rvalue);
3550 return 0;
3551 }
3552
3553 c->syscall_errno = e;
3554 return 0;
3555 }
3556
3557 int config_parse_address_families(
3558 const char *unit,
3559 const char *filename,
3560 unsigned line,
3561 const char *section,
3562 unsigned section_line,
3563 const char *lvalue,
3564 int ltype,
3565 const char *rvalue,
3566 void *data,
3567 void *userdata) {
3568
3569 ExecContext *c = data;
3570 bool invert = false;
3571 int r;
3572
3573 assert(filename);
3574 assert(lvalue);
3575 assert(rvalue);
3576
3577 if (isempty(rvalue)) {
3578 /* Empty assignment resets the list */
3579 c->address_families = set_free(c->address_families);
3580 c->address_families_allow_list = false;
3581 return 0;
3582 }
3583
3584 if (streq(rvalue, "none")) {
3585 /* Forbid all address families. */
3586 c->address_families = set_free(c->address_families);
3587 c->address_families_allow_list = true;
3588 return 0;
3589 }
3590
3591 if (rvalue[0] == '~') {
3592 invert = true;
3593 rvalue++;
3594 }
3595
3596 if (!c->address_families) {
3597 c->address_families = set_new(NULL);
3598 if (!c->address_families)
3599 return log_oom();
3600
3601 c->address_families_allow_list = !invert;
3602 }
3603
3604 for (const char *p = rvalue;;) {
3605 _cleanup_free_ char *word = NULL;
3606 int af;
3607
3608 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3609 if (r == -ENOMEM)
3610 return log_oom();
3611 if (r < 0) {
3612 log_syntax(unit, LOG_WARNING, filename, line, r,
3613 "Invalid syntax, ignoring: %s", rvalue);
3614 return 0;
3615 }
3616 if (r == 0)
3617 return 0;
3618
3619 af = af_from_name(word);
3620 if (af < 0) {
3621 log_syntax(unit, LOG_WARNING, filename, line, af,
3622 "Failed to parse address family, ignoring: %s", word);
3623 continue;
3624 }
3625
3626 /* If we previously wanted to forbid an address family and now
3627 * we want to allow it, then just remove it from the list.
3628 */
3629 if (!invert == c->address_families_allow_list) {
3630 r = set_put(c->address_families, INT_TO_PTR(af));
3631 if (r < 0)
3632 return log_oom();
3633 } else
3634 set_remove(c->address_families, INT_TO_PTR(af));
3635 }
3636 }
3637
3638 int config_parse_restrict_namespaces(
3639 const char *unit,
3640 const char *filename,
3641 unsigned line,
3642 const char *section,
3643 unsigned section_line,
3644 const char *lvalue,
3645 int ltype,
3646 const char *rvalue,
3647 void *data,
3648 void *userdata) {
3649
3650 ExecContext *c = data;
3651 unsigned long flags;
3652 bool invert = false;
3653 int r;
3654
3655 if (isempty(rvalue)) {
3656 /* Reset to the default. */
3657 c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL;
3658 return 0;
3659 }
3660
3661 /* Boolean parameter ignores the previous settings */
3662 r = parse_boolean(rvalue);
3663 if (r > 0) {
3664 c->restrict_namespaces = 0;
3665 return 0;
3666 } else if (r == 0) {
3667 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
3668 return 0;
3669 }
3670
3671 if (rvalue[0] == '~') {
3672 invert = true;
3673 rvalue++;
3674 }
3675
3676 /* Not a boolean argument, in this case it's a list of namespace types. */
3677 r = namespace_flags_from_string(rvalue, &flags);
3678 if (r < 0) {
3679 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
3680 return 0;
3681 }
3682
3683 if (c->restrict_namespaces == NAMESPACE_FLAGS_INITIAL)
3684 /* Initial assignment. Just set the value. */
3685 c->restrict_namespaces = invert ? (~flags) & NAMESPACE_FLAGS_ALL : flags;
3686 else
3687 /* Merge the value with the previous one. */
3688 SET_FLAG(c->restrict_namespaces, flags, !invert);
3689
3690 return 0;
3691 }
3692 #endif
3693
3694 int config_parse_restrict_filesystems(
3695 const char *unit,
3696 const char *filename,
3697 unsigned line,
3698 const char *section,
3699 unsigned section_line,
3700 const char *lvalue,
3701 int ltype,
3702 const char *rvalue,
3703 void *data,
3704 void *userdata) {
3705 ExecContext *c = data;
3706 bool invert = false;
3707 int r;
3708
3709 assert(filename);
3710 assert(lvalue);
3711 assert(rvalue);
3712 assert(data);
3713
3714 if (isempty(rvalue)) {
3715 /* Empty assignment resets the list */
3716 c->restrict_filesystems = set_free(c->restrict_filesystems);
3717 c->restrict_filesystems_allow_list = false;
3718 return 0;
3719 }
3720
3721 if (rvalue[0] == '~') {
3722 invert = true;
3723 rvalue++;
3724 }
3725
3726 if (!c->restrict_filesystems) {
3727 if (invert)
3728 /* Allow everything but the ones listed */
3729 c->restrict_filesystems_allow_list = false;
3730 else
3731 /* Allow nothing but the ones listed */
3732 c->restrict_filesystems_allow_list = true;
3733 }
3734
3735 for (const char *p = rvalue;;) {
3736 _cleanup_free_ char *word = NULL;
3737
3738 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3739 if (r == 0)
3740 break;
3741 if (r == -ENOMEM)
3742 return log_oom();
3743 if (r < 0) {
3744 log_syntax(unit, LOG_WARNING, filename, line, r,
3745 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
3746 break;
3747 }
3748
3749 r = lsm_bpf_parse_filesystem(
3750 word,
3751 &c->restrict_filesystems,
3752 FILESYSTEM_PARSE_LOG|
3753 (invert ? FILESYSTEM_PARSE_INVERT : 0)|
3754 (c->restrict_filesystems_allow_list ? FILESYSTEM_PARSE_ALLOW_LIST : 0),
3755 unit, filename, line);
3756
3757 if (r < 0)
3758 return r;
3759 }
3760
3761 return 0;
3762 }
3763
3764 int config_parse_unit_slice(
3765 const char *unit,
3766 const char *filename,
3767 unsigned line,
3768 const char *section,
3769 unsigned section_line,
3770 const char *lvalue,
3771 int ltype,
3772 const char *rvalue,
3773 void *data,
3774 void *userdata) {
3775
3776 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3777 _cleanup_free_ char *k = NULL;
3778 Unit *u = userdata, *slice;
3779 int r;
3780
3781 assert(filename);
3782 assert(lvalue);
3783 assert(rvalue);
3784 assert(u);
3785
3786 r = unit_name_printf(u, rvalue, &k);
3787 if (r < 0) {
3788 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
3789 return 0;
3790 }
3791
3792 r = manager_load_unit(u->manager, k, NULL, &error, &slice);
3793 if (r < 0) {
3794 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to load slice unit %s, ignoring: %s", k, bus_error_message(&error, r));
3795 return 0;
3796 }
3797
3798 r = unit_set_slice(u, slice);
3799 if (r < 0) {
3800 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to assign slice %s to unit %s, ignoring: %m", slice->id, u->id);
3801 return 0;
3802 }
3803
3804 return 0;
3805 }
3806
3807 int config_parse_cpu_quota(
3808 const char *unit,
3809 const char *filename,
3810 unsigned line,
3811 const char *section,
3812 unsigned section_line,
3813 const char *lvalue,
3814 int ltype,
3815 const char *rvalue,
3816 void *data,
3817 void *userdata) {
3818
3819 CGroupContext *c = data;
3820 int r;
3821
3822 assert(filename);
3823 assert(lvalue);
3824 assert(rvalue);
3825
3826 if (isempty(rvalue)) {
3827 c->cpu_quota_per_sec_usec = USEC_INFINITY;
3828 return 0;
3829 }
3830
3831 r = parse_permyriad_unbounded(rvalue);
3832 if (r <= 0) {
3833 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid CPU quota '%s', ignoring.", rvalue);
3834 return 0;
3835 }
3836
3837 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 10000U;
3838 return 0;
3839 }
3840
3841 int config_parse_allowed_cpuset(
3842 const char *unit,
3843 const char *filename,
3844 unsigned line,
3845 const char *section,
3846 unsigned section_line,
3847 const char *lvalue,
3848 int ltype,
3849 const char *rvalue,
3850 void *data,
3851 void *userdata) {
3852
3853 CPUSet *c = data;
3854
3855 (void) parse_cpu_set_extend(rvalue, c, true, unit, filename, line, lvalue);
3856 return 0;
3857 }
3858
3859 int config_parse_memory_limit(
3860 const char *unit,
3861 const char *filename,
3862 unsigned line,
3863 const char *section,
3864 unsigned section_line,
3865 const char *lvalue,
3866 int ltype,
3867 const char *rvalue,
3868 void *data,
3869 void *userdata) {
3870
3871 CGroupContext *c = data;
3872 uint64_t bytes = CGROUP_LIMIT_MAX;
3873 int r;
3874
3875 if (isempty(rvalue) && STR_IN_SET(lvalue, "DefaultMemoryLow",
3876 "DefaultMemoryMin",
3877 "MemoryLow",
3878 "MemoryMin"))
3879 bytes = CGROUP_LIMIT_MIN;
3880 else if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
3881
3882 r = parse_permyriad(rvalue);
3883 if (r < 0) {
3884 r = parse_size(rvalue, 1024, &bytes);
3885 if (r < 0) {
3886 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid memory limit '%s', ignoring: %m", rvalue);
3887 return 0;
3888 }
3889 } else
3890 bytes = physical_memory_scale(r, 10000U);
3891
3892 if (bytes >= UINT64_MAX ||
3893 (bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) {
3894 log_syntax(unit, LOG_WARNING, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue);
3895 return 0;
3896 }
3897 }
3898
3899 if (streq(lvalue, "DefaultMemoryLow")) {
3900 c->default_memory_low = bytes;
3901 c->default_memory_low_set = true;
3902 } else if (streq(lvalue, "DefaultMemoryMin")) {
3903 c->default_memory_min = bytes;
3904 c->default_memory_min_set = true;
3905 } else if (streq(lvalue, "MemoryMin")) {
3906 c->memory_min = bytes;
3907 c->memory_min_set = true;
3908 } else if (streq(lvalue, "MemoryLow")) {
3909 c->memory_low = bytes;
3910 c->memory_low_set = true;
3911 } else if (streq(lvalue, "MemoryHigh"))
3912 c->memory_high = bytes;
3913 else if (streq(lvalue, "MemoryMax"))
3914 c->memory_max = bytes;
3915 else if (streq(lvalue, "MemorySwapMax"))
3916 c->memory_swap_max = bytes;
3917 else if (streq(lvalue, "MemoryLimit"))
3918 c->memory_limit = bytes;
3919 else
3920 return -EINVAL;
3921
3922 return 0;
3923 }
3924
3925 int config_parse_tasks_max(
3926 const char *unit,
3927 const char *filename,
3928 unsigned line,
3929 const char *section,
3930 unsigned section_line,
3931 const char *lvalue,
3932 int ltype,
3933 const char *rvalue,
3934 void *data,
3935 void *userdata) {
3936
3937 const Unit *u = userdata;
3938 TasksMax *tasks_max = data;
3939 uint64_t v;
3940 int r;
3941
3942 if (isempty(rvalue)) {
3943 *tasks_max = u ? u->manager->default_tasks_max : TASKS_MAX_UNSET;
3944 return 0;
3945 }
3946
3947 if (streq(rvalue, "infinity")) {
3948 *tasks_max = TASKS_MAX_UNSET;
3949 return 0;
3950 }
3951
3952 r = parse_permyriad(rvalue);
3953 if (r >= 0)
3954 *tasks_max = (TasksMax) { r, 10000U }; /* r‱ */
3955 else {
3956 r = safe_atou64(rvalue, &v);
3957 if (r < 0) {
3958 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid maximum tasks value '%s', ignoring: %m", rvalue);
3959 return 0;
3960 }
3961
3962 if (v <= 0 || v >= UINT64_MAX) {
3963 log_syntax(unit, LOG_WARNING, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
3964 return 0;
3965 }
3966
3967 *tasks_max = (TasksMax) { v };
3968 }
3969
3970 return 0;
3971 }
3972
3973 int config_parse_delegate(
3974 const char *unit,
3975 const char *filename,
3976 unsigned line,
3977 const char *section,
3978 unsigned section_line,
3979 const char *lvalue,
3980 int ltype,
3981 const char *rvalue,
3982 void *data,
3983 void *userdata) {
3984
3985 CGroupContext *c = data;
3986 UnitType t;
3987 int r;
3988
3989 t = unit_name_to_type(unit);
3990 assert(t != _UNIT_TYPE_INVALID);
3991
3992 if (!unit_vtable[t]->can_delegate) {
3993 log_syntax(unit, LOG_WARNING, filename, line, 0, "Delegate= setting not supported for this unit type, ignoring.");
3994 return 0;
3995 }
3996
3997 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3998 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3999 * mask to delegate. */
4000
4001 if (isempty(rvalue)) {
4002 /* An empty string resets controllers and set Delegate=yes. */
4003 c->delegate = true;
4004 c->delegate_controllers = 0;
4005 return 0;
4006 }
4007
4008 r = parse_boolean(rvalue);
4009 if (r < 0) {
4010 CGroupMask mask = 0;
4011
4012 for (const char *p = rvalue;;) {
4013 _cleanup_free_ char *word = NULL;
4014 CGroupController cc;
4015
4016 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
4017 if (r == -ENOMEM)
4018 return log_oom();
4019 if (r < 0) {
4020 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
4021 return 0;
4022 }
4023 if (r == 0)
4024 break;
4025
4026 cc = cgroup_controller_from_string(word);
4027 if (cc < 0) {
4028 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid controller name '%s', ignoring", word);
4029 continue;
4030 }
4031
4032 mask |= CGROUP_CONTROLLER_TO_MASK(cc);
4033 }
4034
4035 c->delegate = true;
4036 c->delegate_controllers |= mask;
4037
4038 } else if (r > 0) {
4039 c->delegate = true;
4040 c->delegate_controllers = _CGROUP_MASK_ALL;
4041 } else {
4042 c->delegate = false;
4043 c->delegate_controllers = 0;
4044 }
4045
4046 return 0;
4047 }
4048
4049 int config_parse_managed_oom_mode(
4050 const char *unit,
4051 const char *filename,
4052 unsigned line,
4053 const char *section,
4054 unsigned section_line,
4055 const char *lvalue,
4056 int ltype,
4057 const char *rvalue,
4058 void *data,
4059 void *userdata) {
4060
4061 ManagedOOMMode *mode = data, m;
4062 UnitType t;
4063
4064 t = unit_name_to_type(unit);
4065 assert(t != _UNIT_TYPE_INVALID);
4066
4067 if (!unit_vtable[t]->can_set_managed_oom)
4068 return log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= is not supported for this unit type, ignoring.", lvalue);
4069
4070 if (isempty(rvalue)) {
4071 *mode = MANAGED_OOM_AUTO;
4072 return 0;
4073 }
4074
4075 m = managed_oom_mode_from_string(rvalue);
4076 if (m < 0) {
4077 log_syntax(unit, LOG_WARNING, filename, line, m, "Invalid syntax, ignoring: %s", rvalue);
4078 return 0;
4079 }
4080
4081 *mode = m;
4082 return 0;
4083 }
4084
4085 int config_parse_managed_oom_mem_pressure_limit(
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 uint32_t *limit = data;
4098 UnitType t;
4099 int r;
4100
4101 t = unit_name_to_type(unit);
4102 assert(t != _UNIT_TYPE_INVALID);
4103
4104 if (!unit_vtable[t]->can_set_managed_oom)
4105 return log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= is not supported for this unit type, ignoring.", lvalue);
4106
4107 if (isempty(rvalue)) {
4108 *limit = 0;
4109 return 0;
4110 }
4111
4112 r = parse_permyriad(rvalue);
4113 if (r < 0) {
4114 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse memory pressure limit value, ignoring: %s", rvalue);
4115 return 0;
4116 }
4117
4118 /* Normalize to 2^32-1 == 100% */
4119 *limit = UINT32_SCALE_FROM_PERMYRIAD(r);
4120 return 0;
4121 }
4122
4123 int config_parse_device_allow(
4124 const char *unit,
4125 const char *filename,
4126 unsigned line,
4127 const char *section,
4128 unsigned section_line,
4129 const char *lvalue,
4130 int ltype,
4131 const char *rvalue,
4132 void *data,
4133 void *userdata) {
4134
4135 _cleanup_free_ char *path = NULL, *resolved = NULL;
4136 CGroupContext *c = data;
4137 const char *p = rvalue;
4138 int r;
4139
4140 if (isempty(rvalue)) {
4141 while (c->device_allow)
4142 cgroup_context_free_device_allow(c, c->device_allow);
4143
4144 return 0;
4145 }
4146
4147 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4148 if (r == -ENOMEM)
4149 return log_oom();
4150 if (r <= 0) {
4151 log_syntax(unit, LOG_WARNING, filename, line, r,
4152 "Failed to extract device path and rights from '%s', ignoring.", rvalue);
4153 return 0;
4154 }
4155
4156 r = unit_path_printf(userdata, path, &resolved);
4157 if (r < 0) {
4158 log_syntax(unit, LOG_WARNING, filename, line, r,
4159 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4160 return 0;
4161 }
4162
4163 if (!STARTSWITH_SET(resolved, "block-", "char-")) {
4164
4165 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4166 if (r < 0)
4167 return 0;
4168
4169 if (!valid_device_node_path(resolved)) {
4170 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid device node path '%s', ignoring.", resolved);
4171 return 0;
4172 }
4173 }
4174
4175 if (!isempty(p) && !in_charset(p, "rwm")) {
4176 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid device rights '%s', ignoring.", p);
4177 return 0;
4178 }
4179
4180 return cgroup_add_device_allow(c, resolved, p);
4181 }
4182
4183 int config_parse_io_device_weight(
4184 const char *unit,
4185 const char *filename,
4186 unsigned line,
4187 const char *section,
4188 unsigned section_line,
4189 const char *lvalue,
4190 int ltype,
4191 const char *rvalue,
4192 void *data,
4193 void *userdata) {
4194
4195 _cleanup_free_ char *path = NULL, *resolved = NULL;
4196 CGroupIODeviceWeight *w;
4197 CGroupContext *c = data;
4198 const char *p = rvalue;
4199 uint64_t u;
4200 int r;
4201
4202 assert(filename);
4203 assert(lvalue);
4204 assert(rvalue);
4205
4206 if (isempty(rvalue)) {
4207 while (c->io_device_weights)
4208 cgroup_context_free_io_device_weight(c, c->io_device_weights);
4209
4210 return 0;
4211 }
4212
4213 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4214 if (r == -ENOMEM)
4215 return log_oom();
4216 if (r < 0) {
4217 log_syntax(unit, LOG_WARNING, filename, line, r,
4218 "Failed to extract device path and weight from '%s', ignoring.", rvalue);
4219 return 0;
4220 }
4221 if (r == 0 || isempty(p)) {
4222 log_syntax(unit, LOG_WARNING, filename, line, 0,
4223 "Invalid device path or weight specified in '%s', ignoring.", rvalue);
4224 return 0;
4225 }
4226
4227 r = unit_path_printf(userdata, path, &resolved);
4228 if (r < 0) {
4229 log_syntax(unit, LOG_WARNING, filename, line, r,
4230 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4231 return 0;
4232 }
4233
4234 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4235 if (r < 0)
4236 return 0;
4237
4238 r = cg_weight_parse(p, &u);
4239 if (r < 0) {
4240 log_syntax(unit, LOG_WARNING, filename, line, r, "IO weight '%s' invalid, ignoring: %m", p);
4241 return 0;
4242 }
4243
4244 assert(u != CGROUP_WEIGHT_INVALID);
4245
4246 w = new0(CGroupIODeviceWeight, 1);
4247 if (!w)
4248 return log_oom();
4249
4250 w->path = TAKE_PTR(resolved);
4251 w->weight = u;
4252
4253 LIST_PREPEND(device_weights, c->io_device_weights, w);
4254 return 0;
4255 }
4256
4257 int config_parse_io_device_latency(
4258 const char *unit,
4259 const char *filename,
4260 unsigned line,
4261 const char *section,
4262 unsigned section_line,
4263 const char *lvalue,
4264 int ltype,
4265 const char *rvalue,
4266 void *data,
4267 void *userdata) {
4268
4269 _cleanup_free_ char *path = NULL, *resolved = NULL;
4270 CGroupIODeviceLatency *l;
4271 CGroupContext *c = data;
4272 const char *p = rvalue;
4273 usec_t usec;
4274 int r;
4275
4276 assert(filename);
4277 assert(lvalue);
4278 assert(rvalue);
4279
4280 if (isempty(rvalue)) {
4281 while (c->io_device_latencies)
4282 cgroup_context_free_io_device_latency(c, c->io_device_latencies);
4283
4284 return 0;
4285 }
4286
4287 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4288 if (r == -ENOMEM)
4289 return log_oom();
4290 if (r < 0) {
4291 log_syntax(unit, LOG_WARNING, filename, line, r,
4292 "Failed to extract device path and latency from '%s', ignoring.", rvalue);
4293 return 0;
4294 }
4295 if (r == 0 || isempty(p)) {
4296 log_syntax(unit, LOG_WARNING, filename, line, 0,
4297 "Invalid device path or latency specified in '%s', ignoring.", rvalue);
4298 return 0;
4299 }
4300
4301 r = unit_path_printf(userdata, path, &resolved);
4302 if (r < 0) {
4303 log_syntax(unit, LOG_WARNING, filename, line, r,
4304 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4305 return 0;
4306 }
4307
4308 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4309 if (r < 0)
4310 return 0;
4311
4312 r = parse_sec(p, &usec);
4313 if (r < 0) {
4314 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse timer value, ignoring: %s", p);
4315 return 0;
4316 }
4317
4318 l = new0(CGroupIODeviceLatency, 1);
4319 if (!l)
4320 return log_oom();
4321
4322 l->path = TAKE_PTR(resolved);
4323 l->target_usec = usec;
4324
4325 LIST_PREPEND(device_latencies, c->io_device_latencies, l);
4326 return 0;
4327 }
4328
4329 int config_parse_io_limit(
4330 const char *unit,
4331 const char *filename,
4332 unsigned line,
4333 const char *section,
4334 unsigned section_line,
4335 const char *lvalue,
4336 int ltype,
4337 const char *rvalue,
4338 void *data,
4339 void *userdata) {
4340
4341 _cleanup_free_ char *path = NULL, *resolved = NULL;
4342 CGroupIODeviceLimit *l = NULL;
4343 CGroupContext *c = data;
4344 CGroupIOLimitType type;
4345 const char *p = rvalue;
4346 uint64_t num;
4347 int r;
4348
4349 assert(filename);
4350 assert(lvalue);
4351 assert(rvalue);
4352
4353 type = cgroup_io_limit_type_from_string(lvalue);
4354 assert(type >= 0);
4355
4356 if (isempty(rvalue)) {
4357 LIST_FOREACH(device_limits, t, c->io_device_limits)
4358 t->limits[type] = cgroup_io_limit_defaults[type];
4359 return 0;
4360 }
4361
4362 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4363 if (r == -ENOMEM)
4364 return log_oom();
4365 if (r < 0) {
4366 log_syntax(unit, LOG_WARNING, filename, line, r,
4367 "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue);
4368 return 0;
4369 }
4370 if (r == 0 || isempty(p)) {
4371 log_syntax(unit, LOG_WARNING, filename, line, 0,
4372 "Invalid device node or bandwidth specified in '%s', ignoring.", rvalue);
4373 return 0;
4374 }
4375
4376 r = unit_path_printf(userdata, path, &resolved);
4377 if (r < 0) {
4378 log_syntax(unit, LOG_WARNING, filename, line, r,
4379 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4380 return 0;
4381 }
4382
4383 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4384 if (r < 0)
4385 return 0;
4386
4387 if (streq("infinity", p))
4388 num = CGROUP_LIMIT_MAX;
4389 else {
4390 r = parse_size(p, 1000, &num);
4391 if (r < 0 || num <= 0) {
4392 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid IO limit '%s', ignoring.", p);
4393 return 0;
4394 }
4395 }
4396
4397 LIST_FOREACH(device_limits, t, c->io_device_limits)
4398 if (path_equal(resolved, t->path)) {
4399 l = t;
4400 break;
4401 }
4402
4403 if (!l) {
4404 CGroupIOLimitType ttype;
4405
4406 l = new0(CGroupIODeviceLimit, 1);
4407 if (!l)
4408 return log_oom();
4409
4410 l->path = TAKE_PTR(resolved);
4411 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
4412 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
4413
4414 LIST_PREPEND(device_limits, c->io_device_limits, l);
4415 }
4416
4417 l->limits[type] = num;
4418
4419 return 0;
4420 }
4421
4422 int config_parse_blockio_device_weight(
4423 const char *unit,
4424 const char *filename,
4425 unsigned line,
4426 const char *section,
4427 unsigned section_line,
4428 const char *lvalue,
4429 int ltype,
4430 const char *rvalue,
4431 void *data,
4432 void *userdata) {
4433
4434 _cleanup_free_ char *path = NULL, *resolved = NULL;
4435 CGroupBlockIODeviceWeight *w;
4436 CGroupContext *c = data;
4437 const char *p = rvalue;
4438 uint64_t u;
4439 int r;
4440
4441 assert(filename);
4442 assert(lvalue);
4443 assert(rvalue);
4444
4445 if (isempty(rvalue)) {
4446 while (c->blockio_device_weights)
4447 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
4448
4449 return 0;
4450 }
4451
4452 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4453 if (r == -ENOMEM)
4454 return log_oom();
4455 if (r < 0) {
4456 log_syntax(unit, LOG_WARNING, filename, line, r,
4457 "Failed to extract device node and weight from '%s', ignoring.", rvalue);
4458 return 0;
4459 }
4460 if (r == 0 || isempty(p)) {
4461 log_syntax(unit, LOG_WARNING, filename, line, 0,
4462 "Invalid device node or weight specified in '%s', ignoring.", rvalue);
4463 return 0;
4464 }
4465
4466 r = unit_path_printf(userdata, path, &resolved);
4467 if (r < 0) {
4468 log_syntax(unit, LOG_WARNING, filename, line, r,
4469 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4470 return 0;
4471 }
4472
4473 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4474 if (r < 0)
4475 return 0;
4476
4477 r = cg_blkio_weight_parse(p, &u);
4478 if (r < 0) {
4479 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid block IO weight '%s', ignoring: %m", p);
4480 return 0;
4481 }
4482
4483 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
4484
4485 w = new0(CGroupBlockIODeviceWeight, 1);
4486 if (!w)
4487 return log_oom();
4488
4489 w->path = TAKE_PTR(resolved);
4490 w->weight = u;
4491
4492 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4493 return 0;
4494 }
4495
4496 int config_parse_blockio_bandwidth(
4497 const char *unit,
4498 const char *filename,
4499 unsigned line,
4500 const char *section,
4501 unsigned section_line,
4502 const char *lvalue,
4503 int ltype,
4504 const char *rvalue,
4505 void *data,
4506 void *userdata) {
4507
4508 _cleanup_free_ char *path = NULL, *resolved = NULL;
4509 CGroupBlockIODeviceBandwidth *b = NULL;
4510 CGroupContext *c = data;
4511 const char *p = rvalue;
4512 uint64_t bytes;
4513 bool read;
4514 int r;
4515
4516 assert(filename);
4517 assert(lvalue);
4518 assert(rvalue);
4519
4520 read = streq("BlockIOReadBandwidth", lvalue);
4521
4522 if (isempty(rvalue)) {
4523 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
4524 t->rbps = CGROUP_LIMIT_MAX;
4525 t->wbps = CGROUP_LIMIT_MAX;
4526 }
4527 return 0;
4528 }
4529
4530 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4531 if (r == -ENOMEM)
4532 return log_oom();
4533 if (r < 0) {
4534 log_syntax(unit, LOG_WARNING, filename, line, r,
4535 "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue);
4536 return 0;
4537 }
4538 if (r == 0 || isempty(p)) {
4539 log_syntax(unit, LOG_WARNING, filename, line, 0,
4540 "Invalid device node or bandwidth specified in '%s', ignoring.", rvalue);
4541 return 0;
4542 }
4543
4544 r = unit_path_printf(userdata, path, &resolved);
4545 if (r < 0) {
4546 log_syntax(unit, LOG_WARNING, filename, line, r,
4547 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4548 return 0;
4549 }
4550
4551 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4552 if (r < 0)
4553 return 0;
4554
4555 r = parse_size(p, 1000, &bytes);
4556 if (r < 0 || bytes <= 0) {
4557 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid Block IO Bandwidth '%s', ignoring.", p);
4558 return 0;
4559 }
4560
4561 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths)
4562 if (path_equal(resolved, t->path)) {
4563 b = t;
4564 break;
4565 }
4566
4567 if (!b) {
4568 b = new0(CGroupBlockIODeviceBandwidth, 1);
4569 if (!b)
4570 return log_oom();
4571
4572 b->path = TAKE_PTR(resolved);
4573 b->rbps = CGROUP_LIMIT_MAX;
4574 b->wbps = CGROUP_LIMIT_MAX;
4575
4576 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
4577 }
4578
4579 if (read)
4580 b->rbps = bytes;
4581 else
4582 b->wbps = bytes;
4583
4584 return 0;
4585 }
4586
4587 int config_parse_job_mode_isolate(
4588 const char *unit,
4589 const char *filename,
4590 unsigned line,
4591 const char *section,
4592 unsigned section_line,
4593 const char *lvalue,
4594 int ltype,
4595 const char *rvalue,
4596 void *data,
4597 void *userdata) {
4598
4599 JobMode *m = data;
4600 int r;
4601
4602 assert(filename);
4603 assert(lvalue);
4604 assert(rvalue);
4605
4606 r = parse_boolean(rvalue);
4607 if (r < 0) {
4608 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
4609 return 0;
4610 }
4611
4612 log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue);
4613
4614 *m = r ? JOB_ISOLATE : JOB_REPLACE;
4615 return 0;
4616 }
4617
4618 int config_parse_exec_directories(
4619 const char *unit,
4620 const char *filename,
4621 unsigned line,
4622 const char *section,
4623 unsigned section_line,
4624 const char *lvalue,
4625 int ltype,
4626 const char *rvalue,
4627 void *data,
4628 void *userdata) {
4629
4630 ExecDirectory *ed = data;
4631 const Unit *u = userdata;
4632 int r;
4633
4634 assert(filename);
4635 assert(lvalue);
4636 assert(rvalue);
4637 assert(data);
4638
4639 if (isempty(rvalue)) {
4640 /* Empty assignment resets the list */
4641 exec_directory_done(ed);
4642 return 0;
4643 }
4644
4645 for (const char *p = rvalue;;) {
4646 _cleanup_free_ char *tuple = NULL;
4647
4648 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
4649 if (r == -ENOMEM)
4650 return log_oom();
4651 if (r < 0) {
4652 log_syntax(unit, LOG_WARNING, filename, line, r,
4653 "Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
4654 return 0;
4655 }
4656 if (r == 0)
4657 return 0;
4658
4659 _cleanup_free_ char *src = NULL, *dest = NULL;
4660 const char *q = tuple;
4661 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &src, &dest, NULL);
4662 if (r == -ENOMEM)
4663 return log_oom();
4664 if (r <= 0) {
4665 log_syntax(unit, LOG_WARNING, filename, line, r ?: SYNTHETIC_ERRNO(EINVAL),
4666 "Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
4667 return 0;
4668 }
4669
4670 _cleanup_free_ char *sresolved = NULL;
4671 r = unit_path_printf(u, src, &sresolved);
4672 if (r < 0) {
4673 log_syntax(unit, LOG_WARNING, filename, line, r,
4674 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", src);
4675 continue;
4676 }
4677
4678 r = path_simplify_and_warn(sresolved, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
4679 if (r < 0)
4680 continue;
4681
4682 if (path_startswith(sresolved, "private")) {
4683 log_syntax(unit, LOG_WARNING, filename, line, 0,
4684 "%s= path can't be 'private', ignoring assignment: %s", lvalue, tuple);
4685 continue;
4686 }
4687
4688 /* For State and Runtime directories we support an optional destination parameter, which
4689 * will be used to create a symlink to the source. */
4690 _cleanup_strv_free_ char **symlinks = NULL;
4691 if (!isempty(dest)) {
4692 _cleanup_free_ char *dresolved = NULL;
4693
4694 if (streq(lvalue, "ConfigurationDirectory")) {
4695 log_syntax(unit, LOG_WARNING, filename, line, 0,
4696 "Destination parameter is not supported for ConfigurationDirectory, ignoring: %s", tuple);
4697 continue;
4698 }
4699
4700 r = unit_path_printf(u, dest, &dresolved);
4701 if (r < 0) {
4702 log_syntax(unit, LOG_WARNING, filename, line, r,
4703 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", dest);
4704 continue;
4705 }
4706
4707 r = path_simplify_and_warn(dresolved, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
4708 if (r < 0)
4709 continue;
4710
4711 r = strv_consume(&symlinks, TAKE_PTR(dresolved));
4712 if (r < 0)
4713 return log_oom();
4714 }
4715
4716 r = exec_directory_add(&ed->items, &ed->n_items, sresolved, symlinks);
4717 if (r < 0)
4718 return log_oom();
4719 }
4720 }
4721
4722 int config_parse_set_credential(
4723 const char *unit,
4724 const char *filename,
4725 unsigned line,
4726 const char *section,
4727 unsigned section_line,
4728 const char *lvalue,
4729 int ltype,
4730 const char *rvalue,
4731 void *data,
4732 void *userdata) {
4733
4734 _cleanup_free_ char *word = NULL, *k = NULL;
4735 _cleanup_free_ void *d = NULL;
4736 ExecContext *context = data;
4737 ExecSetCredential *old;
4738 Unit *u = userdata;
4739 bool encrypted = ltype;
4740 const char *p = rvalue;
4741 size_t size;
4742 int r;
4743
4744 assert(filename);
4745 assert(lvalue);
4746 assert(rvalue);
4747 assert(context);
4748
4749 if (isempty(rvalue)) {
4750 /* Empty assignment resets the list */
4751 context->set_credentials = hashmap_free(context->set_credentials);
4752 return 0;
4753 }
4754
4755 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4756 if (r == -ENOMEM)
4757 return log_oom();
4758 if (r < 0) {
4759 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract credential name, ignoring: %s", rvalue);
4760 return 0;
4761 }
4762 if (r == 0 || isempty(p)) {
4763 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid syntax, ignoring: %s", rvalue);
4764 return 0;
4765 }
4766
4767 r = unit_cred_printf(u, word, &k);
4768 if (r < 0) {
4769 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4770 return 0;
4771 }
4772 if (!credential_name_valid(k)) {
4773 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", k);
4774 return 0;
4775 }
4776
4777 if (encrypted) {
4778 r = unbase64mem_full(p, SIZE_MAX, true, &d, &size);
4779 if (r < 0) {
4780 log_syntax(unit, LOG_WARNING, filename, line, r, "Encrypted credential data not valid Base64 data, ignoring.");
4781 return 0;
4782 }
4783 } else {
4784 char *unescaped;
4785 ssize_t l;
4786
4787 /* We support escape codes here, so that users can insert trailing \n if they like */
4788 l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
4789 if (l < 0) {
4790 log_syntax(unit, LOG_WARNING, filename, line, l, "Can't unescape \"%s\", ignoring: %m", p);
4791 return 0;
4792 }
4793
4794 d = unescaped;
4795 size = l;
4796 }
4797
4798 old = hashmap_get(context->set_credentials, k);
4799 if (old) {
4800 free_and_replace(old->data, d);
4801 old->size = size;
4802 old->encrypted = encrypted;
4803 } else {
4804 _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL;
4805
4806 sc = new(ExecSetCredential, 1);
4807 if (!sc)
4808 return log_oom();
4809
4810 *sc = (ExecSetCredential) {
4811 .id = TAKE_PTR(k),
4812 .data = TAKE_PTR(d),
4813 .size = size,
4814 .encrypted = encrypted,
4815 };
4816
4817 r = hashmap_ensure_put(&context->set_credentials, &exec_set_credential_hash_ops, sc->id, sc);
4818 if (r == -ENOMEM)
4819 return log_oom();
4820 if (r < 0) {
4821 log_syntax(unit, LOG_WARNING, filename, line, r,
4822 "Duplicated credential value '%s', ignoring assignment: %s", sc->id, rvalue);
4823 return 0;
4824 }
4825
4826 TAKE_PTR(sc);
4827 }
4828
4829 return 0;
4830 }
4831
4832 int config_parse_load_credential(
4833 const char *unit,
4834 const char *filename,
4835 unsigned line,
4836 const char *section,
4837 unsigned section_line,
4838 const char *lvalue,
4839 int ltype,
4840 const char *rvalue,
4841 void *data,
4842 void *userdata) {
4843
4844 _cleanup_free_ char *word = NULL, *k = NULL, *q = NULL;
4845 ExecContext *context = data;
4846 ExecLoadCredential *old;
4847 bool encrypted = ltype;
4848 Unit *u = userdata;
4849 const char *p;
4850 int r;
4851
4852 assert(filename);
4853 assert(lvalue);
4854 assert(rvalue);
4855 assert(context);
4856
4857 if (isempty(rvalue)) {
4858 /* Empty assignment resets the list */
4859 context->load_credentials = hashmap_free(context->load_credentials);
4860 return 0;
4861 }
4862
4863 p = rvalue;
4864 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4865 if (r == -ENOMEM)
4866 return log_oom();
4867 if (r <= 0) {
4868 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
4869 return 0;
4870 }
4871
4872 r = unit_cred_printf(u, word, &k);
4873 if (r < 0) {
4874 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4875 return 0;
4876 }
4877 if (!credential_name_valid(k)) {
4878 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", k);
4879 return 0;
4880 }
4881
4882 if (isempty(p)) {
4883 /* If only one field is specified take it as shortcut for inheriting a credential named
4884 * the same way from our parent */
4885 q = strdup(k);
4886 if (!q)
4887 return log_oom();
4888 } else {
4889 r = unit_path_printf(u, p, &q);
4890 if (r < 0) {
4891 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", p);
4892 return 0;
4893 }
4894 if (path_is_absolute(q) ? !path_is_normalized(q) : !credential_name_valid(q)) {
4895 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential source \"%s\" not valid, ignoring.", q);
4896 return 0;
4897 }
4898 }
4899
4900 old = hashmap_get(context->load_credentials, k);
4901 if (old) {
4902 free_and_replace(old->path, q);
4903 old->encrypted = encrypted;
4904 } else {
4905 _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
4906
4907 lc = new(ExecLoadCredential, 1);
4908 if (!lc)
4909 return log_oom();
4910
4911 *lc = (ExecLoadCredential) {
4912 .id = TAKE_PTR(k),
4913 .path = TAKE_PTR(q),
4914 .encrypted = encrypted,
4915 };
4916
4917 r = hashmap_ensure_put(&context->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
4918 if (r == -ENOMEM)
4919 return log_oom();
4920 if (r < 0) {
4921 log_syntax(unit, LOG_WARNING, filename, line, r,
4922 "Duplicated credential value '%s', ignoring assignment: %s", lc->id, rvalue);
4923 return 0;
4924 }
4925
4926 TAKE_PTR(lc);
4927 }
4928
4929 return 0;
4930 }
4931
4932 int config_parse_set_status(
4933 const char *unit,
4934 const char *filename,
4935 unsigned line,
4936 const char *section,
4937 unsigned section_line,
4938 const char *lvalue,
4939 int ltype,
4940 const char *rvalue,
4941 void *data,
4942 void *userdata) {
4943
4944 ExitStatusSet *status_set = data;
4945 int r;
4946
4947 assert(filename);
4948 assert(lvalue);
4949 assert(rvalue);
4950 assert(status_set);
4951
4952 /* Empty assignment resets the list */
4953 if (isempty(rvalue)) {
4954 exit_status_set_free(status_set);
4955 return 0;
4956 }
4957
4958 for (const char *p = rvalue;;) {
4959 _cleanup_free_ char *word = NULL;
4960 Bitmap *bitmap;
4961
4962 r = extract_first_word(&p, &word, NULL, 0);
4963 if (r == -ENOMEM)
4964 return log_oom();
4965 if (r < 0) {
4966 log_syntax(unit, LOG_WARNING, filename, line, r,
4967 "Failed to parse %s=%s, ignoring: %m", lvalue, rvalue);
4968 return 0;
4969 }
4970 if (r == 0)
4971 return 0;
4972
4973 /* We need to call exit_status_from_string() first, because we want
4974 * to parse numbers as exit statuses, not signals. */
4975
4976 r = exit_status_from_string(word);
4977 if (r >= 0) {
4978 assert(r >= 0 && r < 256);
4979 bitmap = &status_set->status;
4980 } else {
4981 r = signal_from_string(word);
4982 if (r < 0) {
4983 log_syntax(unit, LOG_WARNING, filename, line, r,
4984 "Failed to parse value, ignoring: %s", word);
4985 continue;
4986 }
4987 bitmap = &status_set->signal;
4988 }
4989
4990 r = bitmap_set(bitmap, r);
4991 if (r < 0)
4992 log_syntax(unit, LOG_WARNING, filename, line, r,
4993 "Failed to set signal or status %s, ignoring: %m", word);
4994 }
4995 }
4996
4997 int config_parse_namespace_path_strv(
4998 const char *unit,
4999 const char *filename,
5000 unsigned line,
5001 const char *section,
5002 unsigned section_line,
5003 const char *lvalue,
5004 int ltype,
5005 const char *rvalue,
5006 void *data,
5007 void *userdata) {
5008
5009 const Unit *u = userdata;
5010 char*** sv = data;
5011 int r;
5012
5013 assert(filename);
5014 assert(lvalue);
5015 assert(rvalue);
5016 assert(data);
5017
5018 if (isempty(rvalue)) {
5019 /* Empty assignment resets the list */
5020 *sv = strv_free(*sv);
5021 return 0;
5022 }
5023
5024 for (const char *p = rvalue;;) {
5025 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
5026 const char *w;
5027 bool ignore_enoent = false, shall_prefix = false;
5028
5029 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
5030 if (r == -ENOMEM)
5031 return log_oom();
5032 if (r < 0) {
5033 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
5034 return 0;
5035 }
5036 if (r == 0)
5037 break;
5038
5039 w = word;
5040 if (startswith(w, "-")) {
5041 ignore_enoent = true;
5042 w++;
5043 }
5044 if (startswith(w, "+")) {
5045 shall_prefix = true;
5046 w++;
5047 }
5048
5049 r = unit_path_printf(u, w, &resolved);
5050 if (r < 0) {
5051 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s: %m", w);
5052 continue;
5053 }
5054
5055 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5056 if (r < 0)
5057 continue;
5058
5059 joined = strjoin(ignore_enoent ? "-" : "",
5060 shall_prefix ? "+" : "",
5061 resolved);
5062
5063 r = strv_push(sv, joined);
5064 if (r < 0)
5065 return log_oom();
5066
5067 joined = NULL;
5068 }
5069
5070 return 0;
5071 }
5072
5073 int config_parse_temporary_filesystems(
5074 const char *unit,
5075 const char *filename,
5076 unsigned line,
5077 const char *section,
5078 unsigned section_line,
5079 const char *lvalue,
5080 int ltype,
5081 const char *rvalue,
5082 void *data,
5083 void *userdata) {
5084
5085 const Unit *u = userdata;
5086 ExecContext *c = data;
5087 int r;
5088
5089 assert(filename);
5090 assert(lvalue);
5091 assert(rvalue);
5092 assert(data);
5093
5094 if (isempty(rvalue)) {
5095 /* Empty assignment resets the list */
5096 temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
5097 c->temporary_filesystems = NULL;
5098 c->n_temporary_filesystems = 0;
5099 return 0;
5100 }
5101
5102 for (const char *p = rvalue;;) {
5103 _cleanup_free_ char *word = NULL, *path = NULL, *resolved = NULL;
5104 const char *w;
5105
5106 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
5107 if (r == -ENOMEM)
5108 return log_oom();
5109 if (r < 0) {
5110 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
5111 return 0;
5112 }
5113 if (r == 0)
5114 return 0;
5115
5116 w = word;
5117 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
5118 if (r == -ENOMEM)
5119 return log_oom();
5120 if (r < 0) {
5121 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", word);
5122 continue;
5123 }
5124 if (r == 0) {
5125 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid syntax, ignoring: %s", word);
5126 continue;
5127 }
5128
5129 r = unit_path_printf(u, path, &resolved);
5130 if (r < 0) {
5131 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", path);
5132 continue;
5133 }
5134
5135 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5136 if (r < 0)
5137 continue;
5138
5139 r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, resolved, w);
5140 if (r < 0)
5141 return log_oom();
5142 }
5143 }
5144
5145 int config_parse_bind_paths(
5146 const char *unit,
5147 const char *filename,
5148 unsigned line,
5149 const char *section,
5150 unsigned section_line,
5151 const char *lvalue,
5152 int ltype,
5153 const char *rvalue,
5154 void *data,
5155 void *userdata) {
5156
5157 ExecContext *c = data;
5158 const Unit *u = userdata;
5159 int r;
5160
5161 assert(filename);
5162 assert(lvalue);
5163 assert(rvalue);
5164 assert(data);
5165
5166 if (isempty(rvalue)) {
5167 /* Empty assignment resets the list */
5168 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
5169 c->bind_mounts = NULL;
5170 c->n_bind_mounts = 0;
5171 return 0;
5172 }
5173
5174 for (const char *p = rvalue;;) {
5175 _cleanup_free_ char *source = NULL, *destination = NULL;
5176 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
5177 char *s = NULL, *d = NULL;
5178 bool rbind = true, ignore_enoent = false;
5179
5180 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
5181 if (r == -ENOMEM)
5182 return log_oom();
5183 if (r < 0) {
5184 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
5185 return 0;
5186 }
5187 if (r == 0)
5188 break;
5189
5190 r = unit_full_printf_full(u, source, PATH_MAX, &sresolved);
5191 if (r < 0) {
5192 log_syntax(unit, LOG_WARNING, filename, line, r,
5193 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", source);
5194 continue;
5195 }
5196
5197 s = sresolved;
5198 if (s[0] == '-') {
5199 ignore_enoent = true;
5200 s++;
5201 }
5202
5203 r = path_simplify_and_warn(s, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5204 if (r < 0)
5205 continue;
5206
5207 /* Optionally, the destination is specified. */
5208 if (p && p[-1] == ':') {
5209 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
5210 if (r == -ENOMEM)
5211 return log_oom();
5212 if (r < 0) {
5213 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
5214 return 0;
5215 }
5216 if (r == 0) {
5217 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing argument after ':', ignoring: %s", s);
5218 continue;
5219 }
5220
5221 r = unit_path_printf(u, destination, &dresolved);
5222 if (r < 0) {
5223 log_syntax(unit, LOG_WARNING, filename, line, r,
5224 "Failed to resolve specifiers in \"%s\", ignoring: %m", destination);
5225 continue;
5226 }
5227
5228 r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5229 if (r < 0)
5230 continue;
5231
5232 d = dresolved;
5233
5234 /* Optionally, there's also a short option string specified */
5235 if (p && p[-1] == ':') {
5236 _cleanup_free_ char *options = NULL;
5237
5238 r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
5239 if (r == -ENOMEM)
5240 return log_oom();
5241 if (r < 0) {
5242 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
5243 return 0;
5244 }
5245
5246 if (isempty(options) || streq(options, "rbind"))
5247 rbind = true;
5248 else if (streq(options, "norbind"))
5249 rbind = false;
5250 else {
5251 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
5252 continue;
5253 }
5254 }
5255 } else
5256 d = s;
5257
5258 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
5259 &(BindMount) {
5260 .source = s,
5261 .destination = d,
5262 .read_only = !!strstr(lvalue, "ReadOnly"),
5263 .recursive = rbind,
5264 .ignore_enoent = ignore_enoent,
5265 });
5266 if (r < 0)
5267 return log_oom();
5268 }
5269
5270 return 0;
5271 }
5272
5273 int config_parse_mount_images(
5274 const char *unit,
5275 const char *filename,
5276 unsigned line,
5277 const char *section,
5278 unsigned section_line,
5279 const char *lvalue,
5280 int ltype,
5281 const char *rvalue,
5282 void *data,
5283 void *userdata) {
5284
5285 ExecContext *c = data;
5286 const Unit *u = userdata;
5287 int r;
5288
5289 assert(filename);
5290 assert(lvalue);
5291 assert(rvalue);
5292 assert(data);
5293
5294 if (isempty(rvalue)) {
5295 /* Empty assignment resets the list */
5296 c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
5297 return 0;
5298 }
5299
5300 for (const char *p = rvalue;;) {
5301 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
5302 _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
5303 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
5304 const char *q = NULL;
5305 char *s = NULL;
5306 bool permissive = false;
5307
5308 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
5309 if (r == -ENOMEM)
5310 return log_oom();
5311 if (r < 0) {
5312 log_syntax(unit, LOG_WARNING, filename, line, r,
5313 "Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
5314 return 0;
5315 }
5316 if (r == 0)
5317 return 0;
5318
5319 q = tuple;
5320 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
5321 if (r == -ENOMEM)
5322 return log_oom();
5323 if (r < 0) {
5324 log_syntax(unit, LOG_WARNING, filename, line, r,
5325 "Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
5326 return 0;
5327 }
5328 if (r == 0)
5329 continue;
5330
5331 s = first;
5332 if (s[0] == '-') {
5333 permissive = true;
5334 s++;
5335 }
5336
5337 r = unit_path_printf(u, s, &sresolved);
5338 if (r < 0) {
5339 log_syntax(unit, LOG_WARNING, filename, line, r,
5340 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", s);
5341 continue;
5342 }
5343
5344 r = path_simplify_and_warn(sresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5345 if (r < 0)
5346 continue;
5347
5348 if (isempty(second)) {
5349 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing destination in %s, ignoring: %s", lvalue, rvalue);
5350 continue;
5351 }
5352
5353 r = unit_path_printf(u, second, &dresolved);
5354 if (r < 0) {
5355 log_syntax(unit, LOG_WARNING, filename, line, r,
5356 "Failed to resolve specifiers in \"%s\", ignoring: %m", second);
5357 continue;
5358 }
5359
5360 r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5361 if (r < 0)
5362 continue;
5363
5364 for (;;) {
5365 _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
5366 MountOptions *o = NULL;
5367 PartitionDesignator partition_designator;
5368
5369 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
5370 if (r == -ENOMEM)
5371 return log_oom();
5372 if (r < 0) {
5373 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", q);
5374 return 0;
5375 }
5376 if (r == 0)
5377 break;
5378 /* Single set of options, applying to the root partition/single filesystem */
5379 if (r == 1) {
5380 r = unit_full_printf(u, partition, &mount_options_resolved);
5381 if (r < 0) {
5382 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", first);
5383 continue;
5384 }
5385
5386 o = new(MountOptions, 1);
5387 if (!o)
5388 return log_oom();
5389 *o = (MountOptions) {
5390 .partition_designator = PARTITION_ROOT,
5391 .options = TAKE_PTR(mount_options_resolved),
5392 };
5393 LIST_APPEND(mount_options, options, o);
5394
5395 break;
5396 }
5397
5398 partition_designator = partition_designator_from_string(partition);
5399 if (partition_designator < 0) {
5400 log_syntax(unit, LOG_WARNING, filename, line, partition_designator,
5401 "Invalid partition name %s, ignoring", partition);
5402 continue;
5403 }
5404 r = unit_full_printf(u, mount_options, &mount_options_resolved);
5405 if (r < 0) {
5406 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
5407 continue;
5408 }
5409
5410 o = new(MountOptions, 1);
5411 if (!o)
5412 return log_oom();
5413 *o = (MountOptions) {
5414 .partition_designator = partition_designator,
5415 .options = TAKE_PTR(mount_options_resolved),
5416 };
5417 LIST_APPEND(mount_options, options, o);
5418 }
5419
5420 r = mount_image_add(&c->mount_images, &c->n_mount_images,
5421 &(MountImage) {
5422 .source = sresolved,
5423 .destination = dresolved,
5424 .mount_options = options,
5425 .ignore_enoent = permissive,
5426 .type = MOUNT_IMAGE_DISCRETE,
5427 });
5428 if (r < 0)
5429 return log_oom();
5430 }
5431 }
5432
5433 int config_parse_extension_images(
5434 const char *unit,
5435 const char *filename,
5436 unsigned line,
5437 const char *section,
5438 unsigned section_line,
5439 const char *lvalue,
5440 int ltype,
5441 const char *rvalue,
5442 void *data,
5443 void *userdata) {
5444
5445 ExecContext *c = data;
5446 const Unit *u = userdata;
5447 int r;
5448
5449 assert(filename);
5450 assert(lvalue);
5451 assert(rvalue);
5452 assert(data);
5453
5454 if (isempty(rvalue)) {
5455 /* Empty assignment resets the list */
5456 c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
5457 return 0;
5458 }
5459
5460 for (const char *p = rvalue;;) {
5461 _cleanup_free_ char *source = NULL, *tuple = NULL, *sresolved = NULL;
5462 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
5463 bool permissive = false;
5464 const char *q = NULL;
5465 char *s = NULL;
5466
5467 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
5468 if (r == -ENOMEM)
5469 return log_oom();
5470 if (r < 0) {
5471 log_syntax(unit, LOG_WARNING, filename, line, r,
5472 "Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
5473 return 0;
5474 }
5475 if (r == 0)
5476 return 0;
5477
5478 q = tuple;
5479 r = extract_first_word(&q, &source, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS);
5480 if (r == -ENOMEM)
5481 return log_oom();
5482 if (r < 0) {
5483 log_syntax(unit, LOG_WARNING, filename, line, r,
5484 "Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
5485 return 0;
5486 }
5487 if (r == 0)
5488 continue;
5489
5490 s = source;
5491 if (s[0] == '-') {
5492 permissive = true;
5493 s++;
5494 }
5495
5496 r = unit_path_printf(u, s, &sresolved);
5497 if (r < 0) {
5498 log_syntax(unit, LOG_WARNING, filename, line, r,
5499 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", s);
5500 continue;
5501 }
5502
5503 r = path_simplify_and_warn(sresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5504 if (r < 0)
5505 continue;
5506
5507 for (;;) {
5508 _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
5509 MountOptions *o = NULL;
5510 PartitionDesignator partition_designator;
5511
5512 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
5513 if (r == -ENOMEM)
5514 return log_oom();
5515 if (r < 0) {
5516 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", q);
5517 return 0;
5518 }
5519 if (r == 0)
5520 break;
5521 /* Single set of options, applying to the root partition/single filesystem */
5522 if (r == 1) {
5523 r = unit_full_printf(u, partition, &mount_options_resolved);
5524 if (r < 0) {
5525 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", partition);
5526 continue;
5527 }
5528
5529 o = new(MountOptions, 1);
5530 if (!o)
5531 return log_oom();
5532 *o = (MountOptions) {
5533 .partition_designator = PARTITION_ROOT,
5534 .options = TAKE_PTR(mount_options_resolved),
5535 };
5536 LIST_APPEND(mount_options, options, o);
5537
5538 break;
5539 }
5540
5541 partition_designator = partition_designator_from_string(partition);
5542 if (partition_designator < 0) {
5543 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid partition name %s, ignoring", partition);
5544 continue;
5545 }
5546 r = unit_full_printf(u, mount_options, &mount_options_resolved);
5547 if (r < 0) {
5548 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
5549 continue;
5550 }
5551
5552 o = new(MountOptions, 1);
5553 if (!o)
5554 return log_oom();
5555 *o = (MountOptions) {
5556 .partition_designator = partition_designator,
5557 .options = TAKE_PTR(mount_options_resolved),
5558 };
5559 LIST_APPEND(mount_options, options, o);
5560 }
5561
5562 r = mount_image_add(&c->extension_images, &c->n_extension_images,
5563 &(MountImage) {
5564 .source = sresolved,
5565 .mount_options = options,
5566 .ignore_enoent = permissive,
5567 .type = MOUNT_IMAGE_EXTENSION,
5568 });
5569 if (r < 0)
5570 return log_oom();
5571 }
5572 }
5573
5574 int config_parse_job_timeout_sec(
5575 const char* unit,
5576 const char *filename,
5577 unsigned line,
5578 const char *section,
5579 unsigned section_line,
5580 const char *lvalue,
5581 int ltype,
5582 const char *rvalue,
5583 void *data,
5584 void *userdata) {
5585
5586 Unit *u = data;
5587 usec_t usec;
5588 int r;
5589
5590 assert(filename);
5591 assert(lvalue);
5592 assert(rvalue);
5593 assert(u);
5594
5595 r = parse_sec_fix_0(rvalue, &usec);
5596 if (r < 0) {
5597 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue);
5598 return 0;
5599 }
5600
5601 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
5602 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
5603 * count. */
5604
5605 if (!u->job_running_timeout_set)
5606 u->job_running_timeout = usec;
5607
5608 u->job_timeout = usec;
5609
5610 return 0;
5611 }
5612
5613 int config_parse_job_running_timeout_sec(
5614 const char* unit,
5615 const char *filename,
5616 unsigned line,
5617 const char *section,
5618 unsigned section_line,
5619 const char *lvalue,
5620 int ltype,
5621 const char *rvalue,
5622 void *data,
5623 void *userdata) {
5624
5625 Unit *u = data;
5626 usec_t usec;
5627 int r;
5628
5629 assert(filename);
5630 assert(lvalue);
5631 assert(rvalue);
5632 assert(u);
5633
5634 r = parse_sec_fix_0(rvalue, &usec);
5635 if (r < 0) {
5636 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue);
5637 return 0;
5638 }
5639
5640 u->job_running_timeout = usec;
5641 u->job_running_timeout_set = true;
5642
5643 return 0;
5644 }
5645
5646 int config_parse_emergency_action(
5647 const char* unit,
5648 const char *filename,
5649 unsigned line,
5650 const char *section,
5651 unsigned section_line,
5652 const char *lvalue,
5653 int ltype,
5654 const char *rvalue,
5655 void *data,
5656 void *userdata) {
5657
5658 Manager *m = NULL;
5659 EmergencyAction *x = data;
5660 int r;
5661
5662 assert(filename);
5663 assert(lvalue);
5664 assert(rvalue);
5665 assert(data);
5666
5667 if (unit)
5668 m = ((Unit*) userdata)->manager;
5669 else
5670 m = data;
5671
5672 r = parse_emergency_action(rvalue, MANAGER_IS_SYSTEM(m), x);
5673 if (r < 0) {
5674 if (r == -EOPNOTSUPP && MANAGER_IS_USER(m)) {
5675 /* Compat mode: remove for systemd 241. */
5676
5677 log_syntax(unit, LOG_INFO, filename, line, r,
5678 "%s= in user mode specified as \"%s\", using \"exit-force\" instead.",
5679 lvalue, rvalue);
5680 *x = EMERGENCY_ACTION_EXIT_FORCE;
5681 return 0;
5682 }
5683
5684 if (r == -EOPNOTSUPP)
5685 log_syntax(unit, LOG_WARNING, filename, line, r,
5686 "%s= specified as %s mode action, ignoring: %s",
5687 lvalue, MANAGER_IS_SYSTEM(m) ? "user" : "system", rvalue);
5688 else
5689 log_syntax(unit, LOG_WARNING, filename, line, r,
5690 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
5691 return 0;
5692 }
5693
5694 return 0;
5695 }
5696
5697 int config_parse_pid_file(
5698 const char *unit,
5699 const char *filename,
5700 unsigned line,
5701 const char *section,
5702 unsigned section_line,
5703 const char *lvalue,
5704 int ltype,
5705 const char *rvalue,
5706 void *data,
5707 void *userdata) {
5708
5709 _cleanup_free_ char *k = NULL, *n = NULL;
5710 const Unit *u = userdata;
5711 char **s = data;
5712 int r;
5713
5714 assert(filename);
5715 assert(lvalue);
5716 assert(rvalue);
5717 assert(u);
5718
5719 if (isempty(rvalue)) {
5720 /* An empty assignment removes already set value. */
5721 *s = mfree(*s);
5722 return 0;
5723 }
5724
5725 r = unit_path_printf(u, rvalue, &k);
5726 if (r < 0) {
5727 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
5728 return 0;
5729 }
5730
5731 /* If this is a relative path make it absolute by prefixing the /run */
5732 n = path_make_absolute(k, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
5733 if (!n)
5734 return log_oom();
5735
5736 /* Check that the result is a sensible path */
5737 r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5738 if (r < 0)
5739 return r;
5740
5741 r = patch_var_run(unit, filename, line, lvalue, &n);
5742 if (r < 0)
5743 return r;
5744
5745 free_and_replace(*s, n);
5746 return 0;
5747 }
5748
5749 int config_parse_exit_status(
5750 const char *unit,
5751 const char *filename,
5752 unsigned line,
5753 const char *section,
5754 unsigned section_line,
5755 const char *lvalue,
5756 int ltype,
5757 const char *rvalue,
5758 void *data,
5759 void *userdata) {
5760
5761 int *exit_status = data, r;
5762 uint8_t u;
5763
5764 assert(filename);
5765 assert(lvalue);
5766 assert(rvalue);
5767 assert(exit_status);
5768
5769 if (isempty(rvalue)) {
5770 *exit_status = -1;
5771 return 0;
5772 }
5773
5774 r = safe_atou8(rvalue, &u);
5775 if (r < 0) {
5776 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse exit status '%s', ignoring: %m", rvalue);
5777 return 0;
5778 }
5779
5780 *exit_status = u;
5781 return 0;
5782 }
5783
5784 int config_parse_disable_controllers(
5785 const char *unit,
5786 const char *filename,
5787 unsigned line,
5788 const char *section,
5789 unsigned section_line,
5790 const char *lvalue,
5791 int ltype,
5792 const char *rvalue,
5793 void *data,
5794 void *userdata) {
5795
5796 int r;
5797 CGroupContext *c = data;
5798 CGroupMask disabled_mask;
5799
5800 /* 1. If empty, make all controllers eligible for use again.
5801 * 2. If non-empty, merge all listed controllers, space separated. */
5802
5803 if (isempty(rvalue)) {
5804 c->disable_controllers = 0;
5805 return 0;
5806 }
5807
5808 r = cg_mask_from_string(rvalue, &disabled_mask);
5809 if (r < 0 || disabled_mask <= 0) {
5810 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid cgroup string: %s, ignoring", rvalue);
5811 return 0;
5812 }
5813
5814 c->disable_controllers |= disabled_mask;
5815
5816 return 0;
5817 }
5818
5819 int config_parse_ip_filter_bpf_progs(
5820 const char *unit,
5821 const char *filename,
5822 unsigned line,
5823 const char *section,
5824 unsigned section_line,
5825 const char *lvalue,
5826 int ltype,
5827 const char *rvalue,
5828 void *data,
5829 void *userdata) {
5830
5831 _cleanup_free_ char *resolved = NULL;
5832 const Unit *u = userdata;
5833 char ***paths = data;
5834 int r;
5835
5836 assert(filename);
5837 assert(lvalue);
5838 assert(rvalue);
5839 assert(paths);
5840
5841 if (isempty(rvalue)) {
5842 *paths = strv_free(*paths);
5843 return 0;
5844 }
5845
5846 r = unit_path_printf(u, rvalue, &resolved);
5847 if (r < 0) {
5848 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
5849 return 0;
5850 }
5851
5852 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5853 if (r < 0)
5854 return 0;
5855
5856 if (strv_contains(*paths, resolved))
5857 return 0;
5858
5859 r = strv_extend(paths, resolved);
5860 if (r < 0)
5861 return log_oom();
5862
5863 r = bpf_firewall_supported();
5864 if (r < 0)
5865 return r;
5866 if (r != BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
5867 static bool warned = false;
5868
5869 log_full(warned ? LOG_DEBUG : LOG_WARNING,
5870 "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"
5871 "Starting this unit will fail! (This warning is only shown for the first loaded unit using IP firewalling.)", filename, line, lvalue, rvalue);
5872
5873 warned = true;
5874 }
5875
5876 return 0;
5877 }
5878
5879 int config_parse_bpf_foreign_program(
5880 const char *unit,
5881 const char *filename,
5882 unsigned line,
5883 const char *section,
5884 unsigned section_line,
5885 const char *lvalue,
5886 int ltype,
5887 const char *rvalue,
5888 void *data,
5889 void *userdata) {
5890 _cleanup_free_ char *resolved = NULL, *word = NULL;
5891 CGroupContext *c = data;
5892 const char *p = rvalue;
5893 Unit *u = userdata;
5894 int attach_type, r;
5895
5896 assert(filename);
5897 assert(lvalue);
5898 assert(rvalue);
5899
5900 if (isempty(rvalue)) {
5901 while (c->bpf_foreign_programs)
5902 cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
5903
5904 return 0;
5905 }
5906
5907 r = extract_first_word(&p, &word, ":", 0);
5908 if (r == -ENOMEM)
5909 return log_oom();
5910 if (r < 0) {
5911 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse foreign BPF program, ignoring: %s", rvalue);
5912 return 0;
5913 }
5914 if (r == 0 || isempty(p)) {
5915 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid syntax in %s=, ignoring: %s", lvalue, rvalue);
5916 return 0;
5917 }
5918
5919 attach_type = bpf_cgroup_attach_type_from_string(word);
5920 if (attach_type < 0) {
5921 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown BPF attach type=%s, ignoring: %s", word, rvalue);
5922 return 0;
5923 }
5924
5925 r = unit_path_printf(u, p, &resolved);
5926 if (r < 0) {
5927 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %s", p, rvalue);
5928 return 0;
5929 }
5930
5931 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5932 if (r < 0)
5933 return 0;
5934
5935 r = cgroup_add_bpf_foreign_program(c, attach_type, resolved);
5936 if (r < 0)
5937 return log_error_errno(r, "Failed to add foreign BPF program to cgroup context: %m");
5938
5939 return 0;
5940 }
5941
5942 int config_parse_cgroup_socket_bind(
5943 const char *unit,
5944 const char *filename,
5945 unsigned line,
5946 const char *section,
5947 unsigned section_line,
5948 const char *lvalue,
5949 int ltype,
5950 const char *rvalue,
5951 void *data,
5952 void *userdata) {
5953 _cleanup_free_ CGroupSocketBindItem *item = NULL;
5954 CGroupSocketBindItem **head = data;
5955 uint16_t nr_ports, port_min;
5956 int af, ip_protocol, r;
5957
5958 if (isempty(rvalue)) {
5959 cgroup_context_remove_socket_bind(head);
5960 return 0;
5961 }
5962
5963 r = parse_socket_bind_item(rvalue, &af, &ip_protocol, &nr_ports, &port_min);
5964 if (r == -ENOMEM)
5965 return log_oom();
5966 if (r < 0) {
5967 log_syntax(unit, LOG_WARNING, filename, line, r,
5968 "Unable to parse %s= assignment, ignoring: %s", lvalue, rvalue);
5969 return 0;
5970 }
5971
5972 item = new(CGroupSocketBindItem, 1);
5973 if (!item)
5974 return log_oom();
5975 *item = (CGroupSocketBindItem) {
5976 .address_family = af,
5977 .ip_protocol = ip_protocol,
5978 .nr_ports = nr_ports,
5979 .port_min = port_min,
5980 };
5981
5982 LIST_PREPEND(socket_bind_items, *head, TAKE_PTR(item));
5983
5984 return 0;
5985 }
5986
5987 int config_parse_restrict_network_interfaces(
5988 const char *unit,
5989 const char *filename,
5990 unsigned line,
5991 const char *section,
5992 unsigned section_line,
5993 const char *lvalue,
5994 int ltype,
5995 const char *rvalue,
5996 void *data,
5997 void *userdata) {
5998 CGroupContext *c = data;
5999 bool is_allow_rule = true;
6000 int r;
6001
6002 assert(filename);
6003 assert(lvalue);
6004 assert(rvalue);
6005 assert(data);
6006
6007 if (isempty(rvalue)) {
6008 /* Empty assignment resets the list */
6009 c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
6010 return 0;
6011 }
6012
6013 if (rvalue[0] == '~') {
6014 is_allow_rule = false;
6015 rvalue++;
6016 }
6017
6018 if (set_isempty(c->restrict_network_interfaces))
6019 /* Only initialize this when creating the set */
6020 c->restrict_network_interfaces_is_allow_list = is_allow_rule;
6021
6022 for (const char *p = rvalue;;) {
6023 _cleanup_free_ char *word = NULL;
6024
6025 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
6026 if (r == 0)
6027 break;
6028 if (r == -ENOMEM)
6029 return log_oom();
6030 if (r < 0) {
6031 log_syntax(unit, LOG_WARNING, filename, line, r,
6032 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
6033 break;
6034 }
6035
6036 if (!ifname_valid(word)) {
6037 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", word);
6038 continue;
6039 }
6040
6041 if (c->restrict_network_interfaces_is_allow_list != is_allow_rule)
6042 free(set_remove(c->restrict_network_interfaces, word));
6043 else {
6044 r = set_put_strdup(&c->restrict_network_interfaces, word);
6045 if (r < 0)
6046 return log_oom();
6047 }
6048 }
6049
6050 return 0;
6051 }
6052
6053 static int merge_by_names(Unit **u, Set *names, const char *id) {
6054 char *k;
6055 int r;
6056
6057 assert(u);
6058 assert(*u);
6059
6060 /* Let's try to add in all names that are aliases of this unit */
6061 while ((k = set_steal_first(names))) {
6062 _cleanup_free_ _unused_ char *free_k = k;
6063
6064 /* First try to merge in the other name into our unit */
6065 r = unit_merge_by_name(*u, k);
6066 if (r < 0) {
6067 Unit *other;
6068
6069 /* Hmm, we couldn't merge the other unit into ours? Then let's try it the other way
6070 * round. */
6071
6072 other = manager_get_unit((*u)->manager, k);
6073 if (!other)
6074 return r; /* return previous failure */
6075
6076 r = unit_merge(other, *u);
6077 if (r < 0)
6078 return r;
6079
6080 *u = other;
6081 return merge_by_names(u, names, NULL);
6082 }
6083
6084 if (streq_ptr(id, k))
6085 unit_choose_id(*u, id);
6086 }
6087
6088 return 0;
6089 }
6090
6091 int unit_load_fragment(Unit *u) {
6092 const char *fragment;
6093 _cleanup_set_free_free_ Set *names = NULL;
6094 int r;
6095
6096 assert(u);
6097 assert(u->load_state == UNIT_STUB);
6098 assert(u->id);
6099
6100 if (u->transient) {
6101 u->access_selinux_context = mfree(u->access_selinux_context);
6102 u->load_state = UNIT_LOADED;
6103 return 0;
6104 }
6105
6106 /* Possibly rebuild the fragment map to catch new units */
6107 r = unit_file_build_name_map(&u->manager->lookup_paths,
6108 &u->manager->unit_cache_timestamp_hash,
6109 &u->manager->unit_id_map,
6110 &u->manager->unit_name_map,
6111 &u->manager->unit_path_cache);
6112 if (r < 0)
6113 return log_error_errno(r, "Failed to rebuild name map: %m");
6114
6115 r = unit_file_find_fragment(u->manager->unit_id_map,
6116 u->manager->unit_name_map,
6117 u->id,
6118 &fragment,
6119 &names);
6120 if (r < 0 && r != -ENOENT)
6121 return r;
6122
6123 if (fragment) {
6124 /* Open the file, check if this is a mask, otherwise read. */
6125 _cleanup_fclose_ FILE *f = NULL;
6126 struct stat st;
6127
6128 /* Try to open the file name. A symlink is OK, for example for linked files or masks. We
6129 * expect that all symlinks within the lookup paths have been already resolved, but we don't
6130 * verify this here. */
6131 f = fopen(fragment, "re");
6132 if (!f)
6133 return log_unit_notice_errno(u, errno, "Failed to open %s: %m", fragment);
6134
6135 if (fstat(fileno(f), &st) < 0)
6136 return -errno;
6137
6138 r = free_and_strdup(&u->fragment_path, fragment);
6139 if (r < 0)
6140 return r;
6141
6142 if (null_or_empty(&st)) {
6143 /* Unit file is masked */
6144
6145 u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
6146 u->fragment_mtime = 0;
6147 u->access_selinux_context = mfree(u->access_selinux_context);
6148 } else {
6149 #if HAVE_SELINUX
6150 if (mac_selinux_use()) {
6151 _cleanup_freecon_ char *selcon = NULL;
6152
6153 /* Cache the SELinux context of the unit file here. We'll make use of when checking access permissions to loaded units */
6154 r = fgetfilecon_raw(fileno(f), &selcon);
6155 if (r < 0)
6156 log_unit_warning_errno(u, r, "Failed to read SELinux context of '%s', ignoring: %m", fragment);
6157
6158 r = free_and_strdup(&u->access_selinux_context, selcon);
6159 if (r < 0)
6160 return r;
6161 } else
6162 #endif
6163 u->access_selinux_context = mfree(u->access_selinux_context);
6164
6165 u->load_state = UNIT_LOADED;
6166 u->fragment_mtime = timespec_load(&st.st_mtim);
6167
6168 /* Now, parse the file contents */
6169 r = config_parse(u->id, fragment, f,
6170 UNIT_VTABLE(u)->sections,
6171 config_item_perf_lookup, load_fragment_gperf_lookup,
6172 0,
6173 u,
6174 NULL);
6175 if (r == -ENOEXEC)
6176 log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
6177 if (r < 0)
6178 return r;
6179 }
6180 }
6181
6182 /* Call merge_by_names with the name derived from the fragment path as the preferred name.
6183 *
6184 * We do the merge dance here because for some unit types, the unit might have aliases which are not
6185 * declared in the file system. In particular, this is true (and frequent) for device and swap units.
6186 */
6187 const char *id = u->id;
6188 _cleanup_free_ char *free_id = NULL;
6189
6190 if (fragment) {
6191 id = basename(fragment);
6192 if (unit_name_is_valid(id, UNIT_NAME_TEMPLATE)) {
6193 assert(u->instance); /* If we're not trying to use a template for non-instanced unit,
6194 * this must be set. */
6195
6196 r = unit_name_replace_instance(id, u->instance, &free_id);
6197 if (r < 0)
6198 return log_debug_errno(r, "Failed to build id (%s + %s): %m", id, u->instance);
6199 id = free_id;
6200 }
6201 }
6202
6203 Unit *merged = u;
6204 r = merge_by_names(&merged, names, id);
6205 if (r < 0)
6206 return r;
6207
6208 if (merged != u)
6209 u->load_state = UNIT_MERGED;
6210
6211 return 0;
6212 }
6213
6214 void unit_dump_config_items(FILE *f) {
6215 static const struct {
6216 const ConfigParserCallback callback;
6217 const char *rvalue;
6218 } table[] = {
6219 { config_parse_warn_compat, "NOTSUPPORTED" },
6220 { config_parse_int, "INTEGER" },
6221 { config_parse_unsigned, "UNSIGNED" },
6222 { config_parse_iec_size, "SIZE" },
6223 { config_parse_iec_uint64, "SIZE" },
6224 { config_parse_si_uint64, "SIZE" },
6225 { config_parse_bool, "BOOLEAN" },
6226 { config_parse_string, "STRING" },
6227 { config_parse_path, "PATH" },
6228 { config_parse_unit_path_printf, "PATH" },
6229 { config_parse_colon_separated_paths, "PATH" },
6230 { config_parse_strv, "STRING [...]" },
6231 { config_parse_exec_nice, "NICE" },
6232 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
6233 { config_parse_exec_io_class, "IOCLASS" },
6234 { config_parse_exec_io_priority, "IOPRIORITY" },
6235 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
6236 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
6237 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
6238 { config_parse_mode, "MODE" },
6239 { config_parse_unit_env_file, "FILE" },
6240 { config_parse_exec_output, "OUTPUT" },
6241 { config_parse_exec_input, "INPUT" },
6242 { config_parse_log_facility, "FACILITY" },
6243 { config_parse_log_level, "LEVEL" },
6244 { config_parse_exec_secure_bits, "SECUREBITS" },
6245 { config_parse_capability_set, "BOUNDINGSET" },
6246 { config_parse_rlimit, "LIMIT" },
6247 { config_parse_unit_deps, "UNIT [...]" },
6248 { config_parse_exec, "PATH [ARGUMENT [...]]" },
6249 { config_parse_service_type, "SERVICETYPE" },
6250 { config_parse_service_exit_type, "SERVICEEXITTYPE" },
6251 { config_parse_service_restart, "SERVICERESTART" },
6252 { config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
6253 { config_parse_kill_mode, "KILLMODE" },
6254 { config_parse_signal, "SIGNAL" },
6255 { config_parse_socket_listen, "SOCKET [...]" },
6256 { config_parse_socket_bind, "SOCKETBIND" },
6257 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
6258 { config_parse_sec, "SECONDS" },
6259 { config_parse_nsec, "NANOSECONDS" },
6260 { config_parse_namespace_path_strv, "PATH [...]" },
6261 { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
6262 { config_parse_unit_requires_mounts_for, "PATH [...]" },
6263 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
6264 { config_parse_unit_string_printf, "STRING" },
6265 { config_parse_trigger_unit, "UNIT" },
6266 { config_parse_timer, "TIMER" },
6267 { config_parse_path_spec, "PATH" },
6268 { config_parse_notify_access, "ACCESS" },
6269 { config_parse_ip_tos, "TOS" },
6270 { config_parse_unit_condition_path, "CONDITION" },
6271 { config_parse_unit_condition_string, "CONDITION" },
6272 { config_parse_unit_slice, "SLICE" },
6273 { config_parse_documentation, "URL" },
6274 { config_parse_service_timeout, "SECONDS" },
6275 { config_parse_emergency_action, "ACTION" },
6276 { config_parse_set_status, "STATUS" },
6277 { config_parse_service_sockets, "SOCKETS" },
6278 { config_parse_environ, "ENVIRON" },
6279 #if HAVE_SECCOMP
6280 { config_parse_syscall_filter, "SYSCALLS" },
6281 { config_parse_syscall_archs, "ARCHS" },
6282 { config_parse_syscall_errno, "ERRNO" },
6283 { config_parse_syscall_log, "SYSCALLS" },
6284 { config_parse_address_families, "FAMILIES" },
6285 { config_parse_restrict_namespaces, "NAMESPACES" },
6286 #endif
6287 { config_parse_restrict_filesystems, "FILESYSTEMS" },
6288 { config_parse_cpu_shares, "SHARES" },
6289 { config_parse_cg_weight, "WEIGHT" },
6290 { config_parse_cg_cpu_weight, "CPUWEIGHT" },
6291 { config_parse_memory_limit, "LIMIT" },
6292 { config_parse_device_allow, "DEVICE" },
6293 { config_parse_device_policy, "POLICY" },
6294 { config_parse_io_limit, "LIMIT" },
6295 { config_parse_io_device_weight, "DEVICEWEIGHT" },
6296 { config_parse_io_device_latency, "DEVICELATENCY" },
6297 { config_parse_blockio_bandwidth, "BANDWIDTH" },
6298 { config_parse_blockio_weight, "WEIGHT" },
6299 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
6300 { config_parse_long, "LONG" },
6301 { config_parse_socket_service, "SERVICE" },
6302 #if HAVE_SELINUX
6303 { config_parse_exec_selinux_context, "LABEL" },
6304 #endif
6305 { config_parse_job_mode, "MODE" },
6306 { config_parse_job_mode_isolate, "BOOLEAN" },
6307 { config_parse_personality, "PERSONALITY" },
6308 };
6309
6310 const char *prev = NULL;
6311 const char *i;
6312
6313 assert(f);
6314
6315 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
6316 const char *rvalue = "OTHER", *lvalue;
6317 const ConfigPerfItem *p;
6318 const char *dot;
6319
6320 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
6321
6322 /* Hide legacy settings */
6323 if (p->parse == config_parse_warn_compat &&
6324 p->ltype == DISABLED_LEGACY)
6325 continue;
6326
6327 for (size_t j = 0; j < ELEMENTSOF(table); j++)
6328 if (p->parse == table[j].callback) {
6329 rvalue = table[j].rvalue;
6330 break;
6331 }
6332
6333 dot = strchr(i, '.');
6334 lvalue = dot ? dot + 1 : i;
6335
6336 if (dot) {
6337 size_t prefix_len = dot - i;
6338
6339 if (!prev || !strneq(prev, i, prefix_len+1)) {
6340 if (prev)
6341 fputc('\n', f);
6342
6343 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
6344 }
6345 }
6346
6347 fprintf(f, "%s=%s\n", lvalue, rvalue);
6348 prev = i;
6349 }
6350 }
6351
6352 int config_parse_cpu_affinity2(
6353 const char *unit,
6354 const char *filename,
6355 unsigned line,
6356 const char *section,
6357 unsigned section_line,
6358 const char *lvalue,
6359 int ltype,
6360 const char *rvalue,
6361 void *data,
6362 void *userdata) {
6363
6364 CPUSet *affinity = data;
6365
6366 assert(affinity);
6367
6368 (void) parse_cpu_set_extend(rvalue, affinity, true, unit, filename, line, lvalue);
6369
6370 return 0;
6371 }
6372
6373 int config_parse_show_status(
6374 const char* unit,
6375 const char *filename,
6376 unsigned line,
6377 const char *section,
6378 unsigned section_line,
6379 const char *lvalue,
6380 int ltype,
6381 const char *rvalue,
6382 void *data,
6383 void *userdata) {
6384
6385 int k;
6386 ShowStatus *b = data;
6387
6388 assert(filename);
6389 assert(lvalue);
6390 assert(rvalue);
6391 assert(data);
6392
6393 k = parse_show_status(rvalue, b);
6394 if (k < 0)
6395 log_syntax(unit, LOG_WARNING, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
6396
6397 return 0;
6398 }
6399
6400 int config_parse_output_restricted(
6401 const char* unit,
6402 const char *filename,
6403 unsigned line,
6404 const char *section,
6405 unsigned section_line,
6406 const char *lvalue,
6407 int ltype,
6408 const char *rvalue,
6409 void *data,
6410 void *userdata) {
6411
6412 ExecOutput t, *eo = data;
6413 bool obsolete = false;
6414
6415 assert(filename);
6416 assert(lvalue);
6417 assert(rvalue);
6418 assert(data);
6419
6420 if (streq(rvalue, "syslog")) {
6421 t = EXEC_OUTPUT_JOURNAL;
6422 obsolete = true;
6423 } else if (streq(rvalue, "syslog+console")) {
6424 t = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
6425 obsolete = true;
6426 } else {
6427 t = exec_output_from_string(rvalue);
6428 if (t < 0) {
6429 log_syntax(unit, LOG_WARNING, filename, line, t, "Failed to parse output type, ignoring: %s", rvalue);
6430 return 0;
6431 }
6432
6433 if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND, EXEC_OUTPUT_FILE_TRUNCATE)) {
6434 log_syntax(unit, LOG_WARNING, filename, line, 0, "Standard output types socket, fd:, file:, append:, truncate: are not supported as defaults, ignoring: %s", rvalue);
6435 return 0;
6436 }
6437 }
6438
6439 if (obsolete)
6440 log_syntax(unit, LOG_NOTICE, filename, line, 0,
6441 "Standard output type %s is obsolete, automatically updating to %s. Please update your configuration.",
6442 rvalue, exec_output_to_string(t));
6443
6444 *eo = t;
6445 return 0;
6446 }
6447
6448 int config_parse_crash_chvt(
6449 const char* unit,
6450 const char *filename,
6451 unsigned line,
6452 const char *section,
6453 unsigned section_line,
6454 const char *lvalue,
6455 int ltype,
6456 const char *rvalue,
6457 void *data,
6458 void *userdata) {
6459
6460 int r;
6461
6462 assert(filename);
6463 assert(lvalue);
6464 assert(rvalue);
6465 assert(data);
6466
6467 r = parse_crash_chvt(rvalue, data);
6468 if (r < 0)
6469 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
6470
6471 return 0;
6472 }
6473
6474 int config_parse_swap_priority(
6475 const char *unit,
6476 const char *filename,
6477 unsigned line,
6478 const char *section,
6479 unsigned section_line,
6480 const char *lvalue,
6481 int ltype,
6482 const char *rvalue,
6483 void *data,
6484 void *userdata) {
6485
6486 Swap *s = userdata;
6487 int r, priority;
6488
6489 assert(s);
6490 assert(filename);
6491 assert(lvalue);
6492 assert(rvalue);
6493 assert(data);
6494
6495 if (isempty(rvalue)) {
6496 s->parameters_fragment.priority = -1;
6497 s->parameters_fragment.priority_set = false;
6498 return 0;
6499 }
6500
6501 r = safe_atoi(rvalue, &priority);
6502 if (r < 0) {
6503 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid swap priority '%s', ignoring.", rvalue);
6504 return 0;
6505 }
6506
6507 if (priority < -1) {
6508 log_syntax(unit, LOG_WARNING, filename, line, 0, "Sorry, swap priorities smaller than -1 may only be assigned by the kernel itself, ignoring: %s", rvalue);
6509 return 0;
6510 }
6511
6512 if (priority > 32767) {
6513 log_syntax(unit, LOG_WARNING, filename, line, 0, "Swap priority out of range, ignoring: %s", rvalue);
6514 return 0;
6515 }
6516
6517 s->parameters_fragment.priority = priority;
6518 s->parameters_fragment.priority_set = true;
6519 return 0;
6520 }
6521
6522 int config_parse_watchdog_sec(
6523 const char *unit,
6524 const char *filename,
6525 unsigned line,
6526 const char *section,
6527 unsigned section_line,
6528 const char *lvalue,
6529 int ltype,
6530 const char *rvalue,
6531 void *data,
6532 void *userdata) {
6533
6534 usec_t *usec = data;
6535
6536 assert(filename);
6537 assert(lvalue);
6538 assert(rvalue);
6539
6540 /* This is called for {Runtime,Reboot,KExec}WatchdogSec= where "default" maps to
6541 * USEC_INFINITY internally. */
6542
6543 if (streq(rvalue, "default"))
6544 *usec = USEC_INFINITY;
6545 else if (streq(rvalue, "off"))
6546 *usec = 0;
6547 else
6548 return config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
6549
6550 return 0;
6551 }
6552
6553 int config_parse_tty_size(
6554 const char *unit,
6555 const char *filename,
6556 unsigned line,
6557 const char *section,
6558 unsigned section_line,
6559 const char *lvalue,
6560 int ltype,
6561 const char *rvalue,
6562 void *data,
6563 void *userdata) {
6564
6565 unsigned *sz = data;
6566
6567 assert(filename);
6568 assert(lvalue);
6569 assert(rvalue);
6570
6571 if (isempty(rvalue)) {
6572 *sz = UINT_MAX;
6573 return 0;
6574 }
6575
6576 return config_parse_unsigned(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
6577 }