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