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