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