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