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