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