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