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