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