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