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