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