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