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