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