]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/load-fragment.c
d3919adddfae06c66ad6816e786ceb4caca0697d
[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 int 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_archs(
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 Set **archs = data;
3213 int r;
3214
3215 if (isempty(rvalue)) {
3216 *archs = set_free(*archs);
3217 return 0;
3218 }
3219
3220 for (const char *p = rvalue;;) {
3221 _cleanup_free_ char *word = NULL;
3222 uint32_t a;
3223
3224 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3225 if (r == 0)
3226 return 0;
3227 if (r == -ENOMEM)
3228 return log_oom();
3229 if (r < 0) {
3230 log_syntax(unit, LOG_WARNING, filename, line, r,
3231 "Invalid syntax, ignoring: %s", rvalue);
3232 return 0;
3233 }
3234
3235 r = seccomp_arch_from_string(word, &a);
3236 if (r < 0) {
3237 log_syntax(unit, LOG_WARNING, filename, line, r,
3238 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
3239 continue;
3240 }
3241
3242 r = set_ensure_put(archs, NULL, UINT32_TO_PTR(a + 1));
3243 if (r < 0)
3244 return log_oom();
3245 }
3246 }
3247
3248 int config_parse_syscall_errno(
3249 const char *unit,
3250 const char *filename,
3251 unsigned line,
3252 const char *section,
3253 unsigned section_line,
3254 const char *lvalue,
3255 int ltype,
3256 const char *rvalue,
3257 void *data,
3258 void *userdata) {
3259
3260 ExecContext *c = data;
3261 int e;
3262
3263 assert(filename);
3264 assert(lvalue);
3265 assert(rvalue);
3266
3267 if (isempty(rvalue)) {
3268 /* Empty assignment resets to KILL */
3269 c->syscall_errno = 0;
3270 return 0;
3271 }
3272
3273 e = parse_errno(rvalue);
3274 if (e <= 0) {
3275 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
3276 return 0;
3277 }
3278
3279 c->syscall_errno = e;
3280 return 0;
3281 }
3282
3283 int config_parse_address_families(
3284 const char *unit,
3285 const char *filename,
3286 unsigned line,
3287 const char *section,
3288 unsigned section_line,
3289 const char *lvalue,
3290 int ltype,
3291 const char *rvalue,
3292 void *data,
3293 void *userdata) {
3294
3295 ExecContext *c = data;
3296 bool invert = false;
3297 int r;
3298
3299 assert(filename);
3300 assert(lvalue);
3301 assert(rvalue);
3302
3303 if (isempty(rvalue)) {
3304 /* Empty assignment resets the list */
3305 c->address_families = set_free(c->address_families);
3306 c->address_families_allow_list = false;
3307 return 0;
3308 }
3309
3310 if (rvalue[0] == '~') {
3311 invert = true;
3312 rvalue++;
3313 }
3314
3315 if (!c->address_families) {
3316 c->address_families = set_new(NULL);
3317 if (!c->address_families)
3318 return log_oom();
3319
3320 c->address_families_allow_list = !invert;
3321 }
3322
3323 for (const char *p = rvalue;;) {
3324 _cleanup_free_ char *word = NULL;
3325 int af;
3326
3327 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3328 if (r == 0)
3329 return 0;
3330 if (r == -ENOMEM)
3331 return log_oom();
3332 if (r < 0) {
3333 log_syntax(unit, LOG_WARNING, filename, line, r,
3334 "Invalid syntax, ignoring: %s", rvalue);
3335 return 0;
3336 }
3337
3338 af = af_from_name(word);
3339 if (af < 0) {
3340 log_syntax(unit, LOG_WARNING, filename, line, af,
3341 "Failed to parse address family, ignoring: %s", word);
3342 continue;
3343 }
3344
3345 /* If we previously wanted to forbid an address family and now
3346 * we want to allow it, then just remove it from the list.
3347 */
3348 if (!invert == c->address_families_allow_list) {
3349 r = set_put(c->address_families, INT_TO_PTR(af));
3350 if (r < 0)
3351 return log_oom();
3352 } else
3353 set_remove(c->address_families, INT_TO_PTR(af));
3354 }
3355 }
3356
3357 int config_parse_restrict_namespaces(
3358 const char *unit,
3359 const char *filename,
3360 unsigned line,
3361 const char *section,
3362 unsigned section_line,
3363 const char *lvalue,
3364 int ltype,
3365 const char *rvalue,
3366 void *data,
3367 void *userdata) {
3368
3369 ExecContext *c = data;
3370 unsigned long flags;
3371 bool invert = false;
3372 int r;
3373
3374 if (isempty(rvalue)) {
3375 /* Reset to the default. */
3376 c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL;
3377 return 0;
3378 }
3379
3380 /* Boolean parameter ignores the previous settings */
3381 r = parse_boolean(rvalue);
3382 if (r > 0) {
3383 c->restrict_namespaces = 0;
3384 return 0;
3385 } else if (r == 0) {
3386 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
3387 return 0;
3388 }
3389
3390 if (rvalue[0] == '~') {
3391 invert = true;
3392 rvalue++;
3393 }
3394
3395 /* Not a boolean argument, in this case it's a list of namespace types. */
3396 r = namespace_flags_from_string(rvalue, &flags);
3397 if (r < 0) {
3398 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
3399 return 0;
3400 }
3401
3402 if (c->restrict_namespaces == NAMESPACE_FLAGS_INITIAL)
3403 /* Initial assignment. Just set the value. */
3404 c->restrict_namespaces = invert ? (~flags) & NAMESPACE_FLAGS_ALL : flags;
3405 else
3406 /* Merge the value with the previous one. */
3407 SET_FLAG(c->restrict_namespaces, flags, !invert);
3408
3409 return 0;
3410 }
3411 #endif
3412
3413 int config_parse_unit_slice(
3414 const char *unit,
3415 const char *filename,
3416 unsigned line,
3417 const char *section,
3418 unsigned section_line,
3419 const char *lvalue,
3420 int ltype,
3421 const char *rvalue,
3422 void *data,
3423 void *userdata) {
3424
3425 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3426 _cleanup_free_ char *k = NULL;
3427 Unit *u = userdata, *slice;
3428 int r;
3429
3430 assert(filename);
3431 assert(lvalue);
3432 assert(rvalue);
3433 assert(u);
3434
3435 r = unit_name_printf(u, rvalue, &k);
3436 if (r < 0) {
3437 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
3438 return 0;
3439 }
3440
3441 r = manager_load_unit(u->manager, k, NULL, &error, &slice);
3442 if (r < 0) {
3443 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to load slice unit %s, ignoring: %s", k, bus_error_message(&error, r));
3444 return 0;
3445 }
3446
3447 r = unit_set_slice(u, slice);
3448 if (r < 0) {
3449 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to assign slice %s to unit %s, ignoring: %m", slice->id, u->id);
3450 return 0;
3451 }
3452
3453 return 0;
3454 }
3455
3456 int config_parse_cpu_quota(
3457 const char *unit,
3458 const char *filename,
3459 unsigned line,
3460 const char *section,
3461 unsigned section_line,
3462 const char *lvalue,
3463 int ltype,
3464 const char *rvalue,
3465 void *data,
3466 void *userdata) {
3467
3468 CGroupContext *c = data;
3469 int r;
3470
3471 assert(filename);
3472 assert(lvalue);
3473 assert(rvalue);
3474
3475 if (isempty(rvalue)) {
3476 c->cpu_quota_per_sec_usec = USEC_INFINITY;
3477 return 0;
3478 }
3479
3480 r = parse_permille_unbounded(rvalue);
3481 if (r <= 0) {
3482 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid CPU quota '%s', ignoring.", rvalue);
3483 return 0;
3484 }
3485
3486 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 1000U;
3487 return 0;
3488 }
3489
3490 int config_parse_allowed_cpus(
3491 const char *unit,
3492 const char *filename,
3493 unsigned line,
3494 const char *section,
3495 unsigned section_line,
3496 const char *lvalue,
3497 int ltype,
3498 const char *rvalue,
3499 void *data,
3500 void *userdata) {
3501
3502 CGroupContext *c = data;
3503
3504 (void) parse_cpu_set_extend(rvalue, &c->cpuset_cpus, true, unit, filename, line, lvalue);
3505
3506 return 0;
3507 }
3508
3509 int config_parse_allowed_mems(
3510 const char *unit,
3511 const char *filename,
3512 unsigned line,
3513 const char *section,
3514 unsigned section_line,
3515 const char *lvalue,
3516 int ltype,
3517 const char *rvalue,
3518 void *data,
3519 void *userdata) {
3520
3521 CGroupContext *c = data;
3522
3523 (void) parse_cpu_set_extend(rvalue, &c->cpuset_mems, true, unit, filename, line, lvalue);
3524
3525 return 0;
3526 }
3527
3528 int config_parse_memory_limit(
3529 const char *unit,
3530 const char *filename,
3531 unsigned line,
3532 const char *section,
3533 unsigned section_line,
3534 const char *lvalue,
3535 int ltype,
3536 const char *rvalue,
3537 void *data,
3538 void *userdata) {
3539
3540 CGroupContext *c = data;
3541 uint64_t bytes = CGROUP_LIMIT_MAX;
3542 int r;
3543
3544 if (isempty(rvalue) && STR_IN_SET(lvalue, "DefaultMemoryLow",
3545 "DefaultMemoryMin",
3546 "MemoryLow",
3547 "MemoryMin"))
3548 bytes = CGROUP_LIMIT_MIN;
3549 else if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
3550
3551 r = parse_permille(rvalue);
3552 if (r < 0) {
3553 r = parse_size(rvalue, 1024, &bytes);
3554 if (r < 0) {
3555 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid memory limit '%s', ignoring: %m", rvalue);
3556 return 0;
3557 }
3558 } else
3559 bytes = physical_memory_scale(r, 1000U);
3560
3561 if (bytes >= UINT64_MAX ||
3562 (bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) {
3563 log_syntax(unit, LOG_WARNING, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue);
3564 return 0;
3565 }
3566 }
3567
3568 if (streq(lvalue, "DefaultMemoryLow")) {
3569 c->default_memory_low = bytes;
3570 c->default_memory_low_set = true;
3571 } else if (streq(lvalue, "DefaultMemoryMin")) {
3572 c->default_memory_min = bytes;
3573 c->default_memory_min_set = true;
3574 } else if (streq(lvalue, "MemoryMin")) {
3575 c->memory_min = bytes;
3576 c->memory_min_set = true;
3577 } else if (streq(lvalue, "MemoryLow")) {
3578 c->memory_low = bytes;
3579 c->memory_low_set = true;
3580 } else if (streq(lvalue, "MemoryHigh"))
3581 c->memory_high = bytes;
3582 else if (streq(lvalue, "MemoryMax"))
3583 c->memory_max = bytes;
3584 else if (streq(lvalue, "MemorySwapMax"))
3585 c->memory_swap_max = bytes;
3586 else if (streq(lvalue, "MemoryLimit"))
3587 c->memory_limit = bytes;
3588 else
3589 return -EINVAL;
3590
3591 return 0;
3592 }
3593
3594 int config_parse_tasks_max(
3595 const char *unit,
3596 const char *filename,
3597 unsigned line,
3598 const char *section,
3599 unsigned section_line,
3600 const char *lvalue,
3601 int ltype,
3602 const char *rvalue,
3603 void *data,
3604 void *userdata) {
3605
3606 const Unit *u = userdata;
3607 TasksMax *tasks_max = data;
3608 uint64_t v;
3609 int r;
3610
3611 if (isempty(rvalue)) {
3612 *tasks_max = u ? u->manager->default_tasks_max : TASKS_MAX_UNSET;
3613 return 0;
3614 }
3615
3616 if (streq(rvalue, "infinity")) {
3617 *tasks_max = TASKS_MAX_UNSET;
3618 return 0;
3619 }
3620
3621 r = parse_permille(rvalue);
3622 if (r >= 0)
3623 *tasks_max = (TasksMax) { r, 1000U }; /* r‰ */
3624 else {
3625 r = safe_atou64(rvalue, &v);
3626 if (r < 0) {
3627 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid maximum tasks value '%s', ignoring: %m", rvalue);
3628 return 0;
3629 }
3630
3631 if (v <= 0 || v >= UINT64_MAX) {
3632 log_syntax(unit, LOG_WARNING, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
3633 return 0;
3634 }
3635
3636 *tasks_max = (TasksMax) { v };
3637 }
3638
3639 return 0;
3640 }
3641
3642 int config_parse_delegate(
3643 const char *unit,
3644 const char *filename,
3645 unsigned line,
3646 const char *section,
3647 unsigned section_line,
3648 const char *lvalue,
3649 int ltype,
3650 const char *rvalue,
3651 void *data,
3652 void *userdata) {
3653
3654 CGroupContext *c = data;
3655 UnitType t;
3656 int r;
3657
3658 t = unit_name_to_type(unit);
3659 assert(t != _UNIT_TYPE_INVALID);
3660
3661 if (!unit_vtable[t]->can_delegate) {
3662 log_syntax(unit, LOG_WARNING, filename, line, 0, "Delegate= setting not supported for this unit type, ignoring.");
3663 return 0;
3664 }
3665
3666 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3667 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3668 * mask to delegate. */
3669
3670 if (isempty(rvalue)) {
3671 /* An empty string resets controllers and set Delegate=yes. */
3672 c->delegate = true;
3673 c->delegate_controllers = 0;
3674 return 0;
3675 }
3676
3677 r = parse_boolean(rvalue);
3678 if (r < 0) {
3679 CGroupMask mask = 0;
3680
3681 for (const char *p = rvalue;;) {
3682 _cleanup_free_ char *word = NULL;
3683 CGroupController cc;
3684
3685 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
3686 if (r == 0)
3687 break;
3688 if (r == -ENOMEM)
3689 return log_oom();
3690 if (r < 0) {
3691 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
3692 return 0;
3693 }
3694
3695 cc = cgroup_controller_from_string(word);
3696 if (cc < 0) {
3697 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid controller name '%s', ignoring", word);
3698 continue;
3699 }
3700
3701 mask |= CGROUP_CONTROLLER_TO_MASK(cc);
3702 }
3703
3704 c->delegate = true;
3705 c->delegate_controllers |= mask;
3706
3707 } else if (r > 0) {
3708 c->delegate = true;
3709 c->delegate_controllers = _CGROUP_MASK_ALL;
3710 } else {
3711 c->delegate = false;
3712 c->delegate_controllers = 0;
3713 }
3714
3715 return 0;
3716 }
3717
3718 int config_parse_device_allow(
3719 const char *unit,
3720 const char *filename,
3721 unsigned line,
3722 const char *section,
3723 unsigned section_line,
3724 const char *lvalue,
3725 int ltype,
3726 const char *rvalue,
3727 void *data,
3728 void *userdata) {
3729
3730 _cleanup_free_ char *path = NULL, *resolved = NULL;
3731 CGroupContext *c = data;
3732 const char *p = rvalue;
3733 int r;
3734
3735 if (isempty(rvalue)) {
3736 while (c->device_allow)
3737 cgroup_context_free_device_allow(c, c->device_allow);
3738
3739 return 0;
3740 }
3741
3742 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
3743 if (r == -ENOMEM)
3744 return log_oom();
3745 if (r < 0) {
3746 log_syntax(unit, LOG_WARNING, filename, line, r,
3747 "Invalid syntax, ignoring: %s", rvalue);
3748 return 0;
3749 }
3750 if (r == 0) {
3751 log_syntax(unit, LOG_WARNING, filename, line, 0,
3752 "Failed to extract device path and rights from '%s', ignoring.", rvalue);
3753 return 0;
3754 }
3755
3756 r = unit_full_printf(userdata, path, &resolved);
3757 if (r < 0) {
3758 log_syntax(unit, LOG_WARNING, filename, line, r,
3759 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3760 return 0;
3761 }
3762
3763 if (!STARTSWITH_SET(resolved, "block-", "char-")) {
3764
3765 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3766 if (r < 0)
3767 return 0;
3768
3769 if (!valid_device_node_path(resolved)) {
3770 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid device node path '%s', ignoring.", resolved);
3771 return 0;
3772 }
3773 }
3774
3775 if (!isempty(p) && !in_charset(p, "rwm")) {
3776 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid device rights '%s', ignoring.", p);
3777 return 0;
3778 }
3779
3780 return cgroup_add_device_allow(c, resolved, p);
3781 }
3782
3783 int config_parse_io_device_weight(
3784 const char *unit,
3785 const char *filename,
3786 unsigned line,
3787 const char *section,
3788 unsigned section_line,
3789 const char *lvalue,
3790 int ltype,
3791 const char *rvalue,
3792 void *data,
3793 void *userdata) {
3794
3795 _cleanup_free_ char *path = NULL, *resolved = NULL;
3796 CGroupIODeviceWeight *w;
3797 CGroupContext *c = data;
3798 const char *p = rvalue;
3799 uint64_t u;
3800 int r;
3801
3802 assert(filename);
3803 assert(lvalue);
3804 assert(rvalue);
3805
3806 if (isempty(rvalue)) {
3807 while (c->io_device_weights)
3808 cgroup_context_free_io_device_weight(c, c->io_device_weights);
3809
3810 return 0;
3811 }
3812
3813 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
3814 if (r == -ENOMEM)
3815 return log_oom();
3816 if (r < 0) {
3817 log_syntax(unit, LOG_WARNING, filename, line, r,
3818 "Invalid syntax, ignoring: %s", rvalue);
3819 return 0;
3820 }
3821 if (r == 0 || isempty(p)) {
3822 log_syntax(unit, LOG_WARNING, filename, line, 0,
3823 "Failed to extract device path and weight from '%s', ignoring.", rvalue);
3824 return 0;
3825 }
3826
3827 r = unit_full_printf(userdata, path, &resolved);
3828 if (r < 0) {
3829 log_syntax(unit, LOG_WARNING, filename, line, r,
3830 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3831 return 0;
3832 }
3833
3834 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3835 if (r < 0)
3836 return 0;
3837
3838 r = cg_weight_parse(p, &u);
3839 if (r < 0) {
3840 log_syntax(unit, LOG_WARNING, filename, line, r, "IO weight '%s' invalid, ignoring: %m", p);
3841 return 0;
3842 }
3843
3844 assert(u != CGROUP_WEIGHT_INVALID);
3845
3846 w = new0(CGroupIODeviceWeight, 1);
3847 if (!w)
3848 return log_oom();
3849
3850 w->path = TAKE_PTR(resolved);
3851 w->weight = u;
3852
3853 LIST_PREPEND(device_weights, c->io_device_weights, w);
3854 return 0;
3855 }
3856
3857 int config_parse_io_device_latency(
3858 const char *unit,
3859 const char *filename,
3860 unsigned line,
3861 const char *section,
3862 unsigned section_line,
3863 const char *lvalue,
3864 int ltype,
3865 const char *rvalue,
3866 void *data,
3867 void *userdata) {
3868
3869 _cleanup_free_ char *path = NULL, *resolved = NULL;
3870 CGroupIODeviceLatency *l;
3871 CGroupContext *c = data;
3872 const char *p = rvalue;
3873 usec_t usec;
3874 int r;
3875
3876 assert(filename);
3877 assert(lvalue);
3878 assert(rvalue);
3879
3880 if (isempty(rvalue)) {
3881 while (c->io_device_latencies)
3882 cgroup_context_free_io_device_latency(c, c->io_device_latencies);
3883
3884 return 0;
3885 }
3886
3887 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
3888 if (r == -ENOMEM)
3889 return log_oom();
3890 if (r < 0) {
3891 log_syntax(unit, LOG_WARNING, filename, line, r,
3892 "Invalid syntax, ignoring: %s", rvalue);
3893 return 0;
3894 }
3895 if (r == 0 || isempty(p)) {
3896 log_syntax(unit, LOG_WARNING, filename, line, 0,
3897 "Failed to extract device path and latency from '%s', ignoring.", rvalue);
3898 return 0;
3899 }
3900
3901 r = unit_full_printf(userdata, path, &resolved);
3902 if (r < 0) {
3903 log_syntax(unit, LOG_WARNING, filename, line, r,
3904 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3905 return 0;
3906 }
3907
3908 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3909 if (r < 0)
3910 return 0;
3911
3912 r = parse_sec(p, &usec);
3913 if (r < 0) {
3914 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse timer value, ignoring: %s", p);
3915 return 0;
3916 }
3917
3918 l = new0(CGroupIODeviceLatency, 1);
3919 if (!l)
3920 return log_oom();
3921
3922 l->path = TAKE_PTR(resolved);
3923 l->target_usec = usec;
3924
3925 LIST_PREPEND(device_latencies, c->io_device_latencies, l);
3926 return 0;
3927 }
3928
3929 int config_parse_io_limit(
3930 const char *unit,
3931 const char *filename,
3932 unsigned line,
3933 const char *section,
3934 unsigned section_line,
3935 const char *lvalue,
3936 int ltype,
3937 const char *rvalue,
3938 void *data,
3939 void *userdata) {
3940
3941 _cleanup_free_ char *path = NULL, *resolved = NULL;
3942 CGroupIODeviceLimit *l = NULL, *t;
3943 CGroupContext *c = data;
3944 CGroupIOLimitType type;
3945 const char *p = rvalue;
3946 uint64_t num;
3947 int r;
3948
3949 assert(filename);
3950 assert(lvalue);
3951 assert(rvalue);
3952
3953 type = cgroup_io_limit_type_from_string(lvalue);
3954 assert(type >= 0);
3955
3956 if (isempty(rvalue)) {
3957 LIST_FOREACH(device_limits, l, c->io_device_limits)
3958 l->limits[type] = cgroup_io_limit_defaults[type];
3959 return 0;
3960 }
3961
3962 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
3963 if (r == -ENOMEM)
3964 return log_oom();
3965 if (r < 0) {
3966 log_syntax(unit, LOG_WARNING, filename, line, r,
3967 "Invalid syntax, ignoring: %s", rvalue);
3968 return 0;
3969 }
3970 if (r == 0 || isempty(p)) {
3971 log_syntax(unit, LOG_WARNING, filename, line, 0,
3972 "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue);
3973 return 0;
3974 }
3975
3976 r = unit_full_printf(userdata, path, &resolved);
3977 if (r < 0) {
3978 log_syntax(unit, LOG_WARNING, filename, line, r,
3979 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3980 return 0;
3981 }
3982
3983 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3984 if (r < 0)
3985 return 0;
3986
3987 if (streq("infinity", p))
3988 num = CGROUP_LIMIT_MAX;
3989 else {
3990 r = parse_size(p, 1000, &num);
3991 if (r < 0 || num <= 0) {
3992 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid IO limit '%s', ignoring.", p);
3993 return 0;
3994 }
3995 }
3996
3997 LIST_FOREACH(device_limits, t, c->io_device_limits) {
3998 if (path_equal(resolved, t->path)) {
3999 l = t;
4000 break;
4001 }
4002 }
4003
4004 if (!l) {
4005 CGroupIOLimitType ttype;
4006
4007 l = new0(CGroupIODeviceLimit, 1);
4008 if (!l)
4009 return log_oom();
4010
4011 l->path = TAKE_PTR(resolved);
4012 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
4013 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
4014
4015 LIST_PREPEND(device_limits, c->io_device_limits, l);
4016 }
4017
4018 l->limits[type] = num;
4019
4020 return 0;
4021 }
4022
4023 int config_parse_blockio_device_weight(
4024 const char *unit,
4025 const char *filename,
4026 unsigned line,
4027 const char *section,
4028 unsigned section_line,
4029 const char *lvalue,
4030 int ltype,
4031 const char *rvalue,
4032 void *data,
4033 void *userdata) {
4034
4035 _cleanup_free_ char *path = NULL, *resolved = NULL;
4036 CGroupBlockIODeviceWeight *w;
4037 CGroupContext *c = data;
4038 const char *p = rvalue;
4039 uint64_t u;
4040 int r;
4041
4042 assert(filename);
4043 assert(lvalue);
4044 assert(rvalue);
4045
4046 if (isempty(rvalue)) {
4047 while (c->blockio_device_weights)
4048 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
4049
4050 return 0;
4051 }
4052
4053 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4054 if (r == -ENOMEM)
4055 return log_oom();
4056 if (r < 0) {
4057 log_syntax(unit, LOG_WARNING, filename, line, r,
4058 "Invalid syntax, ignoring: %s", rvalue);
4059 return 0;
4060 }
4061 if (r == 0 || isempty(p)) {
4062 log_syntax(unit, LOG_WARNING, filename, line, 0,
4063 "Failed to extract device node and weight from '%s', ignoring.", rvalue);
4064 return 0;
4065 }
4066
4067 r = unit_full_printf(userdata, path, &resolved);
4068 if (r < 0) {
4069 log_syntax(unit, LOG_WARNING, filename, line, r,
4070 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4071 return 0;
4072 }
4073
4074 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4075 if (r < 0)
4076 return 0;
4077
4078 r = cg_blkio_weight_parse(p, &u);
4079 if (r < 0) {
4080 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid block IO weight '%s', ignoring: %m", p);
4081 return 0;
4082 }
4083
4084 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
4085
4086 w = new0(CGroupBlockIODeviceWeight, 1);
4087 if (!w)
4088 return log_oom();
4089
4090 w->path = TAKE_PTR(resolved);
4091 w->weight = u;
4092
4093 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4094 return 0;
4095 }
4096
4097 int config_parse_blockio_bandwidth(
4098 const char *unit,
4099 const char *filename,
4100 unsigned line,
4101 const char *section,
4102 unsigned section_line,
4103 const char *lvalue,
4104 int ltype,
4105 const char *rvalue,
4106 void *data,
4107 void *userdata) {
4108
4109 _cleanup_free_ char *path = NULL, *resolved = NULL;
4110 CGroupBlockIODeviceBandwidth *b = NULL, *t;
4111 CGroupContext *c = data;
4112 const char *p = rvalue;
4113 uint64_t bytes;
4114 bool read;
4115 int r;
4116
4117 assert(filename);
4118 assert(lvalue);
4119 assert(rvalue);
4120
4121 read = streq("BlockIOReadBandwidth", lvalue);
4122
4123 if (isempty(rvalue)) {
4124 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
4125 b->rbps = CGROUP_LIMIT_MAX;
4126 b->wbps = CGROUP_LIMIT_MAX;
4127 }
4128 return 0;
4129 }
4130
4131 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
4132 if (r == -ENOMEM)
4133 return log_oom();
4134 if (r < 0) {
4135 log_syntax(unit, LOG_WARNING, filename, line, r,
4136 "Invalid syntax, ignoring: %s", rvalue);
4137 return 0;
4138 }
4139 if (r == 0 || isempty(p)) {
4140 log_syntax(unit, LOG_WARNING, filename, line, 0,
4141 "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue);
4142 return 0;
4143 }
4144
4145 r = unit_full_printf(userdata, path, &resolved);
4146 if (r < 0) {
4147 log_syntax(unit, LOG_WARNING, filename, line, r,
4148 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4149 return 0;
4150 }
4151
4152 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4153 if (r < 0)
4154 return 0;
4155
4156 r = parse_size(p, 1000, &bytes);
4157 if (r < 0 || bytes <= 0) {
4158 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid Block IO Bandwidth '%s', ignoring.", p);
4159 return 0;
4160 }
4161
4162 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
4163 if (path_equal(resolved, t->path)) {
4164 b = t;
4165 break;
4166 }
4167 }
4168
4169 if (!t) {
4170 b = new0(CGroupBlockIODeviceBandwidth, 1);
4171 if (!b)
4172 return log_oom();
4173
4174 b->path = TAKE_PTR(resolved);
4175 b->rbps = CGROUP_LIMIT_MAX;
4176 b->wbps = CGROUP_LIMIT_MAX;
4177
4178 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
4179 }
4180
4181 if (read)
4182 b->rbps = bytes;
4183 else
4184 b->wbps = bytes;
4185
4186 return 0;
4187 }
4188
4189 int config_parse_job_mode_isolate(
4190 const char *unit,
4191 const char *filename,
4192 unsigned line,
4193 const char *section,
4194 unsigned section_line,
4195 const char *lvalue,
4196 int ltype,
4197 const char *rvalue,
4198 void *data,
4199 void *userdata) {
4200
4201 JobMode *m = data;
4202 int r;
4203
4204 assert(filename);
4205 assert(lvalue);
4206 assert(rvalue);
4207
4208 r = parse_boolean(rvalue);
4209 if (r < 0) {
4210 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
4211 return 0;
4212 }
4213
4214 log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue);
4215
4216 *m = r ? JOB_ISOLATE : JOB_REPLACE;
4217 return 0;
4218 }
4219
4220 int config_parse_exec_directories(
4221 const char *unit,
4222 const char *filename,
4223 unsigned line,
4224 const char *section,
4225 unsigned section_line,
4226 const char *lvalue,
4227 int ltype,
4228 const char *rvalue,
4229 void *data,
4230 void *userdata) {
4231
4232 char***rt = data;
4233 const Unit *u = userdata;
4234 int r;
4235
4236 assert(filename);
4237 assert(lvalue);
4238 assert(rvalue);
4239 assert(data);
4240
4241 if (isempty(rvalue)) {
4242 /* Empty assignment resets the list */
4243 *rt = strv_free(*rt);
4244 return 0;
4245 }
4246
4247 for (const char *p = rvalue;;) {
4248 _cleanup_free_ char *word = NULL, *k = NULL;
4249
4250 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
4251 if (r == -ENOMEM)
4252 return log_oom();
4253 if (r < 0) {
4254 log_syntax(unit, LOG_WARNING, filename, line, r,
4255 "Invalid syntax, ignoring: %s", rvalue);
4256 return 0;
4257 }
4258 if (r == 0)
4259 return 0;
4260
4261 r = unit_full_printf(u, word, &k);
4262 if (r < 0) {
4263 log_syntax(unit, LOG_WARNING, filename, line, r,
4264 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4265 continue;
4266 }
4267
4268 r = path_simplify_and_warn(k, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
4269 if (r < 0)
4270 continue;
4271
4272 if (path_startswith(k, "private")) {
4273 log_syntax(unit, LOG_WARNING, filename, line, 0,
4274 "%s= path can't be 'private', ignoring assignment: %s", lvalue, word);
4275 continue;
4276 }
4277
4278 r = strv_push(rt, k);
4279 if (r < 0)
4280 return log_oom();
4281 k = NULL;
4282 }
4283 }
4284
4285 int config_parse_set_credential(
4286 const char *unit,
4287 const char *filename,
4288 unsigned line,
4289 const char *section,
4290 unsigned section_line,
4291 const char *lvalue,
4292 int ltype,
4293 const char *rvalue,
4294 void *data,
4295 void *userdata) {
4296
4297 _cleanup_free_ char *word = NULL, *k = NULL, *unescaped = NULL;
4298 ExecContext *context = data;
4299 ExecSetCredential *old;
4300 Unit *u = userdata;
4301 const char *p;
4302 int r, l;
4303
4304 assert(filename);
4305 assert(lvalue);
4306 assert(rvalue);
4307 assert(context);
4308
4309 if (isempty(rvalue)) {
4310 /* Empty assignment resets the list */
4311 context->set_credentials = hashmap_free(context->set_credentials);
4312 return 0;
4313 }
4314
4315 p = rvalue;
4316 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4317 if (r == -ENOMEM)
4318 return log_oom();
4319 if (r <= 0 || !p) {
4320 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
4321 return 0;
4322 }
4323
4324 r = unit_full_printf(u, word, &k);
4325 if (r < 0) {
4326 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4327 return 0;
4328 }
4329 if (!credential_name_valid(k)) {
4330 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", k);
4331 return 0;
4332 }
4333
4334 /* We support escape codes here, so that users can insert trailing \n if they like */
4335 l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
4336 if (l < 0) {
4337 log_syntax(unit, LOG_WARNING, filename, line, l, "Can't unescape \"%s\", ignoring: %m", p);
4338 return 0;
4339 }
4340
4341 old = hashmap_get(context->set_credentials, k);
4342 if (old) {
4343 free_and_replace(old->data, unescaped);
4344 old->size = l;
4345 } else {
4346 _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL;
4347
4348 sc = new0(ExecSetCredential, 1);
4349 if (!sc)
4350 return log_oom();
4351
4352 sc->id = TAKE_PTR(k);
4353 sc->data = TAKE_PTR(unescaped);
4354 sc->size = l;
4355
4356 r = hashmap_ensure_allocated(&context->set_credentials, &exec_set_credential_hash_ops);
4357 if (r < 0)
4358 return r;
4359
4360 r = hashmap_put(context->set_credentials, sc->id, sc);
4361 if (r < 0)
4362 return log_oom();
4363
4364 TAKE_PTR(sc);
4365 }
4366
4367 return 0;
4368 }
4369
4370 int config_parse_load_credential(
4371 const char *unit,
4372 const char *filename,
4373 unsigned line,
4374 const char *section,
4375 unsigned section_line,
4376 const char *lvalue,
4377 int ltype,
4378 const char *rvalue,
4379 void *data,
4380 void *userdata) {
4381
4382 _cleanup_free_ char *word = NULL, *k = NULL, *q = NULL;
4383 ExecContext *context = data;
4384 Unit *u = userdata;
4385 const char *p;
4386 int r;
4387
4388 assert(filename);
4389 assert(lvalue);
4390 assert(rvalue);
4391 assert(context);
4392
4393 if (isempty(rvalue)) {
4394 /* Empty assignment resets the list */
4395 context->load_credentials = strv_free(context->load_credentials);
4396 return 0;
4397 }
4398
4399 p = rvalue;
4400 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4401 if (r == -ENOMEM)
4402 return log_oom();
4403 if (r <= 0) {
4404 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
4405 return 0;
4406 }
4407
4408 r = unit_full_printf(u, word, &k);
4409 if (r < 0) {
4410 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4411 return 0;
4412 }
4413 if (!credential_name_valid(k)) {
4414 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", k);
4415 return 0;
4416 }
4417 r = unit_full_printf(u, p, &q);
4418 if (r < 0) {
4419 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", p);
4420 return 0;
4421 }
4422 if (path_is_absolute(q) ? !path_is_normalized(q) : !credential_name_valid(q)) {
4423 log_syntax(unit, LOG_WARNING, filename, line, r, "Credential source \"%s\" not valid, ignoring.", q);
4424 return 0;
4425 }
4426
4427 r = strv_consume_pair(&context->load_credentials, TAKE_PTR(k), TAKE_PTR(q));
4428 if (r < 0)
4429 return log_oom();
4430
4431 return 0;
4432 }
4433
4434 int config_parse_set_status(
4435 const char *unit,
4436 const char *filename,
4437 unsigned line,
4438 const char *section,
4439 unsigned section_line,
4440 const char *lvalue,
4441 int ltype,
4442 const char *rvalue,
4443 void *data,
4444 void *userdata) {
4445
4446 ExitStatusSet *status_set = data;
4447 int r;
4448
4449 assert(filename);
4450 assert(lvalue);
4451 assert(rvalue);
4452 assert(status_set);
4453
4454 /* Empty assignment resets the list */
4455 if (isempty(rvalue)) {
4456 exit_status_set_free(status_set);
4457 return 0;
4458 }
4459
4460 for (const char *p = rvalue;;) {
4461 _cleanup_free_ char *word = NULL;
4462 Bitmap *bitmap;
4463
4464 r = extract_first_word(&p, &word, NULL, 0);
4465 if (r == -ENOMEM)
4466 return log_oom();
4467 if (r < 0) {
4468 log_syntax(unit, LOG_WARNING, filename, line, r,
4469 "Failed to parse %s=%s, ignoring: %m", lvalue, rvalue);
4470 return 0;
4471 }
4472 if (r == 0)
4473 return 0;
4474
4475 /* We need to call exit_status_from_string() first, because we want
4476 * to parse numbers as exit statuses, not signals. */
4477
4478 r = exit_status_from_string(word);
4479 if (r >= 0) {
4480 assert(r >= 0 && r < 256);
4481 bitmap = &status_set->status;
4482 } else {
4483 r = signal_from_string(word);
4484 if (r < 0) {
4485 log_syntax(unit, LOG_WARNING, filename, line, 0,
4486 "Failed to parse value, ignoring: %s", word);
4487 continue;
4488 }
4489 bitmap = &status_set->signal;
4490 }
4491
4492 r = bitmap_set(bitmap, r);
4493 if (r < 0)
4494 log_syntax(unit, LOG_WARNING, filename, line, r,
4495 "Failed to set signal or status %s, ignoring: %m", word);
4496 }
4497 }
4498
4499 int config_parse_namespace_path_strv(
4500 const char *unit,
4501 const char *filename,
4502 unsigned line,
4503 const char *section,
4504 unsigned section_line,
4505 const char *lvalue,
4506 int ltype,
4507 const char *rvalue,
4508 void *data,
4509 void *userdata) {
4510
4511 const Unit *u = userdata;
4512 char*** sv = data;
4513 int r;
4514
4515 assert(filename);
4516 assert(lvalue);
4517 assert(rvalue);
4518 assert(data);
4519
4520 if (isempty(rvalue)) {
4521 /* Empty assignment resets the list */
4522 *sv = strv_free(*sv);
4523 return 0;
4524 }
4525
4526 for (const char *p = rvalue;;) {
4527 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
4528 const char *w;
4529 bool ignore_enoent = false, shall_prefix = false;
4530
4531 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
4532 if (r == 0)
4533 break;
4534 if (r == -ENOMEM)
4535 return log_oom();
4536 if (r < 0) {
4537 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
4538 return 0;
4539 }
4540
4541 w = word;
4542 if (startswith(w, "-")) {
4543 ignore_enoent = true;
4544 w++;
4545 }
4546 if (startswith(w, "+")) {
4547 shall_prefix = true;
4548 w++;
4549 }
4550
4551 r = unit_full_printf(u, w, &resolved);
4552 if (r < 0) {
4553 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s: %m", w);
4554 continue;
4555 }
4556
4557 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4558 if (r < 0)
4559 continue;
4560
4561 joined = strjoin(ignore_enoent ? "-" : "",
4562 shall_prefix ? "+" : "",
4563 resolved);
4564
4565 r = strv_push(sv, joined);
4566 if (r < 0)
4567 return log_oom();
4568
4569 joined = NULL;
4570 }
4571
4572 return 0;
4573 }
4574
4575 int config_parse_temporary_filesystems(
4576 const char *unit,
4577 const char *filename,
4578 unsigned line,
4579 const char *section,
4580 unsigned section_line,
4581 const char *lvalue,
4582 int ltype,
4583 const char *rvalue,
4584 void *data,
4585 void *userdata) {
4586
4587 const Unit *u = userdata;
4588 ExecContext *c = data;
4589 int r;
4590
4591 assert(filename);
4592 assert(lvalue);
4593 assert(rvalue);
4594 assert(data);
4595
4596 if (isempty(rvalue)) {
4597 /* Empty assignment resets the list */
4598 temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
4599 c->temporary_filesystems = NULL;
4600 c->n_temporary_filesystems = 0;
4601 return 0;
4602 }
4603
4604 for (const char *p = rvalue;;) {
4605 _cleanup_free_ char *word = NULL, *path = NULL, *resolved = NULL;
4606 const char *w;
4607
4608 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
4609 if (r == 0)
4610 return 0;
4611 if (r == -ENOMEM)
4612 return log_oom();
4613 if (r < 0) {
4614 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
4615 return 0;
4616 }
4617
4618 w = word;
4619 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4620 if (r == -ENOMEM)
4621 return log_oom();
4622 if (r < 0) {
4623 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", word);
4624 continue;
4625 }
4626 if (r == 0) {
4627 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid syntax, ignoring: %s", word);
4628 continue;
4629 }
4630
4631 r = unit_full_printf(u, path, &resolved);
4632 if (r < 0) {
4633 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", path);
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 r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, resolved, w);
4642 if (r < 0)
4643 return log_oom();
4644 }
4645 }
4646
4647 int config_parse_bind_paths(
4648 const char *unit,
4649 const char *filename,
4650 unsigned line,
4651 const char *section,
4652 unsigned section_line,
4653 const char *lvalue,
4654 int ltype,
4655 const char *rvalue,
4656 void *data,
4657 void *userdata) {
4658
4659 ExecContext *c = data;
4660 const Unit *u = userdata;
4661 int r;
4662
4663 assert(filename);
4664 assert(lvalue);
4665 assert(rvalue);
4666 assert(data);
4667
4668 if (isempty(rvalue)) {
4669 /* Empty assignment resets the list */
4670 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
4671 c->bind_mounts = NULL;
4672 c->n_bind_mounts = 0;
4673 return 0;
4674 }
4675
4676 for (const char *p = rvalue;;) {
4677 _cleanup_free_ char *source = NULL, *destination = NULL;
4678 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
4679 char *s = NULL, *d = NULL;
4680 bool rbind = true, ignore_enoent = false;
4681
4682 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
4683 if (r == 0)
4684 break;
4685 if (r == -ENOMEM)
4686 return log_oom();
4687 if (r < 0) {
4688 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
4689 return 0;
4690 }
4691
4692 r = unit_full_printf(u, source, &sresolved);
4693 if (r < 0) {
4694 log_syntax(unit, LOG_WARNING, filename, line, r,
4695 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", source);
4696 continue;
4697 }
4698
4699 s = sresolved;
4700 if (s[0] == '-') {
4701 ignore_enoent = true;
4702 s++;
4703 }
4704
4705 r = path_simplify_and_warn(s, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4706 if (r < 0)
4707 continue;
4708
4709 /* Optionally, the destination is specified. */
4710 if (p && p[-1] == ':') {
4711 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
4712 if (r == -ENOMEM)
4713 return log_oom();
4714 if (r < 0) {
4715 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
4716 return 0;
4717 }
4718 if (r == 0) {
4719 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing argument after ':', ignoring: %s", s);
4720 continue;
4721 }
4722
4723 r = unit_full_printf(u, destination, &dresolved);
4724 if (r < 0) {
4725 log_syntax(unit, LOG_WARNING, filename, line, r,
4726 "Failed to resolve specifiers in \"%s\", ignoring: %m", destination);
4727 continue;
4728 }
4729
4730 r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4731 if (r < 0)
4732 continue;
4733
4734 d = dresolved;
4735
4736 /* Optionally, there's also a short option string specified */
4737 if (p && p[-1] == ':') {
4738 _cleanup_free_ char *options = NULL;
4739
4740 r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
4741 if (r == -ENOMEM)
4742 return log_oom();
4743 if (r < 0) {
4744 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
4745 return 0;
4746 }
4747
4748 if (isempty(options) || streq(options, "rbind"))
4749 rbind = true;
4750 else if (streq(options, "norbind"))
4751 rbind = false;
4752 else {
4753 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
4754 continue;
4755 }
4756 }
4757 } else
4758 d = s;
4759
4760 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
4761 &(BindMount) {
4762 .source = s,
4763 .destination = d,
4764 .read_only = !!strstr(lvalue, "ReadOnly"),
4765 .recursive = rbind,
4766 .ignore_enoent = ignore_enoent,
4767 });
4768 if (r < 0)
4769 return log_oom();
4770 }
4771
4772 return 0;
4773 }
4774
4775 int config_parse_mount_images(
4776 const char *unit,
4777 const char *filename,
4778 unsigned line,
4779 const char *section,
4780 unsigned section_line,
4781 const char *lvalue,
4782 int ltype,
4783 const char *rvalue,
4784 void *data,
4785 void *userdata) {
4786
4787 ExecContext *c = data;
4788 const Unit *u = userdata;
4789 int r;
4790
4791 assert(filename);
4792 assert(lvalue);
4793 assert(rvalue);
4794 assert(data);
4795
4796 if (isempty(rvalue)) {
4797 /* Empty assignment resets the list */
4798 c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
4799 return 0;
4800 }
4801
4802 for (const char *p = rvalue;;) {
4803 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
4804 _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
4805 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
4806 const char *q = NULL;
4807 char *s = NULL;
4808 bool permissive = false;
4809
4810 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
4811 if (r == -ENOMEM)
4812 return log_oom();
4813 if (r < 0) {
4814 log_syntax(unit, LOG_WARNING, filename, line, r,
4815 "Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
4816 return 0;
4817 }
4818 if (r == 0)
4819 return 0;
4820
4821 q = tuple;
4822 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
4823 if (r == -ENOMEM)
4824 return log_oom();
4825 if (r < 0) {
4826 log_syntax(unit, LOG_WARNING, filename, line, r,
4827 "Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
4828 return 0;
4829 }
4830 if (r == 0)
4831 continue;
4832
4833 r = unit_full_printf(u, first, &sresolved);
4834 if (r < 0) {
4835 log_syntax(unit, LOG_WARNING, filename, line, r,
4836 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", first);
4837 continue;
4838 }
4839
4840 s = sresolved;
4841 if (s[0] == '-') {
4842 permissive = true;
4843 s++;
4844 }
4845
4846 r = path_simplify_and_warn(s, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4847 if (r < 0)
4848 continue;
4849
4850 if (isempty(second)) {
4851 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing destination in %s, ignoring: %s", lvalue, rvalue);
4852 continue;
4853 }
4854
4855 r = unit_full_printf(u, second, &dresolved);
4856 if (r < 0) {
4857 log_syntax(unit, LOG_WARNING, filename, line, r,
4858 "Failed to resolve specifiers in \"%s\", ignoring: %m", second);
4859 continue;
4860 }
4861
4862 r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4863 if (r < 0)
4864 continue;
4865
4866 for (;;) {
4867 _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
4868 MountOptions *o = NULL;
4869 int partition_designator;
4870
4871 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
4872 if (r == -ENOMEM)
4873 return log_oom();
4874 if (r < 0) {
4875 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", q);
4876 return 0;
4877 }
4878 if (r == 0)
4879 break;
4880 /* Single set of options, applying to the root partition/single filesystem */
4881 if (r == 1) {
4882 r = unit_full_printf(u, partition, &mount_options_resolved);
4883 if (r < 0) {
4884 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", first);
4885 continue;
4886 }
4887
4888 o = new(MountOptions, 1);
4889 if (!o)
4890 return log_oom();
4891 *o = (MountOptions) {
4892 .partition_designator = PARTITION_ROOT,
4893 .options = TAKE_PTR(mount_options_resolved),
4894 };
4895 LIST_APPEND(mount_options, options, o);
4896
4897 break;
4898 }
4899
4900 partition_designator = partition_designator_from_string(partition);
4901 if (partition_designator < 0) {
4902 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid partition name %s, ignoring", partition);
4903 continue;
4904 }
4905 r = unit_full_printf(u, mount_options, &mount_options_resolved);
4906 if (r < 0) {
4907 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
4908 continue;
4909 }
4910
4911 o = new(MountOptions, 1);
4912 if (!o)
4913 return log_oom();
4914 *o = (MountOptions) {
4915 .partition_designator = partition_designator,
4916 .options = TAKE_PTR(mount_options_resolved),
4917 };
4918 LIST_APPEND(mount_options, options, o);
4919 }
4920
4921 r = mount_image_add(&c->mount_images, &c->n_mount_images,
4922 &(MountImage) {
4923 .source = s,
4924 .destination = dresolved,
4925 .mount_options = options,
4926 .ignore_enoent = permissive,
4927 });
4928 if (r < 0)
4929 return log_oom();
4930 }
4931 }
4932
4933 int config_parse_job_timeout_sec(
4934 const char* unit,
4935 const char *filename,
4936 unsigned line,
4937 const char *section,
4938 unsigned section_line,
4939 const char *lvalue,
4940 int ltype,
4941 const char *rvalue,
4942 void *data,
4943 void *userdata) {
4944
4945 Unit *u = data;
4946 usec_t usec;
4947 int r;
4948
4949 assert(filename);
4950 assert(lvalue);
4951 assert(rvalue);
4952 assert(u);
4953
4954 r = parse_sec_fix_0(rvalue, &usec);
4955 if (r < 0) {
4956 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue);
4957 return 0;
4958 }
4959
4960 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
4961 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
4962 * count. */
4963
4964 if (!u->job_running_timeout_set)
4965 u->job_running_timeout = usec;
4966
4967 u->job_timeout = usec;
4968
4969 return 0;
4970 }
4971
4972 int config_parse_job_running_timeout_sec(
4973 const char* unit,
4974 const char *filename,
4975 unsigned line,
4976 const char *section,
4977 unsigned section_line,
4978 const char *lvalue,
4979 int ltype,
4980 const char *rvalue,
4981 void *data,
4982 void *userdata) {
4983
4984 Unit *u = data;
4985 usec_t usec;
4986 int r;
4987
4988 assert(filename);
4989 assert(lvalue);
4990 assert(rvalue);
4991 assert(u);
4992
4993 r = parse_sec_fix_0(rvalue, &usec);
4994 if (r < 0) {
4995 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue);
4996 return 0;
4997 }
4998
4999 u->job_running_timeout = usec;
5000 u->job_running_timeout_set = true;
5001
5002 return 0;
5003 }
5004
5005 int config_parse_emergency_action(
5006 const char* unit,
5007 const char *filename,
5008 unsigned line,
5009 const char *section,
5010 unsigned section_line,
5011 const char *lvalue,
5012 int ltype,
5013 const char *rvalue,
5014 void *data,
5015 void *userdata) {
5016
5017 Manager *m = NULL;
5018 EmergencyAction *x = data;
5019 int r;
5020
5021 assert(filename);
5022 assert(lvalue);
5023 assert(rvalue);
5024 assert(data);
5025
5026 if (unit)
5027 m = ((Unit*) userdata)->manager;
5028 else
5029 m = data;
5030
5031 r = parse_emergency_action(rvalue, MANAGER_IS_SYSTEM(m), x);
5032 if (r < 0) {
5033 if (r == -EOPNOTSUPP && MANAGER_IS_USER(m)) {
5034 /* Compat mode: remove for systemd 241. */
5035
5036 log_syntax(unit, LOG_INFO, filename, line, r,
5037 "%s= in user mode specified as \"%s\", using \"exit-force\" instead.",
5038 lvalue, rvalue);
5039 *x = EMERGENCY_ACTION_EXIT_FORCE;
5040 return 0;
5041 }
5042
5043 if (r == -EOPNOTSUPP)
5044 log_syntax(unit, LOG_WARNING, filename, line, r,
5045 "%s= specified as %s mode action, ignoring: %s",
5046 lvalue, MANAGER_IS_SYSTEM(m) ? "user" : "system", rvalue);
5047 else
5048 log_syntax(unit, LOG_WARNING, filename, line, r,
5049 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
5050 return 0;
5051 }
5052
5053 return 0;
5054 }
5055
5056 int config_parse_pid_file(
5057 const char *unit,
5058 const char *filename,
5059 unsigned line,
5060 const char *section,
5061 unsigned section_line,
5062 const char *lvalue,
5063 int ltype,
5064 const char *rvalue,
5065 void *data,
5066 void *userdata) {
5067
5068 _cleanup_free_ char *k = NULL, *n = NULL;
5069 const Unit *u = userdata;
5070 char **s = data;
5071 int r;
5072
5073 assert(filename);
5074 assert(lvalue);
5075 assert(rvalue);
5076 assert(u);
5077
5078 if (isempty(rvalue)) {
5079 /* An empty assignment removes already set value. */
5080 *s = mfree(*s);
5081 return 0;
5082 }
5083
5084 r = unit_full_printf(u, rvalue, &k);
5085 if (r < 0) {
5086 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
5087 return 0;
5088 }
5089
5090 /* If this is a relative path make it absolute by prefixing the /run */
5091 n = path_make_absolute(k, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
5092 if (!n)
5093 return log_oom();
5094
5095 /* Check that the result is a sensible path */
5096 r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5097 if (r < 0)
5098 return r;
5099
5100 r = patch_var_run(unit, filename, line, lvalue, &n);
5101 if (r < 0)
5102 return r;
5103
5104 free_and_replace(*s, n);
5105 return 0;
5106 }
5107
5108 int config_parse_exit_status(
5109 const char *unit,
5110 const char *filename,
5111 unsigned line,
5112 const char *section,
5113 unsigned section_line,
5114 const char *lvalue,
5115 int ltype,
5116 const char *rvalue,
5117 void *data,
5118 void *userdata) {
5119
5120 int *exit_status = data, r;
5121 uint8_t u;
5122
5123 assert(filename);
5124 assert(lvalue);
5125 assert(rvalue);
5126 assert(exit_status);
5127
5128 if (isempty(rvalue)) {
5129 *exit_status = -1;
5130 return 0;
5131 }
5132
5133 r = safe_atou8(rvalue, &u);
5134 if (r < 0) {
5135 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse exit status '%s', ignoring: %m", rvalue);
5136 return 0;
5137 }
5138
5139 *exit_status = u;
5140 return 0;
5141 }
5142
5143 int config_parse_disable_controllers(
5144 const char *unit,
5145 const char *filename,
5146 unsigned line,
5147 const char *section,
5148 unsigned section_line,
5149 const char *lvalue,
5150 int ltype,
5151 const char *rvalue,
5152 void *data,
5153 void *userdata) {
5154
5155 int r;
5156 CGroupContext *c = data;
5157 CGroupMask disabled_mask;
5158
5159 /* 1. If empty, make all controllers eligible for use again.
5160 * 2. If non-empty, merge all listed controllers, space separated. */
5161
5162 if (isempty(rvalue)) {
5163 c->disable_controllers = 0;
5164 return 0;
5165 }
5166
5167 r = cg_mask_from_string(rvalue, &disabled_mask);
5168 if (r < 0 || disabled_mask <= 0) {
5169 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid cgroup string: %s, ignoring", rvalue);
5170 return 0;
5171 }
5172
5173 c->disable_controllers |= disabled_mask;
5174
5175 return 0;
5176 }
5177
5178 int config_parse_ip_filter_bpf_progs(
5179 const char *unit,
5180 const char *filename,
5181 unsigned line,
5182 const char *section,
5183 unsigned section_line,
5184 const char *lvalue,
5185 int ltype,
5186 const char *rvalue,
5187 void *data,
5188 void *userdata) {
5189
5190 _cleanup_free_ char *resolved = NULL;
5191 const Unit *u = userdata;
5192 char ***paths = data;
5193 int r;
5194
5195 assert(filename);
5196 assert(lvalue);
5197 assert(rvalue);
5198 assert(paths);
5199
5200 if (isempty(rvalue)) {
5201 *paths = strv_free(*paths);
5202 return 0;
5203 }
5204
5205 r = unit_full_printf(u, rvalue, &resolved);
5206 if (r < 0) {
5207 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
5208 return 0;
5209 }
5210
5211 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5212 if (r < 0)
5213 return 0;
5214
5215 if (strv_contains(*paths, resolved))
5216 return 0;
5217
5218 r = strv_extend(paths, resolved);
5219 if (r < 0)
5220 return log_oom();
5221
5222 r = bpf_firewall_supported();
5223 if (r < 0)
5224 return r;
5225 if (r != BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
5226 static bool warned = false;
5227
5228 log_full(warned ? LOG_DEBUG : LOG_WARNING,
5229 "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"
5230 "Starting this unit will fail! (This warning is only shown for the first loaded unit using IP firewalling.)", filename, line, lvalue, rvalue);
5231
5232 warned = true;
5233 }
5234
5235 return 0;
5236 }
5237
5238 static int merge_by_names(Unit **u, Set *names, const char *id) {
5239 char *k;
5240 int r;
5241
5242 assert(u);
5243 assert(*u);
5244
5245 /* Let's try to add in all names that are aliases of this unit */
5246 while ((k = set_steal_first(names))) {
5247 _cleanup_free_ _unused_ char *free_k = k;
5248
5249 /* First try to merge in the other name into our unit */
5250 r = unit_merge_by_name(*u, k);
5251 if (r < 0) {
5252 Unit *other;
5253
5254 /* Hmm, we couldn't merge the other unit into ours? Then let's try it the other way
5255 * round. */
5256
5257 other = manager_get_unit((*u)->manager, k);
5258 if (!other)
5259 return r; /* return previous failure */
5260
5261 r = unit_merge(other, *u);
5262 if (r < 0)
5263 return r;
5264
5265 *u = other;
5266 return merge_by_names(u, names, NULL);
5267 }
5268
5269 if (streq_ptr(id, k))
5270 unit_choose_id(*u, id);
5271 }
5272
5273 return 0;
5274 }
5275
5276 int unit_load_fragment(Unit *u) {
5277 const char *fragment;
5278 _cleanup_set_free_free_ Set *names = NULL;
5279 int r;
5280
5281 assert(u);
5282 assert(u->load_state == UNIT_STUB);
5283 assert(u->id);
5284
5285 if (u->transient) {
5286 u->load_state = UNIT_LOADED;
5287 return 0;
5288 }
5289
5290 /* Possibly rebuild the fragment map to catch new units */
5291 r = unit_file_build_name_map(&u->manager->lookup_paths,
5292 &u->manager->unit_cache_timestamp_hash,
5293 &u->manager->unit_id_map,
5294 &u->manager->unit_name_map,
5295 &u->manager->unit_path_cache);
5296 if (r < 0)
5297 return log_error_errno(r, "Failed to rebuild name map: %m");
5298
5299 r = unit_file_find_fragment(u->manager->unit_id_map,
5300 u->manager->unit_name_map,
5301 u->id,
5302 &fragment,
5303 &names);
5304 if (r < 0 && r != -ENOENT)
5305 return r;
5306
5307 if (fragment) {
5308 /* Open the file, check if this is a mask, otherwise read. */
5309 _cleanup_fclose_ FILE *f = NULL;
5310 struct stat st;
5311
5312 /* Try to open the file name. A symlink is OK, for example for linked files or masks. We
5313 * expect that all symlinks within the lookup paths have been already resolved, but we don't
5314 * verify this here. */
5315 f = fopen(fragment, "re");
5316 if (!f)
5317 return log_unit_notice_errno(u, errno, "Failed to open %s: %m", fragment);
5318
5319 if (fstat(fileno(f), &st) < 0)
5320 return -errno;
5321
5322 r = free_and_strdup(&u->fragment_path, fragment);
5323 if (r < 0)
5324 return r;
5325
5326 if (null_or_empty(&st)) {
5327 /* Unit file is masked */
5328
5329 u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
5330 u->fragment_mtime = 0;
5331 } else {
5332 u->load_state = UNIT_LOADED;
5333 u->fragment_mtime = timespec_load(&st.st_mtim);
5334
5335 /* Now, parse the file contents */
5336 r = config_parse(u->id, fragment, f,
5337 UNIT_VTABLE(u)->sections,
5338 config_item_perf_lookup, load_fragment_gperf_lookup,
5339 0,
5340 u,
5341 NULL);
5342 if (r == -ENOEXEC)
5343 log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
5344 if (r < 0)
5345 return r;
5346 }
5347 }
5348
5349 /* We do the merge dance here because for some unit types, the unit might have aliases which are not
5350 * declared in the file system. In particular, this is true (and frequent) for device and swap units.
5351 */
5352 Unit *merged;
5353 const char *id = u->id;
5354 _cleanup_free_ char *free_id = NULL;
5355
5356 if (fragment) {
5357 id = basename(fragment);
5358 if (unit_name_is_valid(id, UNIT_NAME_TEMPLATE)) {
5359 assert(u->instance); /* If we're not trying to use a template for non-instanced unit,
5360 * this must be set. */
5361
5362 r = unit_name_replace_instance(id, u->instance, &free_id);
5363 if (r < 0)
5364 return log_debug_errno(r, "Failed to build id (%s + %s): %m", id, u->instance);
5365 id = free_id;
5366 }
5367 }
5368
5369 merged = u;
5370 r = merge_by_names(&merged, names, id);
5371 if (r < 0)
5372 return r;
5373
5374 if (merged != u)
5375 u->load_state = UNIT_MERGED;
5376
5377 return 0;
5378 }
5379
5380 void unit_dump_config_items(FILE *f) {
5381 static const struct {
5382 const ConfigParserCallback callback;
5383 const char *rvalue;
5384 } table[] = {
5385 { config_parse_warn_compat, "NOTSUPPORTED" },
5386 { config_parse_int, "INTEGER" },
5387 { config_parse_unsigned, "UNSIGNED" },
5388 { config_parse_iec_size, "SIZE" },
5389 { config_parse_iec_uint64, "SIZE" },
5390 { config_parse_si_uint64, "SIZE" },
5391 { config_parse_bool, "BOOLEAN" },
5392 { config_parse_string, "STRING" },
5393 { config_parse_path, "PATH" },
5394 { config_parse_unit_path_printf, "PATH" },
5395 { config_parse_strv, "STRING [...]" },
5396 { config_parse_exec_nice, "NICE" },
5397 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
5398 { config_parse_exec_io_class, "IOCLASS" },
5399 { config_parse_exec_io_priority, "IOPRIORITY" },
5400 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
5401 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
5402 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
5403 { config_parse_mode, "MODE" },
5404 { config_parse_unit_env_file, "FILE" },
5405 { config_parse_exec_output, "OUTPUT" },
5406 { config_parse_exec_input, "INPUT" },
5407 { config_parse_log_facility, "FACILITY" },
5408 { config_parse_log_level, "LEVEL" },
5409 { config_parse_exec_secure_bits, "SECUREBITS" },
5410 { config_parse_capability_set, "BOUNDINGSET" },
5411 { config_parse_rlimit, "LIMIT" },
5412 { config_parse_unit_deps, "UNIT [...]" },
5413 { config_parse_exec, "PATH [ARGUMENT [...]]" },
5414 { config_parse_service_type, "SERVICETYPE" },
5415 { config_parse_service_restart, "SERVICERESTART" },
5416 { config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
5417 { config_parse_kill_mode, "KILLMODE" },
5418 { config_parse_signal, "SIGNAL" },
5419 { config_parse_socket_listen, "SOCKET [...]" },
5420 { config_parse_socket_bind, "SOCKETBIND" },
5421 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
5422 { config_parse_sec, "SECONDS" },
5423 { config_parse_nsec, "NANOSECONDS" },
5424 { config_parse_namespace_path_strv, "PATH [...]" },
5425 { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
5426 { config_parse_unit_requires_mounts_for, "PATH [...]" },
5427 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
5428 { config_parse_unit_string_printf, "STRING" },
5429 { config_parse_trigger_unit, "UNIT" },
5430 { config_parse_timer, "TIMER" },
5431 { config_parse_path_spec, "PATH" },
5432 { config_parse_notify_access, "ACCESS" },
5433 { config_parse_ip_tos, "TOS" },
5434 { config_parse_unit_condition_path, "CONDITION" },
5435 { config_parse_unit_condition_string, "CONDITION" },
5436 { config_parse_unit_slice, "SLICE" },
5437 { config_parse_documentation, "URL" },
5438 { config_parse_service_timeout, "SECONDS" },
5439 { config_parse_emergency_action, "ACTION" },
5440 { config_parse_set_status, "STATUS" },
5441 { config_parse_service_sockets, "SOCKETS" },
5442 { config_parse_environ, "ENVIRON" },
5443 #if HAVE_SECCOMP
5444 { config_parse_syscall_filter, "SYSCALLS" },
5445 { config_parse_syscall_archs, "ARCHS" },
5446 { config_parse_syscall_errno, "ERRNO" },
5447 { config_parse_address_families, "FAMILIES" },
5448 { config_parse_restrict_namespaces, "NAMESPACES" },
5449 #endif
5450 { config_parse_cpu_shares, "SHARES" },
5451 { config_parse_cg_weight, "WEIGHT" },
5452 { config_parse_memory_limit, "LIMIT" },
5453 { config_parse_device_allow, "DEVICE" },
5454 { config_parse_device_policy, "POLICY" },
5455 { config_parse_io_limit, "LIMIT" },
5456 { config_parse_io_device_weight, "DEVICEWEIGHT" },
5457 { config_parse_io_device_latency, "DEVICELATENCY" },
5458 { config_parse_blockio_bandwidth, "BANDWIDTH" },
5459 { config_parse_blockio_weight, "WEIGHT" },
5460 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
5461 { config_parse_long, "LONG" },
5462 { config_parse_socket_service, "SERVICE" },
5463 #if HAVE_SELINUX
5464 { config_parse_exec_selinux_context, "LABEL" },
5465 #endif
5466 { config_parse_job_mode, "MODE" },
5467 { config_parse_job_mode_isolate, "BOOLEAN" },
5468 { config_parse_personality, "PERSONALITY" },
5469 };
5470
5471 const char *prev = NULL;
5472 const char *i;
5473
5474 assert(f);
5475
5476 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
5477 const char *rvalue = "OTHER", *lvalue;
5478 const ConfigPerfItem *p;
5479 size_t prefix_len;
5480 const char *dot;
5481 unsigned j;
5482
5483 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
5484
5485 /* Hide legacy settings */
5486 if (p->parse == config_parse_warn_compat &&
5487 p->ltype == DISABLED_LEGACY)
5488 continue;
5489
5490 for (j = 0; j < ELEMENTSOF(table); j++)
5491 if (p->parse == table[j].callback) {
5492 rvalue = table[j].rvalue;
5493 break;
5494 }
5495
5496 dot = strchr(i, '.');
5497 lvalue = dot ? dot + 1 : i;
5498 prefix_len = dot-i;
5499
5500 if (dot)
5501 if (!prev || !strneq(prev, i, prefix_len+1)) {
5502 if (prev)
5503 fputc('\n', f);
5504
5505 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
5506 }
5507
5508 fprintf(f, "%s=%s\n", lvalue, rvalue);
5509 prev = i;
5510 }
5511 }
5512
5513 int config_parse_cpu_affinity2(
5514 const char *unit,
5515 const char *filename,
5516 unsigned line,
5517 const char *section,
5518 unsigned section_line,
5519 const char *lvalue,
5520 int ltype,
5521 const char *rvalue,
5522 void *data,
5523 void *userdata) {
5524
5525 CPUSet *affinity = data;
5526
5527 assert(affinity);
5528
5529 (void) parse_cpu_set_extend(rvalue, affinity, true, unit, filename, line, lvalue);
5530
5531 return 0;
5532 }
5533
5534 int config_parse_show_status(
5535 const char* unit,
5536 const char *filename,
5537 unsigned line,
5538 const char *section,
5539 unsigned section_line,
5540 const char *lvalue,
5541 int ltype,
5542 const char *rvalue,
5543 void *data,
5544 void *userdata) {
5545
5546 int k;
5547 ShowStatus *b = data;
5548
5549 assert(filename);
5550 assert(lvalue);
5551 assert(rvalue);
5552 assert(data);
5553
5554 k = parse_show_status(rvalue, b);
5555 if (k < 0)
5556 log_syntax(unit, LOG_WARNING, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
5557
5558 return 0;
5559 }
5560
5561 int config_parse_output_restricted(
5562 const char* unit,
5563 const char *filename,
5564 unsigned line,
5565 const char *section,
5566 unsigned section_line,
5567 const char *lvalue,
5568 int ltype,
5569 const char *rvalue,
5570 void *data,
5571 void *userdata) {
5572
5573 ExecOutput t, *eo = data;
5574 bool obsolete = false;
5575
5576 assert(filename);
5577 assert(lvalue);
5578 assert(rvalue);
5579 assert(data);
5580
5581 if (streq(rvalue, "syslog")) {
5582 t = EXEC_OUTPUT_JOURNAL;
5583 obsolete = true;
5584 } else if (streq(rvalue, "syslog+console")) {
5585 t = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
5586 obsolete = true;
5587 } else {
5588 t = exec_output_from_string(rvalue);
5589 if (t < 0) {
5590 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse output type, ignoring: %s", rvalue);
5591 return 0;
5592 }
5593
5594 if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) {
5595 log_syntax(unit, LOG_WARNING, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue);
5596 return 0;
5597 }
5598 }
5599
5600 if (obsolete)
5601 log_syntax(unit, LOG_NOTICE, filename, line, 0,
5602 "Standard output type %s is obsolete, automatically updating to %s. Please update your configuration.",
5603 rvalue, exec_output_to_string(t));
5604
5605 *eo = t;
5606 return 0;
5607 }
5608
5609 int config_parse_crash_chvt(
5610 const char* unit,
5611 const char *filename,
5612 unsigned line,
5613 const char *section,
5614 unsigned section_line,
5615 const char *lvalue,
5616 int ltype,
5617 const char *rvalue,
5618 void *data,
5619 void *userdata) {
5620
5621 int r;
5622
5623 assert(filename);
5624 assert(lvalue);
5625 assert(rvalue);
5626 assert(data);
5627
5628 r = parse_crash_chvt(rvalue, data);
5629 if (r < 0)
5630 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
5631
5632 return 0;
5633 }
5634
5635 int config_parse_swap_priority(
5636 const char *unit,
5637 const char *filename,
5638 unsigned line,
5639 const char *section,
5640 unsigned section_line,
5641 const char *lvalue,
5642 int ltype,
5643 const char *rvalue,
5644 void *data,
5645 void *userdata) {
5646
5647 Swap *s = userdata;
5648 int r, priority;
5649
5650 assert(s);
5651 assert(filename);
5652 assert(lvalue);
5653 assert(rvalue);
5654 assert(data);
5655
5656 if (isempty(rvalue)) {
5657 s->parameters_fragment.priority = -1;
5658 s->parameters_fragment.priority_set = false;
5659 return 0;
5660 }
5661
5662 r = safe_atoi(rvalue, &priority);
5663 if (r < 0) {
5664 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid swap priority '%s', ignoring.", rvalue);
5665 return 0;
5666 }
5667
5668 if (priority < -1) {
5669 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);
5670 return 0;
5671 }
5672
5673 if (priority > 32767) {
5674 log_syntax(unit, LOG_WARNING, filename, line, 0, "Swap priority out of range, ignoring: %s", rvalue);
5675 return 0;
5676 }
5677
5678 s->parameters_fragment.priority = priority;
5679 s->parameters_fragment.priority_set = true;
5680 return 0;
5681 }