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