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