]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/load-fragment.c
Merge pull request #24742 from keszybz/hwdb-252
[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, min, max, 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 */
1615 min = sched_get_priority_min(c->cpu_sched_policy);
1616 max = sched_get_priority_max(c->cpu_sched_policy);
1617
1618 if (i < min || i > max) {
1619 log_syntax(unit, LOG_WARNING, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue);
1620 return 0;
1621 }
1622
1623 c->cpu_sched_priority = i;
1624 c->cpu_sched_set = true;
1625
1626 return 0;
1627 }
1628
1629 int config_parse_root_image_options(
1630 const char *unit,
1631 const char *filename,
1632 unsigned line,
1633 const char *section,
1634 unsigned section_line,
1635 const char *lvalue,
1636 int ltype,
1637 const char *rvalue,
1638 void *data,
1639 void *userdata) {
1640
1641 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
1642 _cleanup_strv_free_ char **l = NULL;
1643 ExecContext *c = ASSERT_PTR(data);
1644 const Unit *u = userdata;
1645 int r;
1646
1647 assert(filename);
1648 assert(lvalue);
1649 assert(rvalue);
1650
1651 if (isempty(rvalue)) {
1652 c->root_image_options = mount_options_free_all(c->root_image_options);
1653 return 0;
1654 }
1655
1656 r = strv_split_colon_pairs(&l, rvalue);
1657 if (r == -ENOMEM)
1658 return log_oom();
1659 if (r < 0) {
1660 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
1661 return 0;
1662 }
1663
1664 STRV_FOREACH_PAIR(first, second, l) {
1665 MountOptions *o = NULL;
1666 _cleanup_free_ char *mount_options_resolved = NULL;
1667 const char *mount_options = NULL, *partition = "root";
1668 PartitionDesignator partition_designator;
1669
1670 /* Format is either 'root:foo' or 'foo' (root is implied) */
1671 if (!isempty(*second)) {
1672 partition = *first;
1673 mount_options = *second;
1674 } else
1675 mount_options = *first;
1676
1677 partition_designator = partition_designator_from_string(partition);
1678 if (partition_designator < 0) {
1679 log_syntax(unit, LOG_WARNING, filename, line, partition_designator,
1680 "Invalid partition name %s, ignoring", partition);
1681 continue;
1682 }
1683 r = unit_full_printf(u, mount_options, &mount_options_resolved);
1684 if (r < 0) {
1685 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
1686 continue;
1687 }
1688
1689 o = new(MountOptions, 1);
1690 if (!o)
1691 return log_oom();
1692 *o = (MountOptions) {
1693 .partition_designator = partition_designator,
1694 .options = TAKE_PTR(mount_options_resolved),
1695 };
1696 LIST_APPEND(mount_options, options, TAKE_PTR(o));
1697 }
1698
1699 if (options)
1700 LIST_JOIN(mount_options, c->root_image_options, options);
1701 else
1702 /* empty spaces/separators only */
1703 c->root_image_options = mount_options_free_all(c->root_image_options);
1704
1705 return 0;
1706 }
1707
1708 int config_parse_exec_root_hash(
1709 const char *unit,
1710 const char *filename,
1711 unsigned line,
1712 const char *section,
1713 unsigned section_line,
1714 const char *lvalue,
1715 int ltype,
1716 const char *rvalue,
1717 void *data,
1718 void *userdata) {
1719
1720 _cleanup_free_ void *roothash_decoded = NULL;
1721 ExecContext *c = ASSERT_PTR(data);
1722 size_t roothash_decoded_size = 0;
1723 int r;
1724
1725 assert(filename);
1726 assert(line);
1727 assert(rvalue);
1728
1729 if (isempty(rvalue)) {
1730 /* Reset if the empty string is assigned */
1731 c->root_hash_path = mfree(c->root_hash_path);
1732 c->root_hash = mfree(c->root_hash);
1733 c->root_hash_size = 0;
1734 return 0;
1735 }
1736
1737 if (path_is_absolute(rvalue)) {
1738 /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1739 _cleanup_free_ char *p = NULL;
1740
1741 p = strdup(rvalue);
1742 if (!p)
1743 return -ENOMEM;
1744
1745 free_and_replace(c->root_hash_path, p);
1746 c->root_hash = mfree(c->root_hash);
1747 c->root_hash_size = 0;
1748 return 0;
1749 }
1750
1751 /* We have a roothash to decode, eg: RootHash=012345789abcdef */
1752 r = unhexmem(rvalue, strlen(rvalue), &roothash_decoded, &roothash_decoded_size);
1753 if (r < 0) {
1754 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode RootHash=, ignoring: %s", rvalue);
1755 return 0;
1756 }
1757 if (roothash_decoded_size < sizeof(sd_id128_t)) {
1758 log_syntax(unit, LOG_WARNING, filename, line, 0, "RootHash= is too short, ignoring: %s", rvalue);
1759 return 0;
1760 }
1761
1762 free_and_replace(c->root_hash, roothash_decoded);
1763 c->root_hash_size = roothash_decoded_size;
1764 c->root_hash_path = mfree(c->root_hash_path);
1765
1766 return 0;
1767 }
1768
1769 int config_parse_exec_root_hash_sig(
1770 const char *unit,
1771 const char *filename,
1772 unsigned line,
1773 const char *section,
1774 unsigned section_line,
1775 const char *lvalue,
1776 int ltype,
1777 const char *rvalue,
1778 void *data,
1779 void *userdata) {
1780
1781 _cleanup_free_ void *roothash_sig_decoded = NULL;
1782 char *value;
1783 ExecContext *c = ASSERT_PTR(data);
1784 size_t roothash_sig_decoded_size = 0;
1785 int r;
1786
1787 assert(filename);
1788 assert(line);
1789 assert(rvalue);
1790
1791 if (isempty(rvalue)) {
1792 /* Reset if the empty string is assigned */
1793 c->root_hash_sig_path = mfree(c->root_hash_sig_path);
1794 c->root_hash_sig = mfree(c->root_hash_sig);
1795 c->root_hash_sig_size = 0;
1796 return 0;
1797 }
1798
1799 if (path_is_absolute(rvalue)) {
1800 /* We have the path to a roothash signature to load and decode, eg: RootHashSignature=/foo/bar.roothash.p7s */
1801 _cleanup_free_ char *p = NULL;
1802
1803 p = strdup(rvalue);
1804 if (!p)
1805 return log_oom();
1806
1807 free_and_replace(c->root_hash_sig_path, p);
1808 c->root_hash_sig = mfree(c->root_hash_sig);
1809 c->root_hash_sig_size = 0;
1810 return 0;
1811 }
1812
1813 if (!(value = startswith(rvalue, "base64:"))) {
1814 log_syntax(unit, LOG_WARNING, filename, line, 0,
1815 "Failed to decode RootHashSignature=, not a path but doesn't start with 'base64:', ignoring: %s", rvalue);
1816 return 0;
1817 }
1818
1819 /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
1820 r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
1821 if (r < 0) {
1822 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode RootHashSignature=, ignoring: %s", rvalue);
1823 return 0;
1824 }
1825
1826 free_and_replace(c->root_hash_sig, roothash_sig_decoded);
1827 c->root_hash_sig_size = roothash_sig_decoded_size;
1828 c->root_hash_sig_path = mfree(c->root_hash_sig_path);
1829
1830 return 0;
1831 }
1832
1833 int config_parse_exec_cpu_affinity(
1834 const char *unit,
1835 const char *filename,
1836 unsigned line,
1837 const char *section,
1838 unsigned section_line,
1839 const char *lvalue,
1840 int ltype,
1841 const char *rvalue,
1842 void *data,
1843 void *userdata) {
1844
1845 ExecContext *c = ASSERT_PTR(data);
1846 const Unit *u = userdata;
1847 _cleanup_free_ char *k = NULL;
1848 int r;
1849
1850 assert(filename);
1851 assert(lvalue);
1852 assert(rvalue);
1853
1854 if (streq(rvalue, "numa")) {
1855 c->cpu_affinity_from_numa = true;
1856 cpu_set_reset(&c->cpu_set);
1857
1858 return 0;
1859 }
1860
1861 r = unit_full_printf(u, rvalue, &k);
1862 if (r < 0) {
1863 log_syntax(unit, LOG_WARNING, filename, line, r,
1864 "Failed to resolve unit specifiers in '%s', ignoring: %m",
1865 rvalue);
1866 return 0;
1867 }
1868
1869 r = parse_cpu_set_extend(k, &c->cpu_set, true, unit, filename, line, lvalue);
1870 if (r >= 0)
1871 c->cpu_affinity_from_numa = false;
1872
1873 return 0;
1874 }
1875
1876 int config_parse_capability_set(
1877 const char *unit,
1878 const char *filename,
1879 unsigned line,
1880 const char *section,
1881 unsigned section_line,
1882 const char *lvalue,
1883 int ltype,
1884 const char *rvalue,
1885 void *data,
1886 void *userdata) {
1887
1888 uint64_t *capability_set = ASSERT_PTR(data);
1889 uint64_t sum = 0, initial = 0;
1890 bool invert = false;
1891 int r;
1892
1893 assert(filename);
1894 assert(lvalue);
1895 assert(rvalue);
1896
1897 if (rvalue[0] == '~') {
1898 invert = true;
1899 rvalue++;
1900 }
1901
1902 if (streq(lvalue, "CapabilityBoundingSet"))
1903 initial = CAP_ALL; /* initialized to all bits on */
1904 /* else "AmbientCapabilities" initialized to all bits off */
1905
1906 r = capability_set_from_string(rvalue, &sum);
1907 if (r < 0) {
1908 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= specifier '%s', ignoring: %m", lvalue, rvalue);
1909 return 0;
1910 }
1911
1912 if (sum == 0 || *capability_set == initial)
1913 /* "", "~" or uninitialized data -> replace */
1914 *capability_set = invert ? ~sum : sum;
1915 else {
1916 /* previous data -> merge */
1917 if (invert)
1918 *capability_set &= ~sum;
1919 else
1920 *capability_set |= sum;
1921 }
1922
1923 return 0;
1924 }
1925
1926 int config_parse_exec_selinux_context(
1927 const char *unit,
1928 const char *filename,
1929 unsigned line,
1930 const char *section,
1931 unsigned section_line,
1932 const char *lvalue,
1933 int ltype,
1934 const char *rvalue,
1935 void *data,
1936 void *userdata) {
1937
1938 ExecContext *c = ASSERT_PTR(data);
1939 const Unit *u = userdata;
1940 bool ignore;
1941 char *k;
1942 int r;
1943
1944 assert(filename);
1945 assert(lvalue);
1946 assert(rvalue);
1947
1948 if (isempty(rvalue)) {
1949 c->selinux_context = mfree(c->selinux_context);
1950 c->selinux_context_ignore = false;
1951 return 0;
1952 }
1953
1954 if (rvalue[0] == '-') {
1955 ignore = true;
1956 rvalue++;
1957 } else
1958 ignore = false;
1959
1960 r = unit_full_printf(u, rvalue, &k);
1961 if (r < 0) {
1962 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
1963 "Failed to resolve unit specifiers in '%s'%s: %m",
1964 rvalue, ignore ? ", ignoring" : "");
1965 return ignore ? 0 : -ENOEXEC;
1966 }
1967
1968 free_and_replace(c->selinux_context, k);
1969 c->selinux_context_ignore = ignore;
1970
1971 return 0;
1972 }
1973
1974 int config_parse_exec_apparmor_profile(
1975 const char *unit,
1976 const char *filename,
1977 unsigned line,
1978 const char *section,
1979 unsigned section_line,
1980 const char *lvalue,
1981 int ltype,
1982 const char *rvalue,
1983 void *data,
1984 void *userdata) {
1985
1986 ExecContext *c = ASSERT_PTR(data);
1987 const Unit *u = userdata;
1988 bool ignore;
1989 char *k;
1990 int r;
1991
1992 assert(filename);
1993 assert(lvalue);
1994 assert(rvalue);
1995
1996 if (isempty(rvalue)) {
1997 c->apparmor_profile = mfree(c->apparmor_profile);
1998 c->apparmor_profile_ignore = false;
1999 return 0;
2000 }
2001
2002 if (rvalue[0] == '-') {
2003 ignore = true;
2004 rvalue++;
2005 } else
2006 ignore = false;
2007
2008 r = unit_full_printf(u, rvalue, &k);
2009 if (r < 0) {
2010 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
2011 "Failed to resolve unit specifiers in '%s'%s: %m",
2012 rvalue, ignore ? ", ignoring" : "");
2013 return ignore ? 0 : -ENOEXEC;
2014 }
2015
2016 free_and_replace(c->apparmor_profile, k);
2017 c->apparmor_profile_ignore = ignore;
2018
2019 return 0;
2020 }
2021
2022 int config_parse_exec_smack_process_label(
2023 const char *unit,
2024 const char *filename,
2025 unsigned line,
2026 const char *section,
2027 unsigned section_line,
2028 const char *lvalue,
2029 int ltype,
2030 const char *rvalue,
2031 void *data,
2032 void *userdata) {
2033
2034 ExecContext *c = ASSERT_PTR(data);
2035 const Unit *u = userdata;
2036 bool ignore;
2037 char *k;
2038 int r;
2039
2040 assert(filename);
2041 assert(lvalue);
2042 assert(rvalue);
2043
2044 if (isempty(rvalue)) {
2045 c->smack_process_label = mfree(c->smack_process_label);
2046 c->smack_process_label_ignore = false;
2047 return 0;
2048 }
2049
2050 if (rvalue[0] == '-') {
2051 ignore = true;
2052 rvalue++;
2053 } else
2054 ignore = false;
2055
2056 r = unit_full_printf(u, rvalue, &k);
2057 if (r < 0) {
2058 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
2059 "Failed to resolve unit specifiers in '%s'%s: %m",
2060 rvalue, ignore ? ", ignoring" : "");
2061 return ignore ? 0 : -ENOEXEC;
2062 }
2063
2064 free_and_replace(c->smack_process_label, k);
2065 c->smack_process_label_ignore = ignore;
2066
2067 return 0;
2068 }
2069
2070 int config_parse_timer(
2071 const char *unit,
2072 const char *filename,
2073 unsigned line,
2074 const char *section,
2075 unsigned section_line,
2076 const char *lvalue,
2077 int ltype,
2078 const char *rvalue,
2079 void *data,
2080 void *userdata) {
2081
2082 _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
2083 _cleanup_free_ char *k = NULL;
2084 const Unit *u = userdata;
2085 Timer *t = ASSERT_PTR(data);
2086 usec_t usec = 0;
2087 TimerValue *v;
2088 int r;
2089
2090 assert(filename);
2091 assert(lvalue);
2092 assert(rvalue);
2093
2094 if (isempty(rvalue)) {
2095 /* Empty assignment resets list */
2096 timer_free_values(t);
2097 return 0;
2098 }
2099
2100 r = unit_full_printf(u, rvalue, &k);
2101 if (r < 0) {
2102 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2103 return 0;
2104 }
2105
2106 if (ltype == TIMER_CALENDAR) {
2107 r = calendar_spec_from_string(k, &c);
2108 if (r < 0) {
2109 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse calendar specification, ignoring: %s", k);
2110 return 0;
2111 }
2112 } else {
2113 r = parse_sec(k, &usec);
2114 if (r < 0) {
2115 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse timer value, ignoring: %s", k);
2116 return 0;
2117 }
2118 }
2119
2120 v = new(TimerValue, 1);
2121 if (!v)
2122 return log_oom();
2123
2124 *v = (TimerValue) {
2125 .base = ltype,
2126 .value = usec,
2127 .calendar_spec = TAKE_PTR(c),
2128 };
2129
2130 LIST_PREPEND(value, t->values, v);
2131
2132 return 0;
2133 }
2134
2135 int config_parse_trigger_unit(
2136 const char *unit,
2137 const char *filename,
2138 unsigned line,
2139 const char *section,
2140 unsigned section_line,
2141 const char *lvalue,
2142 int ltype,
2143 const char *rvalue,
2144 void *data,
2145 void *userdata) {
2146
2147 _cleanup_free_ char *p = NULL;
2148 Unit *u = ASSERT_PTR(data);
2149 UnitType type;
2150 int r;
2151
2152 assert(filename);
2153 assert(lvalue);
2154 assert(rvalue);
2155
2156 if (UNIT_TRIGGER(u)) {
2157 log_syntax(unit, LOG_WARNING, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
2158 return 0;
2159 }
2160
2161 r = unit_name_printf(u, rvalue, &p);
2162 if (r < 0) {
2163 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2164 return 0;
2165 }
2166
2167 type = unit_name_to_type(p);
2168 if (type < 0) {
2169 log_syntax(unit, LOG_WARNING, filename, line, type, "Unit type not valid, ignoring: %s", rvalue);
2170 return 0;
2171 }
2172 if (unit_has_name(u, p)) {
2173 log_syntax(unit, LOG_WARNING, filename, line, 0, "Units cannot trigger themselves, ignoring: %s", rvalue);
2174 return 0;
2175 }
2176
2177 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, true, UNIT_DEPENDENCY_FILE);
2178 if (r < 0) {
2179 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
2180 return 0;
2181 }
2182
2183 return 0;
2184 }
2185
2186 int config_parse_path_spec(const char *unit,
2187 const char *filename,
2188 unsigned line,
2189 const char *section,
2190 unsigned section_line,
2191 const char *lvalue,
2192 int ltype,
2193 const char *rvalue,
2194 void *data,
2195 void *userdata) {
2196
2197 Path *p = ASSERT_PTR(data);
2198 PathSpec *s;
2199 PathType b;
2200 _cleanup_free_ char *k = NULL;
2201 int r;
2202
2203 assert(filename);
2204 assert(lvalue);
2205 assert(rvalue);
2206
2207 if (isempty(rvalue)) {
2208 /* Empty assignment clears list */
2209 path_free_specs(p);
2210 return 0;
2211 }
2212
2213 b = path_type_from_string(lvalue);
2214 if (b < 0) {
2215 log_syntax(unit, LOG_WARNING, filename, line, b, "Failed to parse path type, ignoring: %s", lvalue);
2216 return 0;
2217 }
2218
2219 r = unit_path_printf(UNIT(p), rvalue, &k);
2220 if (r < 0) {
2221 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2222 return 0;
2223 }
2224
2225 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2226 if (r < 0)
2227 return 0;
2228
2229 s = new0(PathSpec, 1);
2230 if (!s)
2231 return log_oom();
2232
2233 s->unit = UNIT(p);
2234 s->path = TAKE_PTR(k);
2235 s->type = b;
2236 s->inotify_fd = -1;
2237
2238 LIST_PREPEND(spec, p->specs, s);
2239
2240 return 0;
2241 }
2242
2243 int config_parse_socket_service(
2244 const char *unit,
2245 const char *filename,
2246 unsigned line,
2247 const char *section,
2248 unsigned section_line,
2249 const char *lvalue,
2250 int ltype,
2251 const char *rvalue,
2252 void *data,
2253 void *userdata) {
2254
2255 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2256 _cleanup_free_ char *p = NULL;
2257 Socket *s = ASSERT_PTR(data);
2258 Unit *x;
2259 int r;
2260
2261 assert(filename);
2262 assert(lvalue);
2263 assert(rvalue);
2264
2265 r = unit_name_printf(UNIT(s), rvalue, &p);
2266 if (r < 0) {
2267 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2268 return 0;
2269 }
2270
2271 if (!endswith(p, ".service")) {
2272 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
2273 return 0;
2274 }
2275
2276 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
2277 if (r < 0) {
2278 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
2279 return 0;
2280 }
2281
2282 unit_ref_set(&s->service, UNIT(s), x);
2283
2284 return 0;
2285 }
2286
2287 int config_parse_fdname(
2288 const char *unit,
2289 const char *filename,
2290 unsigned line,
2291 const char *section,
2292 unsigned section_line,
2293 const char *lvalue,
2294 int ltype,
2295 const char *rvalue,
2296 void *data,
2297 void *userdata) {
2298
2299 _cleanup_free_ char *p = NULL;
2300 Socket *s = ASSERT_PTR(data);
2301 int r;
2302
2303 assert(filename);
2304 assert(lvalue);
2305 assert(rvalue);
2306
2307 if (isempty(rvalue)) {
2308 s->fdname = mfree(s->fdname);
2309 return 0;
2310 }
2311
2312 r = unit_fd_printf(UNIT(s), rvalue, &p);
2313 if (r < 0) {
2314 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2315 return 0;
2316 }
2317
2318 if (!fdname_is_valid(p)) {
2319 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
2320 return 0;
2321 }
2322
2323 return free_and_replace(s->fdname, p);
2324 }
2325
2326 int config_parse_service_sockets(
2327 const char *unit,
2328 const char *filename,
2329 unsigned line,
2330 const char *section,
2331 unsigned section_line,
2332 const char *lvalue,
2333 int ltype,
2334 const char *rvalue,
2335 void *data,
2336 void *userdata) {
2337
2338 Service *s = ASSERT_PTR(data);
2339 int r;
2340
2341 assert(filename);
2342 assert(lvalue);
2343 assert(rvalue);
2344
2345 for (const char *p = rvalue;;) {
2346 _cleanup_free_ char *word = NULL, *k = NULL;
2347
2348 r = extract_first_word(&p, &word, NULL, 0);
2349 if (r == -ENOMEM)
2350 return log_oom();
2351 if (r < 0) {
2352 log_syntax(unit, LOG_WARNING, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
2353 return 0;
2354 }
2355 if (r == 0)
2356 return 0;
2357
2358 r = unit_name_printf(UNIT(s), word, &k);
2359 if (r < 0) {
2360 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
2361 continue;
2362 }
2363
2364 if (!endswith(k, ".socket")) {
2365 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
2366 continue;
2367 }
2368
2369 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, true, UNIT_DEPENDENCY_FILE);
2370 if (r < 0)
2371 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
2372
2373 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, true, UNIT_DEPENDENCY_FILE);
2374 if (r < 0)
2375 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
2376 }
2377 }
2378
2379 int config_parse_bus_name(
2380 const char *unit,
2381 const char *filename,
2382 unsigned line,
2383 const char *section,
2384 unsigned section_line,
2385 const char *lvalue,
2386 int ltype,
2387 const char *rvalue,
2388 void *data,
2389 void *userdata) {
2390
2391 _cleanup_free_ char *k = NULL;
2392 const Unit *u = ASSERT_PTR(userdata);
2393 int r;
2394
2395 assert(filename);
2396 assert(lvalue);
2397 assert(rvalue);
2398
2399 r = unit_full_printf_full(u, rvalue, SD_BUS_MAXIMUM_NAME_LENGTH, &k);
2400 if (r < 0) {
2401 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2402 return 0;
2403 }
2404
2405 if (!sd_bus_service_name_is_valid(k)) {
2406 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid bus name, ignoring: %s", k);
2407 return 0;
2408 }
2409
2410 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
2411 }
2412
2413 int config_parse_service_timeout(
2414 const char *unit,
2415 const char *filename,
2416 unsigned line,
2417 const char *section,
2418 unsigned section_line,
2419 const char *lvalue,
2420 int ltype,
2421 const char *rvalue,
2422 void *data,
2423 void *userdata) {
2424
2425 Service *s = ASSERT_PTR(userdata);
2426 usec_t usec;
2427 int r;
2428
2429 assert(filename);
2430 assert(lvalue);
2431 assert(rvalue);
2432
2433 /* This is called for two cases: TimeoutSec= and TimeoutStartSec=. */
2434
2435 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
2436 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
2437 * all other timeouts. */
2438 r = parse_sec_fix_0(rvalue, &usec);
2439 if (r < 0) {
2440 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
2441 return 0;
2442 }
2443
2444 s->start_timeout_defined = true;
2445 s->timeout_start_usec = usec;
2446
2447 if (streq(lvalue, "TimeoutSec"))
2448 s->timeout_stop_usec = usec;
2449
2450 return 0;
2451 }
2452
2453 int config_parse_timeout_abort(
2454 const char *unit,
2455 const char *filename,
2456 unsigned line,
2457 const char *section,
2458 unsigned section_line,
2459 const char *lvalue,
2460 int ltype,
2461 const char *rvalue,
2462 void *data,
2463 void *userdata) {
2464
2465 usec_t *ret = ASSERT_PTR(data);
2466 int r;
2467
2468 assert(filename);
2469 assert(lvalue);
2470 assert(rvalue);
2471
2472 /* Note: apart from setting the arg, this returns an extra bit of information in the return value. */
2473
2474 if (isempty(rvalue)) {
2475 *ret = 0;
2476 return 0; /* "not set" */
2477 }
2478
2479 r = parse_sec(rvalue, ret);
2480 if (r < 0)
2481 return log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= setting, ignoring: %s", lvalue, rvalue);
2482
2483 return 1; /* "set" */
2484 }
2485
2486 int config_parse_service_timeout_abort(
2487 const char *unit,
2488 const char *filename,
2489 unsigned line,
2490 const char *section,
2491 unsigned section_line,
2492 const char *lvalue,
2493 int ltype,
2494 const char *rvalue,
2495 void *data,
2496 void *userdata) {
2497
2498 Service *s = ASSERT_PTR(userdata);
2499 int r;
2500
2501 r = config_parse_timeout_abort(unit, filename, line, section, section_line, lvalue, ltype, rvalue,
2502 &s->timeout_abort_usec, s);
2503 if (r >= 0)
2504 s->timeout_abort_set = r;
2505 return 0;
2506 }
2507
2508 int config_parse_user_group_compat(
2509 const char *unit,
2510 const char *filename,
2511 unsigned line,
2512 const char *section,
2513 unsigned section_line,
2514 const char *lvalue,
2515 int ltype,
2516 const char *rvalue,
2517 void *data,
2518 void *userdata) {
2519
2520 _cleanup_free_ char *k = NULL;
2521 char **user = data;
2522 const Unit *u = ASSERT_PTR(userdata);
2523 int r;
2524
2525 assert(filename);
2526 assert(lvalue);
2527 assert(rvalue);
2528
2529 if (isempty(rvalue)) {
2530 *user = mfree(*user);
2531 return 0;
2532 }
2533
2534 r = unit_full_printf(u, rvalue, &k);
2535 if (r < 0) {
2536 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue);
2537 return -ENOEXEC;
2538 }
2539
2540 if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) {
2541 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2542 return -ENOEXEC;
2543 }
2544
2545 if (strstr(lvalue, "User") && streq(k, NOBODY_USER_NAME))
2546 log_struct(LOG_NOTICE,
2547 "MESSAGE=%s:%u: Special user %s configured, this is not safe!", filename, line, k,
2548 "UNIT=%s", unit,
2549 "MESSAGE_ID=" SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR,
2550 "OFFENDING_USER=%s", k,
2551 "CONFIG_FILE=%s", filename,
2552 "CONFIG_LINE=%u", line);
2553
2554 return free_and_replace(*user, k);
2555 }
2556
2557 int config_parse_user_group_strv_compat(
2558 const char *unit,
2559 const char *filename,
2560 unsigned line,
2561 const char *section,
2562 unsigned section_line,
2563 const char *lvalue,
2564 int ltype,
2565 const char *rvalue,
2566 void *data,
2567 void *userdata) {
2568
2569 char ***users = data;
2570 const Unit *u = ASSERT_PTR(userdata);
2571 int r;
2572
2573 assert(filename);
2574 assert(lvalue);
2575 assert(rvalue);
2576
2577 if (isempty(rvalue)) {
2578 *users = strv_free(*users);
2579 return 0;
2580 }
2581
2582 for (const char *p = rvalue;;) {
2583 _cleanup_free_ char *word = NULL, *k = NULL;
2584
2585 r = extract_first_word(&p, &word, NULL, 0);
2586 if (r == -ENOMEM)
2587 return log_oom();
2588 if (r < 0) {
2589 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue);
2590 return -ENOEXEC;
2591 }
2592 if (r == 0)
2593 return 0;
2594
2595 r = unit_full_printf(u, word, &k);
2596 if (r < 0) {
2597 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word);
2598 return -ENOEXEC;
2599 }
2600
2601 if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) {
2602 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2603 return -ENOEXEC;
2604 }
2605
2606 r = strv_push(users, k);
2607 if (r < 0)
2608 return log_oom();
2609
2610 k = NULL;
2611 }
2612 }
2613
2614 int config_parse_working_directory(
2615 const char *unit,
2616 const char *filename,
2617 unsigned line,
2618 const char *section,
2619 unsigned section_line,
2620 const char *lvalue,
2621 int ltype,
2622 const char *rvalue,
2623 void *data,
2624 void *userdata) {
2625
2626 ExecContext *c = ASSERT_PTR(data);
2627 const Unit *u = ASSERT_PTR(userdata);
2628 bool missing_ok;
2629 int r;
2630
2631 assert(filename);
2632 assert(lvalue);
2633 assert(rvalue);
2634
2635 if (isempty(rvalue)) {
2636 c->working_directory_home = false;
2637 c->working_directory = mfree(c->working_directory);
2638 return 0;
2639 }
2640
2641 if (rvalue[0] == '-') {
2642 missing_ok = true;
2643 rvalue++;
2644 } else
2645 missing_ok = false;
2646
2647 if (streq(rvalue, "~")) {
2648 c->working_directory_home = true;
2649 c->working_directory = mfree(c->working_directory);
2650 } else {
2651 _cleanup_free_ char *k = NULL;
2652
2653 r = unit_path_printf(u, rvalue, &k);
2654 if (r < 0) {
2655 log_syntax(unit, missing_ok ? LOG_WARNING : LOG_ERR, filename, line, r,
2656 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2657 rvalue, missing_ok ? ", ignoring" : "");
2658 return missing_ok ? 0 : -ENOEXEC;
2659 }
2660
2661 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE | (missing_ok ? 0 : PATH_CHECK_FATAL), unit, filename, line, lvalue);
2662 if (r < 0)
2663 return missing_ok ? 0 : -ENOEXEC;
2664
2665 c->working_directory_home = false;
2666 free_and_replace(c->working_directory, k);
2667 }
2668
2669 c->working_directory_missing_ok = missing_ok;
2670 return 0;
2671 }
2672
2673 int config_parse_unit_env_file(const char *unit,
2674 const char *filename,
2675 unsigned line,
2676 const char *section,
2677 unsigned section_line,
2678 const char *lvalue,
2679 int ltype,
2680 const char *rvalue,
2681 void *data,
2682 void *userdata) {
2683
2684 char ***env = ASSERT_PTR(data);
2685 const Unit *u = userdata;
2686 _cleanup_free_ char *n = NULL;
2687 int r;
2688
2689 assert(filename);
2690 assert(lvalue);
2691 assert(rvalue);
2692
2693 if (isempty(rvalue)) {
2694 /* Empty assignment frees the list */
2695 *env = strv_free(*env);
2696 return 0;
2697 }
2698
2699 r = unit_full_printf_full(u, rvalue, PATH_MAX, &n);
2700 if (r < 0) {
2701 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2702 return 0;
2703 }
2704
2705 r = path_simplify_and_warn(n[0] == '-' ? n + 1 : n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2706 if (r < 0)
2707 return 0;
2708
2709 r = strv_push(env, n);
2710 if (r < 0)
2711 return log_oom();
2712
2713 n = NULL;
2714
2715 return 0;
2716 }
2717
2718 int config_parse_environ(
2719 const char *unit,
2720 const char *filename,
2721 unsigned line,
2722 const char *section,
2723 unsigned section_line,
2724 const char *lvalue,
2725 int ltype,
2726 const char *rvalue,
2727 void *data,
2728 void *userdata) {
2729
2730 const Unit *u = userdata;
2731 char ***env = ASSERT_PTR(data);
2732 int r;
2733
2734 assert(filename);
2735 assert(lvalue);
2736 assert(rvalue);
2737
2738 if (isempty(rvalue)) {
2739 /* Empty assignment resets the list */
2740 *env = strv_free(*env);
2741 return 0;
2742 }
2743
2744 for (const char *p = rvalue;; ) {
2745 _cleanup_free_ char *word = NULL, *resolved = NULL;
2746
2747 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
2748 if (r == -ENOMEM)
2749 return log_oom();
2750 if (r < 0) {
2751 log_syntax(unit, LOG_WARNING, filename, line, r,
2752 "Invalid syntax, ignoring: %s", rvalue);
2753 return 0;
2754 }
2755 if (r == 0)
2756 return 0;
2757
2758 if (u)
2759 r = unit_env_printf(u, word, &resolved);
2760 else
2761 r = specifier_printf(word, sc_arg_max(), system_and_tmp_specifier_table, NULL, NULL, &resolved);
2762 if (r < 0) {
2763 log_syntax(unit, LOG_WARNING, filename, line, r,
2764 "Failed to resolve specifiers in %s, ignoring: %m", word);
2765 continue;
2766 }
2767
2768 if (!env_assignment_is_valid(resolved)) {
2769 log_syntax(unit, LOG_WARNING, filename, line, 0,
2770 "Invalid environment assignment, ignoring: %s", resolved);
2771 continue;
2772 }
2773
2774 r = strv_env_replace_consume(env, TAKE_PTR(resolved));
2775 if (r < 0)
2776 return log_error_errno(r, "Failed to update environment: %m");
2777 }
2778 }
2779
2780 int config_parse_pass_environ(
2781 const char *unit,
2782 const char *filename,
2783 unsigned line,
2784 const char *section,
2785 unsigned section_line,
2786 const char *lvalue,
2787 int ltype,
2788 const char *rvalue,
2789 void *data,
2790 void *userdata) {
2791
2792 _cleanup_strv_free_ char **n = NULL;
2793 const Unit *u = userdata;
2794 char*** passenv = ASSERT_PTR(data);
2795 size_t nlen = 0;
2796 int r;
2797
2798 assert(filename);
2799 assert(lvalue);
2800 assert(rvalue);
2801
2802 if (isempty(rvalue)) {
2803 /* Empty assignment resets the list */
2804 *passenv = strv_free(*passenv);
2805 return 0;
2806 }
2807
2808 for (const char *p = rvalue;;) {
2809 _cleanup_free_ char *word = NULL, *k = NULL;
2810
2811 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
2812 if (r == -ENOMEM)
2813 return log_oom();
2814 if (r < 0) {
2815 log_syntax(unit, LOG_WARNING, filename, line, r,
2816 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
2817 break;
2818 }
2819 if (r == 0)
2820 break;
2821
2822 if (u) {
2823 r = unit_env_printf(u, word, &k);
2824 if (r < 0) {
2825 log_syntax(unit, LOG_WARNING, filename, line, r,
2826 "Failed to resolve specifiers in %s, ignoring: %m", word);
2827 continue;
2828 }
2829 } else
2830 k = TAKE_PTR(word);
2831
2832 if (!env_name_is_valid(k)) {
2833 log_syntax(unit, LOG_WARNING, filename, line, 0,
2834 "Invalid environment name for %s, ignoring: %s", lvalue, k);
2835 continue;
2836 }
2837
2838 if (!GREEDY_REALLOC(n, nlen + 2))
2839 return log_oom();
2840
2841 n[nlen++] = TAKE_PTR(k);
2842 n[nlen] = NULL;
2843 }
2844
2845 if (n) {
2846 r = strv_extend_strv(passenv, n, true);
2847 if (r < 0)
2848 return log_oom();
2849 }
2850
2851 return 0;
2852 }
2853
2854 int config_parse_unset_environ(
2855 const char *unit,
2856 const char *filename,
2857 unsigned line,
2858 const char *section,
2859 unsigned section_line,
2860 const char *lvalue,
2861 int ltype,
2862 const char *rvalue,
2863 void *data,
2864 void *userdata) {
2865
2866 _cleanup_strv_free_ char **n = NULL;
2867 char*** unsetenv = ASSERT_PTR(data);
2868 const Unit *u = userdata;
2869 size_t nlen = 0;
2870 int r;
2871
2872 assert(filename);
2873 assert(lvalue);
2874 assert(rvalue);
2875
2876 if (isempty(rvalue)) {
2877 /* Empty assignment resets the list */
2878 *unsetenv = strv_free(*unsetenv);
2879 return 0;
2880 }
2881
2882 for (const char *p = rvalue;;) {
2883 _cleanup_free_ char *word = NULL, *k = NULL;
2884
2885 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
2886 if (r == -ENOMEM)
2887 return log_oom();
2888 if (r < 0) {
2889 log_syntax(unit, LOG_WARNING, filename, line, r,
2890 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
2891 break;
2892 }
2893 if (r == 0)
2894 break;
2895
2896 if (u) {
2897 r = unit_env_printf(u, word, &k);
2898 if (r < 0) {
2899 log_syntax(unit, LOG_WARNING, filename, line, r,
2900 "Failed to resolve unit specifiers in %s, ignoring: %m", word);
2901 continue;
2902 }
2903 } else
2904 k = TAKE_PTR(word);
2905
2906 if (!env_assignment_is_valid(k) && !env_name_is_valid(k)) {
2907 log_syntax(unit, LOG_WARNING, filename, line, 0,
2908 "Invalid environment name or assignment %s, ignoring: %s", lvalue, k);
2909 continue;
2910 }
2911
2912 if (!GREEDY_REALLOC(n, nlen + 2))
2913 return log_oom();
2914
2915 n[nlen++] = TAKE_PTR(k);
2916 n[nlen] = NULL;
2917 }
2918
2919 if (n) {
2920 r = strv_extend_strv(unsetenv, n, true);
2921 if (r < 0)
2922 return log_oom();
2923 }
2924
2925 return 0;
2926 }
2927
2928 int config_parse_log_extra_fields(
2929 const char *unit,
2930 const char *filename,
2931 unsigned line,
2932 const char *section,
2933 unsigned section_line,
2934 const char *lvalue,
2935 int ltype,
2936 const char *rvalue,
2937 void *data,
2938 void *userdata) {
2939
2940 ExecContext *c = ASSERT_PTR(data);
2941 const Unit *u = userdata;
2942 int r;
2943
2944 assert(filename);
2945 assert(lvalue);
2946 assert(rvalue);
2947
2948 if (isempty(rvalue)) {
2949 exec_context_free_log_extra_fields(c);
2950 return 0;
2951 }
2952
2953 for (const char *p = rvalue;;) {
2954 _cleanup_free_ char *word = NULL, *k = NULL;
2955 struct iovec *t;
2956 const char *eq;
2957
2958 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
2959 if (r == -ENOMEM)
2960 return log_oom();
2961 if (r < 0) {
2962 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
2963 return 0;
2964 }
2965 if (r == 0)
2966 return 0;
2967
2968 r = unit_full_printf(u, word, &k);
2969 if (r < 0) {
2970 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word);
2971 continue;
2972 }
2973
2974 eq = strchr(k, '=');
2975 if (!eq) {
2976 log_syntax(unit, LOG_WARNING, filename, line, 0, "Log field lacks '=' character, ignoring: %s", k);
2977 continue;
2978 }
2979
2980 if (!journal_field_valid(k, eq-k, false)) {
2981 log_syntax(unit, LOG_WARNING, filename, line, 0, "Log field name is invalid, ignoring: %s", k);
2982 continue;
2983 }
2984
2985 t = reallocarray(c->log_extra_fields, c->n_log_extra_fields+1, sizeof(struct iovec));
2986 if (!t)
2987 return log_oom();
2988
2989 c->log_extra_fields = t;
2990 c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE_STRING(k);
2991
2992 k = NULL;
2993 }
2994 }
2995
2996 int config_parse_log_namespace(
2997 const char *unit,
2998 const char *filename,
2999 unsigned line,
3000 const char *section,
3001 unsigned section_line,
3002 const char *lvalue,
3003 int ltype,
3004 const char *rvalue,
3005 void *data,
3006 void *userdata) {
3007
3008 _cleanup_free_ char *k = NULL;
3009 ExecContext *c = ASSERT_PTR(data);
3010 const Unit *u = userdata;
3011 int r;
3012
3013 assert(filename);
3014 assert(lvalue);
3015 assert(rvalue);
3016
3017 if (isempty(rvalue)) {
3018 c->log_namespace = mfree(c->log_namespace);
3019 return 0;
3020 }
3021
3022 r = unit_full_printf_full(u, rvalue, NAME_MAX, &k);
3023 if (r < 0) {
3024 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
3025 return 0;
3026 }
3027
3028 if (!log_namespace_name_valid(k)) {
3029 log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified log namespace name is not valid, ignoring: %s", k);
3030 return 0;
3031 }
3032
3033 free_and_replace(c->log_namespace, k);
3034 return 0;
3035 }
3036
3037 int config_parse_unit_condition_path(
3038 const char *unit,
3039 const char *filename,
3040 unsigned line,
3041 const char *section,
3042 unsigned section_line,
3043 const char *lvalue,
3044 int ltype,
3045 const char *rvalue,
3046 void *data,
3047 void *userdata) {
3048
3049 _cleanup_free_ char *p = NULL;
3050 Condition **list = ASSERT_PTR(data), *c;
3051 ConditionType t = ltype;
3052 bool trigger, negate;
3053 const Unit *u = userdata;
3054 int r;
3055
3056 assert(filename);
3057 assert(lvalue);
3058 assert(rvalue);
3059
3060 if (isempty(rvalue)) {
3061 /* Empty assignment resets the list */
3062 *list = condition_free_list(*list);
3063 return 0;
3064 }
3065
3066 trigger = rvalue[0] == '|';
3067 if (trigger)
3068 rvalue++;
3069
3070 negate = rvalue[0] == '!';
3071 if (negate)
3072 rvalue++;
3073
3074 r = unit_path_printf(u, rvalue, &p);
3075 if (r < 0) {
3076 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
3077 return 0;
3078 }
3079
3080 r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
3081 if (r < 0)
3082 return 0;
3083
3084 c = condition_new(t, p, trigger, negate);
3085 if (!c)
3086 return log_oom();
3087
3088 LIST_PREPEND(conditions, *list, c);
3089 return 0;
3090 }
3091
3092 int config_parse_unit_condition_string(
3093 const char *unit,
3094 const char *filename,
3095 unsigned line,
3096 const char *section,
3097 unsigned section_line,
3098 const char *lvalue,
3099 int ltype,
3100 const char *rvalue,
3101 void *data,
3102 void *userdata) {
3103
3104 _cleanup_free_ char *s = NULL;
3105 Condition **list = ASSERT_PTR(data), *c;
3106 ConditionType t = ltype;
3107 bool trigger, negate;
3108 const Unit *u = userdata;
3109 int r;
3110
3111 assert(filename);
3112 assert(lvalue);
3113 assert(rvalue);
3114
3115 if (isempty(rvalue)) {
3116 /* Empty assignment resets the list */
3117 *list = condition_free_list(*list);
3118 return 0;
3119 }
3120
3121 trigger = *rvalue == '|';
3122 if (trigger)
3123 rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
3124
3125 negate = *rvalue == '!';
3126 if (negate)
3127 rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
3128
3129 r = unit_full_printf(u, rvalue, &s);
3130 if (r < 0) {
3131 log_syntax(unit, LOG_WARNING, filename, line, r,
3132 "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
3133 return 0;
3134 }
3135
3136 c = condition_new(t, s, trigger, negate);
3137 if (!c)
3138 return log_oom();
3139
3140 LIST_PREPEND(conditions, *list, c);
3141 return 0;
3142 }
3143
3144 int config_parse_unit_requires_mounts_for(
3145 const char *unit,
3146 const char *filename,
3147 unsigned line,
3148 const char *section,
3149 unsigned section_line,
3150 const char *lvalue,
3151 int ltype,
3152 const char *rvalue,
3153 void *data,
3154 void *userdata) {
3155
3156 Unit *u = userdata;
3157 int r;
3158
3159 assert(filename);
3160 assert(lvalue);
3161 assert(rvalue);
3162 assert(data);
3163
3164 for (const char *p = rvalue;;) {
3165 _cleanup_free_ char *word = NULL, *resolved = NULL;
3166
3167 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3168 if (r == -ENOMEM)
3169 return log_oom();
3170 if (r < 0) {
3171 log_syntax(unit, LOG_WARNING, filename, line, r,
3172 "Invalid syntax, ignoring: %s", rvalue);
3173 return 0;
3174 }
3175 if (r == 0)
3176 return 0;
3177
3178 r = unit_path_printf(u, word, &resolved);
3179 if (r < 0) {
3180 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
3181 continue;
3182 }
3183
3184 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
3185 if (r < 0)
3186 continue;
3187
3188 r = unit_require_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE);
3189 if (r < 0) {
3190 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add required mount '%s', ignoring: %m", resolved);
3191 continue;
3192 }
3193 }
3194 }
3195
3196 int config_parse_documentation(
3197 const char *unit,
3198 const char *filename,
3199 unsigned line,
3200 const char *section,
3201 unsigned section_line,
3202 const char *lvalue,
3203 int ltype,
3204 const char *rvalue,
3205 void *data,
3206 void *userdata) {
3207
3208 Unit *u = ASSERT_PTR(userdata);
3209 int r;
3210 char **a, **b;
3211
3212 assert(filename);
3213 assert(lvalue);
3214 assert(rvalue);
3215
3216 if (isempty(rvalue)) {
3217 /* Empty assignment resets the list */
3218 u->documentation = strv_free(u->documentation);
3219 return 0;
3220 }
3221
3222 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
3223 rvalue, data, userdata);
3224 if (r < 0)
3225 return r;
3226
3227 for (a = b = u->documentation; a && *a; a++) {
3228
3229 if (documentation_url_is_valid(*a))
3230 *(b++) = *a;
3231 else {
3232 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid URL, ignoring: %s", *a);
3233 free(*a);
3234 }
3235 }
3236 if (b)
3237 *b = NULL;
3238
3239 return 0;
3240 }
3241
3242 #if HAVE_SECCOMP
3243 int config_parse_syscall_filter(
3244 const char *unit,
3245 const char *filename,
3246 unsigned line,
3247 const char *section,
3248 unsigned section_line,
3249 const char *lvalue,
3250 int ltype,
3251 const char *rvalue,
3252 void *data,
3253 void *userdata) {
3254
3255 ExecContext *c = data;
3256 _unused_ const Unit *u = ASSERT_PTR(userdata);
3257 bool invert = false;
3258 int r;
3259
3260 assert(filename);
3261 assert(lvalue);
3262 assert(rvalue);
3263
3264 if (isempty(rvalue)) {
3265 /* Empty assignment resets the list */
3266 c->syscall_filter = hashmap_free(c->syscall_filter);
3267 c->syscall_allow_list = false;
3268 return 0;
3269 }
3270
3271 if (rvalue[0] == '~') {
3272 invert = true;
3273 rvalue++;
3274 }
3275
3276 if (!c->syscall_filter) {
3277 c->syscall_filter = hashmap_new(NULL);
3278 if (!c->syscall_filter)
3279 return log_oom();
3280
3281 if (invert)
3282 /* Allow everything but the ones listed */
3283 c->syscall_allow_list = false;
3284 else {
3285 /* Allow nothing but the ones listed */
3286 c->syscall_allow_list = true;
3287
3288 /* Accept default syscalls if we are on an allow_list */
3289 r = seccomp_parse_syscall_filter(
3290 "@default", -1, c->syscall_filter,
3291 SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_ALLOW_LIST,
3292 unit,
3293 NULL, 0);
3294 if (r < 0)
3295 return r;
3296 }
3297 }
3298
3299 for (const char *p = rvalue;;) {
3300 _cleanup_free_ char *word = NULL, *name = NULL;
3301 int num;
3302
3303 r = extract_first_word(&p, &word, NULL, 0);
3304 if (r == -ENOMEM)
3305 return log_oom();
3306 if (r < 0) {
3307 log_syntax(unit, LOG_WARNING, filename, line, r,
3308 "Invalid syntax, ignoring: %s", rvalue);
3309 return 0;
3310 }
3311 if (r == 0)
3312 return 0;
3313
3314 r = parse_syscall_and_errno(word, &name, &num);
3315 if (r < 0) {
3316 log_syntax(unit, LOG_WARNING, filename, line, r,
3317 "Failed to parse syscall:errno, ignoring: %s", word);
3318 continue;
3319 }
3320 if (!invert && num >= 0) {
3321 log_syntax(unit, LOG_WARNING, filename, line, 0,
3322 "Allow-listed system calls cannot take error number, ignoring: %s", word);
3323 continue;
3324 }
3325
3326 r = seccomp_parse_syscall_filter(
3327 name, num, c->syscall_filter,
3328 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
3329 (invert ? SECCOMP_PARSE_INVERT : 0)|
3330 (c->syscall_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
3331 unit, filename, line);
3332 if (r < 0)
3333 return r;
3334 }
3335 }
3336
3337 int config_parse_syscall_log(
3338 const char *unit,
3339 const char *filename,
3340 unsigned line,
3341 const char *section,
3342 unsigned section_line,
3343 const char *lvalue,
3344 int ltype,
3345 const char *rvalue,
3346 void *data,
3347 void *userdata) {
3348
3349 ExecContext *c = data;
3350 _unused_ const Unit *u = ASSERT_PTR(userdata);
3351 bool invert = false;
3352 const char *p;
3353 int r;
3354
3355 assert(filename);
3356 assert(lvalue);
3357 assert(rvalue);
3358
3359 if (isempty(rvalue)) {
3360 /* Empty assignment resets the list */
3361 c->syscall_log = hashmap_free(c->syscall_log);
3362 c->syscall_log_allow_list = false;
3363 return 0;
3364 }
3365
3366 if (rvalue[0] == '~') {
3367 invert = true;
3368 rvalue++;
3369 }
3370
3371 if (!c->syscall_log) {
3372 c->syscall_log = hashmap_new(NULL);
3373 if (!c->syscall_log)
3374 return log_oom();
3375
3376 if (invert)
3377 /* Log everything but the ones listed */
3378 c->syscall_log_allow_list = false;
3379 else
3380 /* Log nothing but the ones listed */
3381 c->syscall_log_allow_list = true;
3382 }
3383
3384 p = rvalue;
3385 for (;;) {
3386 _cleanup_free_ char *word = NULL;
3387
3388 r = extract_first_word(&p, &word, NULL, 0);
3389 if (r == -ENOMEM)
3390 return log_oom();
3391 if (r < 0) {
3392 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
3393 return 0;
3394 }
3395 if (r == 0)
3396 return 0;
3397
3398 r = seccomp_parse_syscall_filter(
3399 word, -1, c->syscall_log,
3400 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
3401 (invert ? SECCOMP_PARSE_INVERT : 0)|
3402 (c->syscall_log_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
3403 unit, filename, line);
3404 if (r < 0)
3405 return r;
3406 }
3407 }
3408
3409 int config_parse_syscall_archs(
3410 const char *unit,
3411 const char *filename,
3412 unsigned line,
3413 const char *section,
3414 unsigned section_line,
3415 const char *lvalue,
3416 int ltype,
3417 const char *rvalue,
3418 void *data,
3419 void *userdata) {
3420
3421 Set **archs = data;
3422 int r;
3423
3424 if (isempty(rvalue)) {
3425 *archs = set_free(*archs);
3426 return 0;
3427 }
3428
3429 for (const char *p = rvalue;;) {
3430 _cleanup_free_ char *word = NULL;
3431 uint32_t a;
3432
3433 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3434 if (r == -ENOMEM)
3435 return log_oom();
3436 if (r < 0) {
3437 log_syntax(unit, LOG_WARNING, filename, line, r,
3438 "Invalid syntax, ignoring: %s", rvalue);
3439 return 0;
3440 }
3441 if (r == 0)
3442 return 0;
3443
3444 r = seccomp_arch_from_string(word, &a);
3445 if (r < 0) {
3446 log_syntax(unit, LOG_WARNING, filename, line, r,
3447 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
3448 continue;
3449 }
3450
3451 r = set_ensure_put(archs, NULL, UINT32_TO_PTR(a + 1));
3452 if (r < 0)
3453 return log_oom();
3454 }
3455 }
3456
3457 int config_parse_syscall_errno(
3458 const char *unit,
3459 const char *filename,
3460 unsigned line,
3461 const char *section,
3462 unsigned section_line,
3463 const char *lvalue,
3464 int ltype,
3465 const char *rvalue,
3466 void *data,
3467 void *userdata) {
3468
3469 ExecContext *c = data;
3470 int e;
3471
3472 assert(filename);
3473 assert(lvalue);
3474 assert(rvalue);
3475
3476 if (isempty(rvalue) || streq(rvalue, "kill")) {
3477 /* Empty assignment resets to KILL */
3478 c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
3479 return 0;
3480 }
3481
3482 e = parse_errno(rvalue);
3483 if (e < 0) {
3484 log_syntax(unit, LOG_WARNING, filename, line, e, "Failed to parse error number, ignoring: %s", rvalue);
3485 return 0;
3486 }
3487 if (e == 0) {
3488 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid error number, ignoring: %s", rvalue);
3489 return 0;
3490 }
3491
3492 c->syscall_errno = e;
3493 return 0;
3494 }
3495
3496 int config_parse_address_families(
3497 const char *unit,
3498 const char *filename,
3499 unsigned line,
3500 const char *section,
3501 unsigned section_line,
3502 const char *lvalue,
3503 int ltype,
3504 const char *rvalue,
3505 void *data,
3506 void *userdata) {
3507
3508 ExecContext *c = data;
3509 bool invert = false;
3510 int r;
3511
3512 assert(filename);
3513 assert(lvalue);
3514 assert(rvalue);
3515
3516 if (isempty(rvalue)) {
3517 /* Empty assignment resets the list */
3518 c->address_families = set_free(c->address_families);
3519 c->address_families_allow_list = false;
3520 return 0;
3521 }
3522
3523 if (streq(rvalue, "none")) {
3524 /* Forbid all address families. */
3525 c->address_families = set_free(c->address_families);
3526 c->address_families_allow_list = true;
3527 return 0;
3528 }
3529
3530 if (rvalue[0] == '~') {
3531 invert = true;
3532 rvalue++;
3533 }
3534
3535 if (!c->address_families) {
3536 c->address_families = set_new(NULL);
3537 if (!c->address_families)
3538 return log_oom();
3539
3540 c->address_families_allow_list = !invert;
3541 }
3542
3543 for (const char *p = rvalue;;) {
3544 _cleanup_free_ char *word = NULL;
3545 int af;
3546
3547 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3548 if (r == -ENOMEM)
3549 return log_oom();
3550 if (r < 0) {
3551 log_syntax(unit, LOG_WARNING, filename, line, r,
3552 "Invalid syntax, ignoring: %s", rvalue);
3553 return 0;
3554 }
3555 if (r == 0)
3556 return 0;
3557
3558 af = af_from_name(word);
3559 if (af < 0) {
3560 log_syntax(unit, LOG_WARNING, filename, line, af,
3561 "Failed to parse address family, ignoring: %s", word);
3562 continue;
3563 }
3564
3565 /* If we previously wanted to forbid an address family and now
3566 * we want to allow it, then just remove it from the list.
3567 */
3568 if (!invert == c->address_families_allow_list) {
3569 r = set_put(c->address_families, INT_TO_PTR(af));
3570 if (r < 0)
3571 return log_oom();
3572 } else
3573 set_remove(c->address_families, INT_TO_PTR(af));
3574 }
3575 }
3576
3577 int config_parse_restrict_namespaces(
3578 const char *unit,
3579 const char *filename,
3580 unsigned line,
3581 const char *section,
3582 unsigned section_line,
3583 const char *lvalue,
3584 int ltype,
3585 const char *rvalue,
3586 void *data,
3587 void *userdata) {
3588
3589 ExecContext *c = data;
3590 unsigned long flags;
3591 bool invert = false;
3592 int r;
3593
3594 if (isempty(rvalue)) {
3595 /* Reset to the default. */
3596 c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL;
3597 return 0;
3598 }
3599
3600 /* Boolean parameter ignores the previous settings */
3601 r = parse_boolean(rvalue);
3602 if (r > 0) {
3603 c->restrict_namespaces = 0;
3604 return 0;
3605 } else if (r == 0) {
3606 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
3607 return 0;
3608 }
3609
3610 if (rvalue[0] == '~') {
3611 invert = true;
3612 rvalue++;
3613 }
3614
3615 /* Not a boolean argument, in this case it's a list of namespace types. */
3616 r = namespace_flags_from_string(rvalue, &flags);
3617 if (r < 0) {
3618 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
3619 return 0;
3620 }
3621
3622 if (c->restrict_namespaces == NAMESPACE_FLAGS_INITIAL)
3623 /* Initial assignment. Just set the value. */
3624 c->restrict_namespaces = invert ? (~flags) & NAMESPACE_FLAGS_ALL : flags;
3625 else
3626 /* Merge the value with the previous one. */
3627 SET_FLAG(c->restrict_namespaces, flags, !invert);
3628
3629 return 0;
3630 }
3631 #endif
3632
3633 int config_parse_restrict_filesystems(
3634 const char *unit,
3635 const char *filename,
3636 unsigned line,
3637 const char *section,
3638 unsigned section_line,
3639 const char *lvalue,
3640 int ltype,
3641 const char *rvalue,
3642 void *data,
3643 void *userdata) {
3644 ExecContext *c = ASSERT_PTR(data);
3645 bool invert = false;
3646 int r;
3647
3648 assert(filename);
3649 assert(lvalue);
3650 assert(rvalue);
3651
3652 if (isempty(rvalue)) {
3653 /* Empty assignment resets the list */
3654 c->restrict_filesystems = set_free(c->restrict_filesystems);
3655 c->restrict_filesystems_allow_list = false;
3656 return 0;
3657 }
3658
3659 if (rvalue[0] == '~') {
3660 invert = true;
3661 rvalue++;
3662 }
3663
3664 if (!c->restrict_filesystems) {
3665 if (invert)
3666 /* Allow everything but the ones listed */
3667 c->restrict_filesystems_allow_list = false;
3668 else
3669 /* Allow nothing but the ones listed */
3670 c->restrict_filesystems_allow_list = true;
3671 }
3672
3673 for (const char *p = rvalue;;) {
3674 _cleanup_free_ char *word = NULL;
3675
3676 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3677 if (r == 0)
3678 break;
3679 if (r == -ENOMEM)
3680 return log_oom();
3681 if (r < 0) {
3682 log_syntax(unit, LOG_WARNING, filename, line, r,
3683 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
3684 break;
3685 }
3686
3687 r = lsm_bpf_parse_filesystem(
3688 word,
3689 &c->restrict_filesystems,
3690 FILESYSTEM_PARSE_LOG|
3691 (invert ? FILESYSTEM_PARSE_INVERT : 0)|
3692 (c->restrict_filesystems_allow_list ? FILESYSTEM_PARSE_ALLOW_LIST : 0),
3693 unit, filename, line);
3694
3695 if (r < 0)
3696 return r;
3697 }
3698
3699 return 0;
3700 }
3701
3702 int config_parse_unit_slice(
3703 const char *unit,
3704 const char *filename,
3705 unsigned line,
3706 const char *section,
3707 unsigned section_line,
3708 const char *lvalue,
3709 int ltype,
3710 const char *rvalue,
3711 void *data,
3712 void *userdata) {
3713
3714 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3715 _cleanup_free_ char *k = NULL;
3716 Unit *u = userdata, *slice;
3717 int r;
3718
3719 assert(filename);
3720 assert(lvalue);
3721 assert(rvalue);
3722 assert(u);
3723
3724 r = unit_name_printf(u, rvalue, &k);
3725 if (r < 0) {
3726 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
3727 return 0;
3728 }
3729
3730 r = manager_load_unit(u->manager, k, NULL, &error, &slice);
3731 if (r < 0) {
3732 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to load slice unit %s, ignoring: %s", k, bus_error_message(&error, r));
3733 return 0;
3734 }
3735
3736 r = unit_set_slice(u, slice);
3737 if (r < 0) {
3738 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to assign slice %s to unit %s, ignoring: %m", slice->id, u->id);
3739 return 0;
3740 }
3741
3742 return 0;
3743 }
3744
3745 int config_parse_cpu_quota(
3746 const char *unit,
3747 const char *filename,
3748 unsigned line,
3749 const char *section,
3750 unsigned section_line,
3751 const char *lvalue,
3752 int ltype,
3753 const char *rvalue,
3754 void *data,
3755 void *userdata) {
3756
3757 CGroupContext *c = data;
3758 int r;
3759
3760 assert(filename);
3761 assert(lvalue);
3762 assert(rvalue);
3763
3764 if (isempty(rvalue)) {
3765 c->cpu_quota_per_sec_usec = USEC_INFINITY;
3766 return 0;
3767 }
3768
3769 r = parse_permyriad_unbounded(rvalue);
3770 if (r <= 0) {
3771 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid CPU quota '%s', ignoring.", rvalue);
3772 return 0;
3773 }
3774
3775 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 10000U;
3776 return 0;
3777 }
3778
3779 int config_parse_allowed_cpuset(
3780 const char *unit,
3781 const char *filename,
3782 unsigned line,
3783 const char *section,
3784 unsigned section_line,
3785 const char *lvalue,
3786 int ltype,
3787 const char *rvalue,
3788 void *data,
3789 void *userdata) {
3790
3791 CPUSet *c = data;
3792
3793 (void) parse_cpu_set_extend(rvalue, c, true, unit, filename, line, lvalue);
3794 return 0;
3795 }
3796
3797 int config_parse_memory_limit(
3798 const char *unit,
3799 const char *filename,
3800 unsigned line,
3801 const char *section,
3802 unsigned section_line,
3803 const char *lvalue,
3804 int ltype,
3805 const char *rvalue,
3806 void *data,
3807 void *userdata) {
3808
3809 CGroupContext *c = data;
3810 uint64_t bytes = CGROUP_LIMIT_MAX;
3811 int r;
3812
3813 if (isempty(rvalue) && STR_IN_SET(lvalue, "DefaultMemoryLow",
3814 "DefaultMemoryMin",
3815 "MemoryLow",
3816 "MemoryMin"))
3817 bytes = CGROUP_LIMIT_MIN;
3818 else if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
3819
3820 r = parse_permyriad(rvalue);
3821 if (r < 0) {
3822 r = parse_size(rvalue, 1024, &bytes);
3823 if (r < 0) {
3824 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid memory limit '%s', ignoring: %m", rvalue);
3825 return 0;
3826 }
3827 } else
3828 bytes = physical_memory_scale(r, 10000U);
3829
3830 if (bytes >= UINT64_MAX ||
3831 (bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) {
3832 log_syntax(unit, LOG_WARNING, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue);
3833 return 0;
3834 }
3835 }
3836
3837 if (streq(lvalue, "DefaultMemoryLow")) {
3838 c->default_memory_low = bytes;
3839 c->default_memory_low_set = true;
3840 } else if (streq(lvalue, "DefaultMemoryMin")) {
3841 c->default_memory_min = bytes;
3842 c->default_memory_min_set = true;
3843 } else if (streq(lvalue, "MemoryMin")) {
3844 c->memory_min = bytes;
3845 c->memory_min_set = true;
3846 } else if (streq(lvalue, "MemoryLow")) {
3847 c->memory_low = bytes;
3848 c->memory_low_set = true;
3849 } else if (streq(lvalue, "MemoryHigh"))
3850 c->memory_high = bytes;
3851 else if (streq(lvalue, "MemoryMax"))
3852 c->memory_max = bytes;
3853 else if (streq(lvalue, "MemorySwapMax"))
3854 c->memory_swap_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_strv_free_ char **symlinks = NULL;
4633 if (!isempty(dest)) {
4634 _cleanup_free_ char *dresolved = NULL;
4635
4636 if (streq(lvalue, "ConfigurationDirectory")) {
4637 log_syntax(unit, LOG_WARNING, filename, line, 0,
4638 "Destination parameter is not supported for ConfigurationDirectory, ignoring: %s", tuple);
4639 continue;
4640 }
4641
4642 r = unit_path_printf(u, dest, &dresolved);
4643 if (r < 0) {
4644 log_syntax(unit, LOG_WARNING, filename, line, r,
4645 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", dest);
4646 continue;
4647 }
4648
4649 r = path_simplify_and_warn(dresolved, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
4650 if (r < 0)
4651 continue;
4652
4653 r = strv_consume(&symlinks, TAKE_PTR(dresolved));
4654 if (r < 0)
4655 return log_oom();
4656 }
4657
4658 r = exec_directory_add(&ed->items, &ed->n_items, sresolved, symlinks);
4659 if (r < 0)
4660 return log_oom();
4661 }
4662 }
4663
4664 int config_parse_set_credential(
4665 const char *unit,
4666 const char *filename,
4667 unsigned line,
4668 const char *section,
4669 unsigned section_line,
4670 const char *lvalue,
4671 int ltype,
4672 const char *rvalue,
4673 void *data,
4674 void *userdata) {
4675
4676 _cleanup_free_ char *word = NULL, *k = NULL;
4677 _cleanup_free_ void *d = NULL;
4678 ExecContext *context = ASSERT_PTR(data);
4679 ExecSetCredential *old;
4680 Unit *u = userdata;
4681 bool encrypted = ltype;
4682 const char *p = ASSERT_PTR(rvalue);
4683 size_t size;
4684 int r;
4685
4686 assert(filename);
4687 assert(lvalue);
4688
4689 if (isempty(rvalue)) {
4690 /* Empty assignment resets the list */
4691 context->set_credentials = hashmap_free(context->set_credentials);
4692 return 0;
4693 }
4694
4695 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4696 if (r == -ENOMEM)
4697 return log_oom();
4698 if (r < 0) {
4699 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract credential name, ignoring: %s", rvalue);
4700 return 0;
4701 }
4702 if (r == 0 || isempty(p)) {
4703 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid syntax, ignoring: %s", rvalue);
4704 return 0;
4705 }
4706
4707 r = unit_cred_printf(u, word, &k);
4708 if (r < 0) {
4709 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4710 return 0;
4711 }
4712 if (!credential_name_valid(k)) {
4713 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", k);
4714 return 0;
4715 }
4716
4717 if (encrypted) {
4718 r = unbase64mem_full(p, SIZE_MAX, true, &d, &size);
4719 if (r < 0) {
4720 log_syntax(unit, LOG_WARNING, filename, line, r, "Encrypted credential data not valid Base64 data, ignoring.");
4721 return 0;
4722 }
4723 } else {
4724 char *unescaped;
4725 ssize_t l;
4726
4727 /* We support escape codes here, so that users can insert trailing \n if they like */
4728 l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
4729 if (l < 0) {
4730 log_syntax(unit, LOG_WARNING, filename, line, l, "Can't unescape \"%s\", ignoring: %m", p);
4731 return 0;
4732 }
4733
4734 d = unescaped;
4735 size = l;
4736 }
4737
4738 old = hashmap_get(context->set_credentials, k);
4739 if (old) {
4740 free_and_replace(old->data, d);
4741 old->size = size;
4742 old->encrypted = encrypted;
4743 } else {
4744 _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL;
4745
4746 sc = new(ExecSetCredential, 1);
4747 if (!sc)
4748 return log_oom();
4749
4750 *sc = (ExecSetCredential) {
4751 .id = TAKE_PTR(k),
4752 .data = TAKE_PTR(d),
4753 .size = size,
4754 .encrypted = encrypted,
4755 };
4756
4757 r = hashmap_ensure_put(&context->set_credentials, &exec_set_credential_hash_ops, sc->id, sc);
4758 if (r == -ENOMEM)
4759 return log_oom();
4760 if (r < 0) {
4761 log_syntax(unit, LOG_WARNING, filename, line, r,
4762 "Duplicated credential value '%s', ignoring assignment: %s", sc->id, rvalue);
4763 return 0;
4764 }
4765
4766 TAKE_PTR(sc);
4767 }
4768
4769 return 0;
4770 }
4771
4772 int config_parse_load_credential(
4773 const char *unit,
4774 const char *filename,
4775 unsigned line,
4776 const char *section,
4777 unsigned section_line,
4778 const char *lvalue,
4779 int ltype,
4780 const char *rvalue,
4781 void *data,
4782 void *userdata) {
4783
4784 _cleanup_free_ char *word = NULL, *k = NULL, *q = NULL;
4785 ExecContext *context = ASSERT_PTR(data);
4786 ExecLoadCredential *old;
4787 bool encrypted = ltype;
4788 Unit *u = userdata;
4789 const char *p;
4790 int r;
4791
4792 assert(filename);
4793 assert(lvalue);
4794 assert(rvalue);
4795
4796 if (isempty(rvalue)) {
4797 /* Empty assignment resets the list */
4798 context->load_credentials = hashmap_free(context->load_credentials);
4799 return 0;
4800 }
4801
4802 p = rvalue;
4803 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4804 if (r == -ENOMEM)
4805 return log_oom();
4806 if (r <= 0) {
4807 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
4808 return 0;
4809 }
4810
4811 r = unit_cred_printf(u, word, &k);
4812 if (r < 0) {
4813 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4814 return 0;
4815 }
4816 if (!credential_name_valid(k)) {
4817 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", k);
4818 return 0;
4819 }
4820
4821 if (isempty(p)) {
4822 /* If only one field is specified take it as shortcut for inheriting a credential named
4823 * the same way from our parent */
4824 q = strdup(k);
4825 if (!q)
4826 return log_oom();
4827 } else {
4828 r = unit_path_printf(u, p, &q);
4829 if (r < 0) {
4830 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", p);
4831 return 0;
4832 }
4833 if (path_is_absolute(q) ? !path_is_normalized(q) : !credential_name_valid(q)) {
4834 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential source \"%s\" not valid, ignoring.", q);
4835 return 0;
4836 }
4837 }
4838
4839 old = hashmap_get(context->load_credentials, k);
4840 if (old) {
4841 free_and_replace(old->path, q);
4842 old->encrypted = encrypted;
4843 } else {
4844 _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
4845
4846 lc = new(ExecLoadCredential, 1);
4847 if (!lc)
4848 return log_oom();
4849
4850 *lc = (ExecLoadCredential) {
4851 .id = TAKE_PTR(k),
4852 .path = TAKE_PTR(q),
4853 .encrypted = encrypted,
4854 };
4855
4856 r = hashmap_ensure_put(&context->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
4857 if (r == -ENOMEM)
4858 return log_oom();
4859 if (r < 0) {
4860 log_syntax(unit, LOG_WARNING, filename, line, r,
4861 "Duplicated credential value '%s', ignoring assignment: %s", lc->id, rvalue);
4862 return 0;
4863 }
4864
4865 TAKE_PTR(lc);
4866 }
4867
4868 return 0;
4869 }
4870
4871 int config_parse_set_status(
4872 const char *unit,
4873 const char *filename,
4874 unsigned line,
4875 const char *section,
4876 unsigned section_line,
4877 const char *lvalue,
4878 int ltype,
4879 const char *rvalue,
4880 void *data,
4881 void *userdata) {
4882
4883 ExitStatusSet *status_set = ASSERT_PTR(data);
4884 int r;
4885
4886 assert(filename);
4887 assert(lvalue);
4888 assert(rvalue);
4889
4890 /* Empty assignment resets the list */
4891 if (isempty(rvalue)) {
4892 exit_status_set_free(status_set);
4893 return 0;
4894 }
4895
4896 for (const char *p = rvalue;;) {
4897 _cleanup_free_ char *word = NULL;
4898 Bitmap *bitmap;
4899
4900 r = extract_first_word(&p, &word, NULL, 0);
4901 if (r == -ENOMEM)
4902 return log_oom();
4903 if (r < 0) {
4904 log_syntax(unit, LOG_WARNING, filename, line, r,
4905 "Failed to parse %s=%s, ignoring: %m", lvalue, rvalue);
4906 return 0;
4907 }
4908 if (r == 0)
4909 return 0;
4910
4911 /* We need to call exit_status_from_string() first, because we want
4912 * to parse numbers as exit statuses, not signals. */
4913
4914 r = exit_status_from_string(word);
4915 if (r >= 0) {
4916 assert(r >= 0 && r < 256);
4917 bitmap = &status_set->status;
4918 } else {
4919 r = signal_from_string(word);
4920 if (r < 0) {
4921 log_syntax(unit, LOG_WARNING, filename, line, r,
4922 "Failed to parse value, ignoring: %s", word);
4923 continue;
4924 }
4925 bitmap = &status_set->signal;
4926 }
4927
4928 r = bitmap_set(bitmap, r);
4929 if (r < 0)
4930 log_syntax(unit, LOG_WARNING, filename, line, r,
4931 "Failed to set signal or status %s, ignoring: %m", word);
4932 }
4933 }
4934
4935 int config_parse_namespace_path_strv(
4936 const char *unit,
4937 const char *filename,
4938 unsigned line,
4939 const char *section,
4940 unsigned section_line,
4941 const char *lvalue,
4942 int ltype,
4943 const char *rvalue,
4944 void *data,
4945 void *userdata) {
4946
4947 const Unit *u = userdata;
4948 char*** sv = ASSERT_PTR(data);
4949 int r;
4950
4951 assert(filename);
4952 assert(lvalue);
4953 assert(rvalue);
4954
4955 if (isempty(rvalue)) {
4956 /* Empty assignment resets the list */
4957 *sv = strv_free(*sv);
4958 return 0;
4959 }
4960
4961 for (const char *p = rvalue;;) {
4962 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
4963 const char *w;
4964 bool ignore_enoent = false, shall_prefix = false;
4965
4966 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
4967 if (r == -ENOMEM)
4968 return log_oom();
4969 if (r < 0) {
4970 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
4971 return 0;
4972 }
4973 if (r == 0)
4974 break;
4975
4976 w = word;
4977 if (startswith(w, "-")) {
4978 ignore_enoent = true;
4979 w++;
4980 }
4981 if (startswith(w, "+")) {
4982 shall_prefix = true;
4983 w++;
4984 }
4985
4986 r = unit_path_printf(u, w, &resolved);
4987 if (r < 0) {
4988 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s: %m", w);
4989 continue;
4990 }
4991
4992 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4993 if (r < 0)
4994 continue;
4995
4996 joined = strjoin(ignore_enoent ? "-" : "",
4997 shall_prefix ? "+" : "",
4998 resolved);
4999
5000 r = strv_push(sv, joined);
5001 if (r < 0)
5002 return log_oom();
5003
5004 joined = NULL;
5005 }
5006
5007 return 0;
5008 }
5009
5010 int config_parse_temporary_filesystems(
5011 const char *unit,
5012 const char *filename,
5013 unsigned line,
5014 const char *section,
5015 unsigned section_line,
5016 const char *lvalue,
5017 int ltype,
5018 const char *rvalue,
5019 void *data,
5020 void *userdata) {
5021
5022 const Unit *u = userdata;
5023 ExecContext *c = ASSERT_PTR(data);
5024 int r;
5025
5026 assert(filename);
5027 assert(lvalue);
5028 assert(rvalue);
5029
5030 if (isempty(rvalue)) {
5031 /* Empty assignment resets the list */
5032 temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
5033 c->temporary_filesystems = NULL;
5034 c->n_temporary_filesystems = 0;
5035 return 0;
5036 }
5037
5038 for (const char *p = rvalue;;) {
5039 _cleanup_free_ char *word = NULL, *path = NULL, *resolved = NULL;
5040 const char *w;
5041
5042 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
5043 if (r == -ENOMEM)
5044 return log_oom();
5045 if (r < 0) {
5046 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
5047 return 0;
5048 }
5049 if (r == 0)
5050 return 0;
5051
5052 w = word;
5053 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
5054 if (r == -ENOMEM)
5055 return log_oom();
5056 if (r < 0) {
5057 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", word);
5058 continue;
5059 }
5060 if (r == 0) {
5061 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid syntax, ignoring: %s", word);
5062 continue;
5063 }
5064
5065 r = unit_path_printf(u, path, &resolved);
5066 if (r < 0) {
5067 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", path);
5068 continue;
5069 }
5070
5071 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5072 if (r < 0)
5073 continue;
5074
5075 r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, resolved, w);
5076 if (r < 0)
5077 return log_oom();
5078 }
5079 }
5080
5081 int config_parse_bind_paths(
5082 const char *unit,
5083 const char *filename,
5084 unsigned line,
5085 const char *section,
5086 unsigned section_line,
5087 const char *lvalue,
5088 int ltype,
5089 const char *rvalue,
5090 void *data,
5091 void *userdata) {
5092
5093 ExecContext *c = ASSERT_PTR(data);
5094 const Unit *u = userdata;
5095 int r;
5096
5097 assert(filename);
5098 assert(lvalue);
5099 assert(rvalue);
5100
5101 if (isempty(rvalue)) {
5102 /* Empty assignment resets the list */
5103 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
5104 c->bind_mounts = NULL;
5105 c->n_bind_mounts = 0;
5106 return 0;
5107 }
5108
5109 for (const char *p = rvalue;;) {
5110 _cleanup_free_ char *source = NULL, *destination = NULL;
5111 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
5112 char *s = NULL, *d = NULL;
5113 bool rbind = true, ignore_enoent = false;
5114
5115 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
5116 if (r == -ENOMEM)
5117 return log_oom();
5118 if (r < 0) {
5119 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
5120 return 0;
5121 }
5122 if (r == 0)
5123 break;
5124
5125 r = unit_full_printf_full(u, source, PATH_MAX, &sresolved);
5126 if (r < 0) {
5127 log_syntax(unit, LOG_WARNING, filename, line, r,
5128 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", source);
5129 continue;
5130 }
5131
5132 s = sresolved;
5133 if (s[0] == '-') {
5134 ignore_enoent = true;
5135 s++;
5136 }
5137
5138 r = path_simplify_and_warn(s, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5139 if (r < 0)
5140 continue;
5141
5142 /* Optionally, the destination is specified. */
5143 if (p && p[-1] == ':') {
5144 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
5145 if (r == -ENOMEM)
5146 return log_oom();
5147 if (r < 0) {
5148 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
5149 return 0;
5150 }
5151 if (r == 0) {
5152 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing argument after ':', ignoring: %s", s);
5153 continue;
5154 }
5155
5156 r = unit_path_printf(u, destination, &dresolved);
5157 if (r < 0) {
5158 log_syntax(unit, LOG_WARNING, filename, line, r,
5159 "Failed to resolve specifiers in \"%s\", ignoring: %m", destination);
5160 continue;
5161 }
5162
5163 r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5164 if (r < 0)
5165 continue;
5166
5167 d = dresolved;
5168
5169 /* Optionally, there's also a short option string specified */
5170 if (p && p[-1] == ':') {
5171 _cleanup_free_ char *options = NULL;
5172
5173 r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
5174 if (r == -ENOMEM)
5175 return log_oom();
5176 if (r < 0) {
5177 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
5178 return 0;
5179 }
5180
5181 if (isempty(options) || streq(options, "rbind"))
5182 rbind = true;
5183 else if (streq(options, "norbind"))
5184 rbind = false;
5185 else {
5186 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
5187 continue;
5188 }
5189 }
5190 } else
5191 d = s;
5192
5193 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
5194 &(BindMount) {
5195 .source = s,
5196 .destination = d,
5197 .read_only = !!strstr(lvalue, "ReadOnly"),
5198 .recursive = rbind,
5199 .ignore_enoent = ignore_enoent,
5200 });
5201 if (r < 0)
5202 return log_oom();
5203 }
5204
5205 return 0;
5206 }
5207
5208 int config_parse_mount_images(
5209 const char *unit,
5210 const char *filename,
5211 unsigned line,
5212 const char *section,
5213 unsigned section_line,
5214 const char *lvalue,
5215 int ltype,
5216 const char *rvalue,
5217 void *data,
5218 void *userdata) {
5219
5220 ExecContext *c = ASSERT_PTR(data);
5221 const Unit *u = userdata;
5222 int r;
5223
5224 assert(filename);
5225 assert(lvalue);
5226 assert(rvalue);
5227
5228 if (isempty(rvalue)) {
5229 /* Empty assignment resets the list */
5230 c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
5231 return 0;
5232 }
5233
5234 for (const char *p = rvalue;;) {
5235 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
5236 _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
5237 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
5238 const char *q = NULL;
5239 char *s = NULL;
5240 bool permissive = false;
5241
5242 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
5243 if (r == -ENOMEM)
5244 return log_oom();
5245 if (r < 0) {
5246 log_syntax(unit, LOG_WARNING, filename, line, r,
5247 "Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
5248 return 0;
5249 }
5250 if (r == 0)
5251 return 0;
5252
5253 q = tuple;
5254 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
5255 if (r == -ENOMEM)
5256 return log_oom();
5257 if (r < 0) {
5258 log_syntax(unit, LOG_WARNING, filename, line, r,
5259 "Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
5260 return 0;
5261 }
5262 if (r == 0)
5263 continue;
5264
5265 s = first;
5266 if (s[0] == '-') {
5267 permissive = true;
5268 s++;
5269 }
5270
5271 r = unit_path_printf(u, s, &sresolved);
5272 if (r < 0) {
5273 log_syntax(unit, LOG_WARNING, filename, line, r,
5274 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", s);
5275 continue;
5276 }
5277
5278 r = path_simplify_and_warn(sresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5279 if (r < 0)
5280 continue;
5281
5282 if (isempty(second)) {
5283 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing destination in %s, ignoring: %s", lvalue, rvalue);
5284 continue;
5285 }
5286
5287 r = unit_path_printf(u, second, &dresolved);
5288 if (r < 0) {
5289 log_syntax(unit, LOG_WARNING, filename, line, r,
5290 "Failed to resolve specifiers in \"%s\", ignoring: %m", second);
5291 continue;
5292 }
5293
5294 r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5295 if (r < 0)
5296 continue;
5297
5298 for (;;) {
5299 _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
5300 MountOptions *o = NULL;
5301 PartitionDesignator partition_designator;
5302
5303 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
5304 if (r == -ENOMEM)
5305 return log_oom();
5306 if (r < 0) {
5307 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", q);
5308 return 0;
5309 }
5310 if (r == 0)
5311 break;
5312 /* Single set of options, applying to the root partition/single filesystem */
5313 if (r == 1) {
5314 r = unit_full_printf(u, partition, &mount_options_resolved);
5315 if (r < 0) {
5316 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", first);
5317 continue;
5318 }
5319
5320 o = new(MountOptions, 1);
5321 if (!o)
5322 return log_oom();
5323 *o = (MountOptions) {
5324 .partition_designator = PARTITION_ROOT,
5325 .options = TAKE_PTR(mount_options_resolved),
5326 };
5327 LIST_APPEND(mount_options, options, o);
5328
5329 break;
5330 }
5331
5332 partition_designator = partition_designator_from_string(partition);
5333 if (partition_designator < 0) {
5334 log_syntax(unit, LOG_WARNING, filename, line, partition_designator,
5335 "Invalid partition name %s, ignoring", partition);
5336 continue;
5337 }
5338 r = unit_full_printf(u, mount_options, &mount_options_resolved);
5339 if (r < 0) {
5340 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
5341 continue;
5342 }
5343
5344 o = new(MountOptions, 1);
5345 if (!o)
5346 return log_oom();
5347 *o = (MountOptions) {
5348 .partition_designator = partition_designator,
5349 .options = TAKE_PTR(mount_options_resolved),
5350 };
5351 LIST_APPEND(mount_options, options, o);
5352 }
5353
5354 r = mount_image_add(&c->mount_images, &c->n_mount_images,
5355 &(MountImage) {
5356 .source = sresolved,
5357 .destination = dresolved,
5358 .mount_options = options,
5359 .ignore_enoent = permissive,
5360 .type = MOUNT_IMAGE_DISCRETE,
5361 });
5362 if (r < 0)
5363 return log_oom();
5364 }
5365 }
5366
5367 int config_parse_extension_images(
5368 const char *unit,
5369 const char *filename,
5370 unsigned line,
5371 const char *section,
5372 unsigned section_line,
5373 const char *lvalue,
5374 int ltype,
5375 const char *rvalue,
5376 void *data,
5377 void *userdata) {
5378
5379 ExecContext *c = ASSERT_PTR(data);
5380 const Unit *u = userdata;
5381 int r;
5382
5383 assert(filename);
5384 assert(lvalue);
5385 assert(rvalue);
5386
5387 if (isempty(rvalue)) {
5388 /* Empty assignment resets the list */
5389 c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
5390 return 0;
5391 }
5392
5393 for (const char *p = rvalue;;) {
5394 _cleanup_free_ char *source = NULL, *tuple = NULL, *sresolved = NULL;
5395 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
5396 bool permissive = false;
5397 const char *q = NULL;
5398 char *s = NULL;
5399
5400 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
5401 if (r == -ENOMEM)
5402 return log_oom();
5403 if (r < 0) {
5404 log_syntax(unit, LOG_WARNING, filename, line, r,
5405 "Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
5406 return 0;
5407 }
5408 if (r == 0)
5409 return 0;
5410
5411 q = tuple;
5412 r = extract_first_word(&q, &source, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS);
5413 if (r == -ENOMEM)
5414 return log_oom();
5415 if (r < 0) {
5416 log_syntax(unit, LOG_WARNING, filename, line, r,
5417 "Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
5418 return 0;
5419 }
5420 if (r == 0)
5421 continue;
5422
5423 s = source;
5424 if (s[0] == '-') {
5425 permissive = true;
5426 s++;
5427 }
5428
5429 r = unit_path_printf(u, s, &sresolved);
5430 if (r < 0) {
5431 log_syntax(unit, LOG_WARNING, filename, line, r,
5432 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", s);
5433 continue;
5434 }
5435
5436 r = path_simplify_and_warn(sresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5437 if (r < 0)
5438 continue;
5439
5440 for (;;) {
5441 _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
5442 MountOptions *o = NULL;
5443 PartitionDesignator partition_designator;
5444
5445 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
5446 if (r == -ENOMEM)
5447 return log_oom();
5448 if (r < 0) {
5449 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", q);
5450 return 0;
5451 }
5452 if (r == 0)
5453 break;
5454 /* Single set of options, applying to the root partition/single filesystem */
5455 if (r == 1) {
5456 r = unit_full_printf(u, partition, &mount_options_resolved);
5457 if (r < 0) {
5458 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", partition);
5459 continue;
5460 }
5461
5462 o = new(MountOptions, 1);
5463 if (!o)
5464 return log_oom();
5465 *o = (MountOptions) {
5466 .partition_designator = PARTITION_ROOT,
5467 .options = TAKE_PTR(mount_options_resolved),
5468 };
5469 LIST_APPEND(mount_options, options, o);
5470
5471 break;
5472 }
5473
5474 partition_designator = partition_designator_from_string(partition);
5475 if (partition_designator < 0) {
5476 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid partition name %s, ignoring", partition);
5477 continue;
5478 }
5479 r = unit_full_printf(u, mount_options, &mount_options_resolved);
5480 if (r < 0) {
5481 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
5482 continue;
5483 }
5484
5485 o = new(MountOptions, 1);
5486 if (!o)
5487 return log_oom();
5488 *o = (MountOptions) {
5489 .partition_designator = partition_designator,
5490 .options = TAKE_PTR(mount_options_resolved),
5491 };
5492 LIST_APPEND(mount_options, options, o);
5493 }
5494
5495 r = mount_image_add(&c->extension_images, &c->n_extension_images,
5496 &(MountImage) {
5497 .source = sresolved,
5498 .mount_options = options,
5499 .ignore_enoent = permissive,
5500 .type = MOUNT_IMAGE_EXTENSION,
5501 });
5502 if (r < 0)
5503 return log_oom();
5504 }
5505 }
5506
5507 int config_parse_job_timeout_sec(
5508 const char* unit,
5509 const char *filename,
5510 unsigned line,
5511 const char *section,
5512 unsigned section_line,
5513 const char *lvalue,
5514 int ltype,
5515 const char *rvalue,
5516 void *data,
5517 void *userdata) {
5518
5519 Unit *u = ASSERT_PTR(data);
5520 usec_t usec;
5521 int r;
5522
5523 assert(filename);
5524 assert(lvalue);
5525 assert(rvalue);
5526
5527 r = parse_sec_fix_0(rvalue, &usec);
5528 if (r < 0) {
5529 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue);
5530 return 0;
5531 }
5532
5533 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
5534 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
5535 * count. */
5536
5537 if (!u->job_running_timeout_set)
5538 u->job_running_timeout = usec;
5539
5540 u->job_timeout = usec;
5541
5542 return 0;
5543 }
5544
5545 int config_parse_job_running_timeout_sec(
5546 const char* unit,
5547 const char *filename,
5548 unsigned line,
5549 const char *section,
5550 unsigned section_line,
5551 const char *lvalue,
5552 int ltype,
5553 const char *rvalue,
5554 void *data,
5555 void *userdata) {
5556
5557 Unit *u = ASSERT_PTR(data);
5558 usec_t usec;
5559 int r;
5560
5561 assert(filename);
5562 assert(lvalue);
5563 assert(rvalue);
5564
5565 r = parse_sec_fix_0(rvalue, &usec);
5566 if (r < 0) {
5567 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue);
5568 return 0;
5569 }
5570
5571 u->job_running_timeout = usec;
5572 u->job_running_timeout_set = true;
5573
5574 return 0;
5575 }
5576
5577 int config_parse_emergency_action(
5578 const char* unit,
5579 const char *filename,
5580 unsigned line,
5581 const char *section,
5582 unsigned section_line,
5583 const char *lvalue,
5584 int ltype,
5585 const char *rvalue,
5586 void *data,
5587 void *userdata) {
5588
5589 Manager *m = NULL;
5590 EmergencyAction *x = ASSERT_PTR(data);
5591 int r;
5592
5593 assert(filename);
5594 assert(lvalue);
5595 assert(rvalue);
5596
5597 if (unit)
5598 m = ((Unit*) userdata)->manager;
5599 else
5600 m = data;
5601
5602 r = parse_emergency_action(rvalue, MANAGER_IS_SYSTEM(m), x);
5603 if (r < 0) {
5604 if (r == -EOPNOTSUPP && MANAGER_IS_USER(m)) {
5605 /* Compat mode: remove for systemd 241. */
5606
5607 log_syntax(unit, LOG_INFO, filename, line, r,
5608 "%s= in user mode specified as \"%s\", using \"exit-force\" instead.",
5609 lvalue, rvalue);
5610 *x = EMERGENCY_ACTION_EXIT_FORCE;
5611 return 0;
5612 }
5613
5614 if (r == -EOPNOTSUPP)
5615 log_syntax(unit, LOG_WARNING, filename, line, r,
5616 "%s= specified as %s mode action, ignoring: %s",
5617 lvalue, MANAGER_IS_SYSTEM(m) ? "user" : "system", rvalue);
5618 else
5619 log_syntax(unit, LOG_WARNING, filename, line, r,
5620 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
5621 return 0;
5622 }
5623
5624 return 0;
5625 }
5626
5627 int config_parse_pid_file(
5628 const char *unit,
5629 const char *filename,
5630 unsigned line,
5631 const char *section,
5632 unsigned section_line,
5633 const char *lvalue,
5634 int ltype,
5635 const char *rvalue,
5636 void *data,
5637 void *userdata) {
5638
5639 _cleanup_free_ char *k = NULL, *n = NULL;
5640 const Unit *u = ASSERT_PTR(userdata);
5641 char **s = data;
5642 int r;
5643
5644 assert(filename);
5645 assert(lvalue);
5646 assert(rvalue);
5647
5648 if (isempty(rvalue)) {
5649 /* An empty assignment removes already set value. */
5650 *s = mfree(*s);
5651 return 0;
5652 }
5653
5654 r = unit_path_printf(u, rvalue, &k);
5655 if (r < 0) {
5656 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
5657 return 0;
5658 }
5659
5660 /* If this is a relative path make it absolute by prefixing the /run */
5661 n = path_make_absolute(k, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
5662 if (!n)
5663 return log_oom();
5664
5665 /* Check that the result is a sensible path */
5666 r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5667 if (r < 0)
5668 return r;
5669
5670 r = patch_var_run(unit, filename, line, lvalue, &n);
5671 if (r < 0)
5672 return r;
5673
5674 free_and_replace(*s, n);
5675 return 0;
5676 }
5677
5678 int config_parse_exit_status(
5679 const char *unit,
5680 const char *filename,
5681 unsigned line,
5682 const char *section,
5683 unsigned section_line,
5684 const char *lvalue,
5685 int ltype,
5686 const char *rvalue,
5687 void *data,
5688 void *userdata) {
5689
5690 int *exit_status = data, r;
5691 uint8_t u;
5692
5693 assert(filename);
5694 assert(lvalue);
5695 assert(rvalue);
5696 assert(exit_status);
5697
5698 if (isempty(rvalue)) {
5699 *exit_status = -1;
5700 return 0;
5701 }
5702
5703 r = safe_atou8(rvalue, &u);
5704 if (r < 0) {
5705 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse exit status '%s', ignoring: %m", rvalue);
5706 return 0;
5707 }
5708
5709 *exit_status = u;
5710 return 0;
5711 }
5712
5713 int config_parse_disable_controllers(
5714 const char *unit,
5715 const char *filename,
5716 unsigned line,
5717 const char *section,
5718 unsigned section_line,
5719 const char *lvalue,
5720 int ltype,
5721 const char *rvalue,
5722 void *data,
5723 void *userdata) {
5724
5725 int r;
5726 CGroupContext *c = data;
5727 CGroupMask disabled_mask;
5728
5729 /* 1. If empty, make all controllers eligible for use again.
5730 * 2. If non-empty, merge all listed controllers, space separated. */
5731
5732 if (isempty(rvalue)) {
5733 c->disable_controllers = 0;
5734 return 0;
5735 }
5736
5737 r = cg_mask_from_string(rvalue, &disabled_mask);
5738 if (r < 0 || disabled_mask <= 0) {
5739 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid cgroup string: %s, ignoring", rvalue);
5740 return 0;
5741 }
5742
5743 c->disable_controllers |= disabled_mask;
5744
5745 return 0;
5746 }
5747
5748 int config_parse_ip_filter_bpf_progs(
5749 const char *unit,
5750 const char *filename,
5751 unsigned line,
5752 const char *section,
5753 unsigned section_line,
5754 const char *lvalue,
5755 int ltype,
5756 const char *rvalue,
5757 void *data,
5758 void *userdata) {
5759
5760 _cleanup_free_ char *resolved = NULL;
5761 const Unit *u = userdata;
5762 char ***paths = ASSERT_PTR(data);
5763 int r;
5764
5765 assert(filename);
5766 assert(lvalue);
5767 assert(rvalue);
5768
5769 if (isempty(rvalue)) {
5770 *paths = strv_free(*paths);
5771 return 0;
5772 }
5773
5774 r = unit_path_printf(u, rvalue, &resolved);
5775 if (r < 0) {
5776 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
5777 return 0;
5778 }
5779
5780 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5781 if (r < 0)
5782 return 0;
5783
5784 if (strv_contains(*paths, resolved))
5785 return 0;
5786
5787 r = strv_extend(paths, resolved);
5788 if (r < 0)
5789 return log_oom();
5790
5791 r = bpf_firewall_supported();
5792 if (r < 0)
5793 return r;
5794 if (r != BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
5795 static bool warned = false;
5796
5797 log_full(warned ? LOG_DEBUG : LOG_WARNING,
5798 "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"
5799 "Starting this unit will fail! (This warning is only shown for the first loaded unit using IP firewalling.)", filename, line, lvalue, rvalue);
5800
5801 warned = true;
5802 }
5803
5804 return 0;
5805 }
5806
5807 int config_parse_bpf_foreign_program(
5808 const char *unit,
5809 const char *filename,
5810 unsigned line,
5811 const char *section,
5812 unsigned section_line,
5813 const char *lvalue,
5814 int ltype,
5815 const char *rvalue,
5816 void *data,
5817 void *userdata) {
5818 _cleanup_free_ char *resolved = NULL, *word = NULL;
5819 CGroupContext *c = data;
5820 const char *p = ASSERT_PTR(rvalue);
5821 Unit *u = userdata;
5822 int attach_type, r;
5823
5824 assert(filename);
5825 assert(lvalue);
5826
5827 if (isempty(rvalue)) {
5828 while (c->bpf_foreign_programs)
5829 cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
5830
5831 return 0;
5832 }
5833
5834 r = extract_first_word(&p, &word, ":", 0);
5835 if (r == -ENOMEM)
5836 return log_oom();
5837 if (r < 0) {
5838 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse foreign BPF program, ignoring: %s", rvalue);
5839 return 0;
5840 }
5841 if (r == 0 || isempty(p)) {
5842 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid syntax in %s=, ignoring: %s", lvalue, rvalue);
5843 return 0;
5844 }
5845
5846 attach_type = bpf_cgroup_attach_type_from_string(word);
5847 if (attach_type < 0) {
5848 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown BPF attach type=%s, ignoring: %s", word, rvalue);
5849 return 0;
5850 }
5851
5852 r = unit_path_printf(u, p, &resolved);
5853 if (r < 0) {
5854 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %s", p, rvalue);
5855 return 0;
5856 }
5857
5858 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5859 if (r < 0)
5860 return 0;
5861
5862 r = cgroup_add_bpf_foreign_program(c, attach_type, resolved);
5863 if (r < 0)
5864 return log_error_errno(r, "Failed to add foreign BPF program to cgroup context: %m");
5865
5866 return 0;
5867 }
5868
5869 int config_parse_cgroup_socket_bind(
5870 const char *unit,
5871 const char *filename,
5872 unsigned line,
5873 const char *section,
5874 unsigned section_line,
5875 const char *lvalue,
5876 int ltype,
5877 const char *rvalue,
5878 void *data,
5879 void *userdata) {
5880 _cleanup_free_ CGroupSocketBindItem *item = NULL;
5881 CGroupSocketBindItem **head = data;
5882 uint16_t nr_ports, port_min;
5883 int af, ip_protocol, r;
5884
5885 if (isempty(rvalue)) {
5886 cgroup_context_remove_socket_bind(head);
5887 return 0;
5888 }
5889
5890 r = parse_socket_bind_item(rvalue, &af, &ip_protocol, &nr_ports, &port_min);
5891 if (r == -ENOMEM)
5892 return log_oom();
5893 if (r < 0) {
5894 log_syntax(unit, LOG_WARNING, filename, line, r,
5895 "Unable to parse %s= assignment, ignoring: %s", lvalue, rvalue);
5896 return 0;
5897 }
5898
5899 item = new(CGroupSocketBindItem, 1);
5900 if (!item)
5901 return log_oom();
5902 *item = (CGroupSocketBindItem) {
5903 .address_family = af,
5904 .ip_protocol = ip_protocol,
5905 .nr_ports = nr_ports,
5906 .port_min = port_min,
5907 };
5908
5909 LIST_PREPEND(socket_bind_items, *head, TAKE_PTR(item));
5910
5911 return 0;
5912 }
5913
5914 int config_parse_restrict_network_interfaces(
5915 const char *unit,
5916 const char *filename,
5917 unsigned line,
5918 const char *section,
5919 unsigned section_line,
5920 const char *lvalue,
5921 int ltype,
5922 const char *rvalue,
5923 void *data,
5924 void *userdata) {
5925 CGroupContext *c = ASSERT_PTR(data);
5926 bool is_allow_rule = true;
5927 int r;
5928
5929 assert(filename);
5930 assert(lvalue);
5931 assert(rvalue);
5932
5933 if (isempty(rvalue)) {
5934 /* Empty assignment resets the list */
5935 c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
5936 return 0;
5937 }
5938
5939 if (rvalue[0] == '~') {
5940 is_allow_rule = false;
5941 rvalue++;
5942 }
5943
5944 if (set_isempty(c->restrict_network_interfaces))
5945 /* Only initialize this when creating the set */
5946 c->restrict_network_interfaces_is_allow_list = is_allow_rule;
5947
5948 for (const char *p = rvalue;;) {
5949 _cleanup_free_ char *word = NULL;
5950
5951 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
5952 if (r == 0)
5953 break;
5954 if (r == -ENOMEM)
5955 return log_oom();
5956 if (r < 0) {
5957 log_syntax(unit, LOG_WARNING, filename, line, r,
5958 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
5959 break;
5960 }
5961
5962 if (!ifname_valid(word)) {
5963 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", word);
5964 continue;
5965 }
5966
5967 if (c->restrict_network_interfaces_is_allow_list != is_allow_rule)
5968 free(set_remove(c->restrict_network_interfaces, word));
5969 else {
5970 r = set_put_strdup(&c->restrict_network_interfaces, word);
5971 if (r < 0)
5972 return log_oom();
5973 }
5974 }
5975
5976 return 0;
5977 }
5978
5979 static int merge_by_names(Unit **u, Set *names, const char *id) {
5980 char *k;
5981 int r;
5982
5983 assert(u);
5984 assert(*u);
5985
5986 /* Let's try to add in all names that are aliases of this unit */
5987 while ((k = set_steal_first(names))) {
5988 _cleanup_free_ _unused_ char *free_k = k;
5989
5990 /* First try to merge in the other name into our unit */
5991 r = unit_merge_by_name(*u, k);
5992 if (r < 0) {
5993 Unit *other;
5994
5995 /* Hmm, we couldn't merge the other unit into ours? Then let's try it the other way
5996 * round. */
5997
5998 other = manager_get_unit((*u)->manager, k);
5999 if (!other)
6000 return r; /* return previous failure */
6001
6002 r = unit_merge(other, *u);
6003 if (r < 0)
6004 return r;
6005
6006 *u = other;
6007 return merge_by_names(u, names, NULL);
6008 }
6009
6010 if (streq_ptr(id, k))
6011 unit_choose_id(*u, id);
6012 }
6013
6014 return 0;
6015 }
6016
6017 int unit_load_fragment(Unit *u) {
6018 const char *fragment;
6019 _cleanup_set_free_free_ Set *names = NULL;
6020 int r;
6021
6022 assert(u);
6023 assert(u->load_state == UNIT_STUB);
6024 assert(u->id);
6025
6026 if (u->transient) {
6027 u->access_selinux_context = mfree(u->access_selinux_context);
6028 u->load_state = UNIT_LOADED;
6029 return 0;
6030 }
6031
6032 /* Possibly rebuild the fragment map to catch new units */
6033 r = unit_file_build_name_map(&u->manager->lookup_paths,
6034 &u->manager->unit_cache_timestamp_hash,
6035 &u->manager->unit_id_map,
6036 &u->manager->unit_name_map,
6037 &u->manager->unit_path_cache);
6038 if (r < 0)
6039 return log_error_errno(r, "Failed to rebuild name map: %m");
6040
6041 r = unit_file_find_fragment(u->manager->unit_id_map,
6042 u->manager->unit_name_map,
6043 u->id,
6044 &fragment,
6045 &names);
6046 if (r < 0 && r != -ENOENT)
6047 return r;
6048
6049 if (fragment) {
6050 /* Open the file, check if this is a mask, otherwise read. */
6051 _cleanup_fclose_ FILE *f = NULL;
6052 struct stat st;
6053
6054 /* Try to open the file name. A symlink is OK, for example for linked files or masks. We
6055 * expect that all symlinks within the lookup paths have been already resolved, but we don't
6056 * verify this here. */
6057 f = fopen(fragment, "re");
6058 if (!f)
6059 return log_unit_notice_errno(u, errno, "Failed to open %s: %m", fragment);
6060
6061 if (fstat(fileno(f), &st) < 0)
6062 return -errno;
6063
6064 r = free_and_strdup(&u->fragment_path, fragment);
6065 if (r < 0)
6066 return r;
6067
6068 if (null_or_empty(&st)) {
6069 /* Unit file is masked */
6070
6071 u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
6072 u->fragment_mtime = 0;
6073 u->access_selinux_context = mfree(u->access_selinux_context);
6074 } else {
6075 #if HAVE_SELINUX
6076 if (mac_selinux_use()) {
6077 _cleanup_freecon_ char *selcon = NULL;
6078
6079 /* Cache the SELinux context of the unit file here. We'll make use of when checking access permissions to loaded units */
6080 r = fgetfilecon_raw(fileno(f), &selcon);
6081 if (r < 0)
6082 log_unit_warning_errno(u, r, "Failed to read SELinux context of '%s', ignoring: %m", fragment);
6083
6084 r = free_and_strdup(&u->access_selinux_context, selcon);
6085 if (r < 0)
6086 return r;
6087 } else
6088 #endif
6089 u->access_selinux_context = mfree(u->access_selinux_context);
6090
6091 u->load_state = UNIT_LOADED;
6092 u->fragment_mtime = timespec_load(&st.st_mtim);
6093
6094 /* Now, parse the file contents */
6095 r = config_parse(u->id, fragment, f,
6096 UNIT_VTABLE(u)->sections,
6097 config_item_perf_lookup, load_fragment_gperf_lookup,
6098 0,
6099 u,
6100 NULL);
6101 if (r == -ENOEXEC)
6102 log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
6103 if (r < 0)
6104 return r;
6105 }
6106 }
6107
6108 /* Call merge_by_names with the name derived from the fragment path as the preferred name.
6109 *
6110 * We do the merge dance here because for some unit types, the unit might have aliases which are not
6111 * declared in the file system. In particular, this is true (and frequent) for device and swap units.
6112 */
6113 const char *id = u->id;
6114 _cleanup_free_ char *free_id = NULL;
6115
6116 if (fragment) {
6117 id = basename(fragment);
6118 if (unit_name_is_valid(id, UNIT_NAME_TEMPLATE)) {
6119 assert(u->instance); /* If we're not trying to use a template for non-instanced unit,
6120 * this must be set. */
6121
6122 r = unit_name_replace_instance(id, u->instance, &free_id);
6123 if (r < 0)
6124 return log_debug_errno(r, "Failed to build id (%s + %s): %m", id, u->instance);
6125 id = free_id;
6126 }
6127 }
6128
6129 Unit *merged = u;
6130 r = merge_by_names(&merged, names, id);
6131 if (r < 0)
6132 return r;
6133
6134 if (merged != u)
6135 u->load_state = UNIT_MERGED;
6136
6137 return 0;
6138 }
6139
6140 void unit_dump_config_items(FILE *f) {
6141 static const struct {
6142 const ConfigParserCallback callback;
6143 const char *rvalue;
6144 } table[] = {
6145 { config_parse_warn_compat, "NOTSUPPORTED" },
6146 { config_parse_int, "INTEGER" },
6147 { config_parse_unsigned, "UNSIGNED" },
6148 { config_parse_iec_size, "SIZE" },
6149 { config_parse_iec_uint64, "SIZE" },
6150 { config_parse_si_uint64, "SIZE" },
6151 { config_parse_bool, "BOOLEAN" },
6152 { config_parse_string, "STRING" },
6153 { config_parse_path, "PATH" },
6154 { config_parse_unit_path_printf, "PATH" },
6155 { config_parse_colon_separated_paths, "PATH" },
6156 { config_parse_strv, "STRING [...]" },
6157 { config_parse_exec_nice, "NICE" },
6158 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
6159 { config_parse_exec_io_class, "IOCLASS" },
6160 { config_parse_exec_io_priority, "IOPRIORITY" },
6161 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
6162 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
6163 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
6164 { config_parse_mode, "MODE" },
6165 { config_parse_unit_env_file, "FILE" },
6166 { config_parse_exec_output, "OUTPUT" },
6167 { config_parse_exec_input, "INPUT" },
6168 { config_parse_log_facility, "FACILITY" },
6169 { config_parse_log_level, "LEVEL" },
6170 { config_parse_exec_secure_bits, "SECUREBITS" },
6171 { config_parse_capability_set, "BOUNDINGSET" },
6172 { config_parse_rlimit, "LIMIT" },
6173 { config_parse_unit_deps, "UNIT [...]" },
6174 { config_parse_exec, "PATH [ARGUMENT [...]]" },
6175 { config_parse_service_type, "SERVICETYPE" },
6176 { config_parse_service_exit_type, "SERVICEEXITTYPE" },
6177 { config_parse_service_restart, "SERVICERESTART" },
6178 { config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
6179 { config_parse_kill_mode, "KILLMODE" },
6180 { config_parse_signal, "SIGNAL" },
6181 { config_parse_socket_listen, "SOCKET [...]" },
6182 { config_parse_socket_bind, "SOCKETBIND" },
6183 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
6184 { config_parse_sec, "SECONDS" },
6185 { config_parse_nsec, "NANOSECONDS" },
6186 { config_parse_namespace_path_strv, "PATH [...]" },
6187 { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
6188 { config_parse_unit_requires_mounts_for, "PATH [...]" },
6189 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
6190 { config_parse_unit_string_printf, "STRING" },
6191 { config_parse_trigger_unit, "UNIT" },
6192 { config_parse_timer, "TIMER" },
6193 { config_parse_path_spec, "PATH" },
6194 { config_parse_notify_access, "ACCESS" },
6195 { config_parse_ip_tos, "TOS" },
6196 { config_parse_unit_condition_path, "CONDITION" },
6197 { config_parse_unit_condition_string, "CONDITION" },
6198 { config_parse_unit_slice, "SLICE" },
6199 { config_parse_documentation, "URL" },
6200 { config_parse_service_timeout, "SECONDS" },
6201 { config_parse_emergency_action, "ACTION" },
6202 { config_parse_set_status, "STATUS" },
6203 { config_parse_service_sockets, "SOCKETS" },
6204 { config_parse_environ, "ENVIRON" },
6205 #if HAVE_SECCOMP
6206 { config_parse_syscall_filter, "SYSCALLS" },
6207 { config_parse_syscall_archs, "ARCHS" },
6208 { config_parse_syscall_errno, "ERRNO" },
6209 { config_parse_syscall_log, "SYSCALLS" },
6210 { config_parse_address_families, "FAMILIES" },
6211 { config_parse_restrict_namespaces, "NAMESPACES" },
6212 #endif
6213 { config_parse_restrict_filesystems, "FILESYSTEMS" },
6214 { config_parse_cpu_shares, "SHARES" },
6215 { config_parse_cg_weight, "WEIGHT" },
6216 { config_parse_cg_cpu_weight, "CPUWEIGHT" },
6217 { config_parse_memory_limit, "LIMIT" },
6218 { config_parse_device_allow, "DEVICE" },
6219 { config_parse_device_policy, "POLICY" },
6220 { config_parse_io_limit, "LIMIT" },
6221 { config_parse_io_device_weight, "DEVICEWEIGHT" },
6222 { config_parse_io_device_latency, "DEVICELATENCY" },
6223 { config_parse_blockio_bandwidth, "BANDWIDTH" },
6224 { config_parse_blockio_weight, "WEIGHT" },
6225 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
6226 { config_parse_long, "LONG" },
6227 { config_parse_socket_service, "SERVICE" },
6228 #if HAVE_SELINUX
6229 { config_parse_exec_selinux_context, "LABEL" },
6230 #endif
6231 { config_parse_job_mode, "MODE" },
6232 { config_parse_job_mode_isolate, "BOOLEAN" },
6233 { config_parse_personality, "PERSONALITY" },
6234 };
6235
6236 const char *prev = NULL;
6237 const char *i;
6238
6239 assert(f);
6240
6241 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
6242 const char *rvalue = "OTHER", *lvalue;
6243 const ConfigPerfItem *p;
6244 const char *dot;
6245
6246 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
6247
6248 /* Hide legacy settings */
6249 if (p->parse == config_parse_warn_compat &&
6250 p->ltype == DISABLED_LEGACY)
6251 continue;
6252
6253 for (size_t j = 0; j < ELEMENTSOF(table); j++)
6254 if (p->parse == table[j].callback) {
6255 rvalue = table[j].rvalue;
6256 break;
6257 }
6258
6259 dot = strchr(i, '.');
6260 lvalue = dot ? dot + 1 : i;
6261
6262 if (dot) {
6263 size_t prefix_len = dot - i;
6264
6265 if (!prev || !strneq(prev, i, prefix_len+1)) {
6266 if (prev)
6267 fputc('\n', f);
6268
6269 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
6270 }
6271 }
6272
6273 fprintf(f, "%s=%s\n", lvalue, rvalue);
6274 prev = i;
6275 }
6276 }
6277
6278 int config_parse_cpu_affinity2(
6279 const char *unit,
6280 const char *filename,
6281 unsigned line,
6282 const char *section,
6283 unsigned section_line,
6284 const char *lvalue,
6285 int ltype,
6286 const char *rvalue,
6287 void *data,
6288 void *userdata) {
6289
6290 CPUSet *affinity = ASSERT_PTR(data);
6291
6292 (void) parse_cpu_set_extend(rvalue, affinity, true, unit, filename, line, lvalue);
6293
6294 return 0;
6295 }
6296
6297 int config_parse_show_status(
6298 const char* unit,
6299 const char *filename,
6300 unsigned line,
6301 const char *section,
6302 unsigned section_line,
6303 const char *lvalue,
6304 int ltype,
6305 const char *rvalue,
6306 void *data,
6307 void *userdata) {
6308
6309 int k;
6310 ShowStatus *b = ASSERT_PTR(data);
6311
6312 assert(filename);
6313 assert(lvalue);
6314 assert(rvalue);
6315
6316 k = parse_show_status(rvalue, b);
6317 if (k < 0)
6318 log_syntax(unit, LOG_WARNING, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
6319
6320 return 0;
6321 }
6322
6323 int config_parse_output_restricted(
6324 const char* unit,
6325 const char *filename,
6326 unsigned line,
6327 const char *section,
6328 unsigned section_line,
6329 const char *lvalue,
6330 int ltype,
6331 const char *rvalue,
6332 void *data,
6333 void *userdata) {
6334
6335 ExecOutput t, *eo = ASSERT_PTR(data);
6336 bool obsolete = false;
6337
6338 assert(filename);
6339 assert(lvalue);
6340 assert(rvalue);
6341
6342 if (streq(rvalue, "syslog")) {
6343 t = EXEC_OUTPUT_JOURNAL;
6344 obsolete = true;
6345 } else if (streq(rvalue, "syslog+console")) {
6346 t = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
6347 obsolete = true;
6348 } else {
6349 t = exec_output_from_string(rvalue);
6350 if (t < 0) {
6351 log_syntax(unit, LOG_WARNING, filename, line, t, "Failed to parse output type, ignoring: %s", rvalue);
6352 return 0;
6353 }
6354
6355 if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND, EXEC_OUTPUT_FILE_TRUNCATE)) {
6356 log_syntax(unit, LOG_WARNING, filename, line, 0, "Standard output types socket, fd:, file:, append:, truncate: are not supported as defaults, ignoring: %s", rvalue);
6357 return 0;
6358 }
6359 }
6360
6361 if (obsolete)
6362 log_syntax(unit, LOG_NOTICE, filename, line, 0,
6363 "Standard output type %s is obsolete, automatically updating to %s. Please update your configuration.",
6364 rvalue, exec_output_to_string(t));
6365
6366 *eo = t;
6367 return 0;
6368 }
6369
6370 int config_parse_crash_chvt(
6371 const char* unit,
6372 const char *filename,
6373 unsigned line,
6374 const char *section,
6375 unsigned section_line,
6376 const char *lvalue,
6377 int ltype,
6378 const char *rvalue,
6379 void *data,
6380 void *userdata) {
6381
6382 int r;
6383
6384 assert(filename);
6385 assert(lvalue);
6386 assert(rvalue);
6387 assert(data);
6388
6389 r = parse_crash_chvt(rvalue, data);
6390 if (r < 0)
6391 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
6392
6393 return 0;
6394 }
6395
6396 int config_parse_swap_priority(
6397 const char *unit,
6398 const char *filename,
6399 unsigned line,
6400 const char *section,
6401 unsigned section_line,
6402 const char *lvalue,
6403 int ltype,
6404 const char *rvalue,
6405 void *data,
6406 void *userdata) {
6407
6408 Swap *s = ASSERT_PTR(userdata);
6409 int r, priority;
6410
6411 assert(filename);
6412 assert(lvalue);
6413 assert(rvalue);
6414 assert(data);
6415
6416 if (isempty(rvalue)) {
6417 s->parameters_fragment.priority = -1;
6418 s->parameters_fragment.priority_set = false;
6419 return 0;
6420 }
6421
6422 r = safe_atoi(rvalue, &priority);
6423 if (r < 0) {
6424 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid swap priority '%s', ignoring.", rvalue);
6425 return 0;
6426 }
6427
6428 if (priority < -1) {
6429 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);
6430 return 0;
6431 }
6432
6433 if (priority > 32767) {
6434 log_syntax(unit, LOG_WARNING, filename, line, 0, "Swap priority out of range, ignoring: %s", rvalue);
6435 return 0;
6436 }
6437
6438 s->parameters_fragment.priority = priority;
6439 s->parameters_fragment.priority_set = true;
6440 return 0;
6441 }
6442
6443 int config_parse_watchdog_sec(
6444 const char *unit,
6445 const char *filename,
6446 unsigned line,
6447 const char *section,
6448 unsigned section_line,
6449 const char *lvalue,
6450 int ltype,
6451 const char *rvalue,
6452 void *data,
6453 void *userdata) {
6454
6455 usec_t *usec = data;
6456
6457 assert(filename);
6458 assert(lvalue);
6459 assert(rvalue);
6460
6461 /* This is called for {Runtime,Reboot,KExec}WatchdogSec= where "default" maps to
6462 * USEC_INFINITY internally. */
6463
6464 if (streq(rvalue, "default"))
6465 *usec = USEC_INFINITY;
6466 else if (streq(rvalue, "off"))
6467 *usec = 0;
6468 else
6469 return config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
6470
6471 return 0;
6472 }
6473
6474 int config_parse_tty_size(
6475 const char *unit,
6476 const char *filename,
6477 unsigned line,
6478 const char *section,
6479 unsigned section_line,
6480 const char *lvalue,
6481 int ltype,
6482 const char *rvalue,
6483 void *data,
6484 void *userdata) {
6485
6486 unsigned *sz = data;
6487
6488 assert(filename);
6489 assert(lvalue);
6490 assert(rvalue);
6491
6492 if (isempty(rvalue)) {
6493 *sz = UINT_MAX;
6494 return 0;
6495 }
6496
6497 return config_parse_unsigned(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
6498 }