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