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