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