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