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