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