Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is part of systemd.
5 :
6 : Copyright 2010 Lennart Poettering
7 : Copyright 2012 Holger Hans Peter Freyther
8 :
9 : systemd is free software; you can redistribute it and/or modify it
10 : under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation; either version 2.1 of the License, or
12 : (at your option) any later version.
13 :
14 : systemd is distributed in the hope that it will be useful, but
15 : WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 : ***/
22 :
23 : #include <linux/oom.h>
24 : #include <errno.h>
25 : #include <string.h>
26 : #include <fcntl.h>
27 : #include <sched.h>
28 : #include <linux/fs.h>
29 : #include <sys/stat.h>
30 : #include <sys/resource.h>
31 :
32 : #ifdef HAVE_SECCOMP
33 : #include <seccomp.h>
34 : #endif
35 :
36 : #include "unit.h"
37 : #include "strv.h"
38 : #include "conf-parser.h"
39 : #include "load-fragment.h"
40 : #include "log.h"
41 : #include "ioprio.h"
42 : #include "securebits.h"
43 : #include "missing.h"
44 : #include "unit-name.h"
45 : #include "unit-printf.h"
46 : #include "utf8.h"
47 : #include "path-util.h"
48 : #include "env-util.h"
49 : #include "cgroup.h"
50 : #include "bus-util.h"
51 : #include "bus-error.h"
52 : #include "errno-list.h"
53 : #include "af-list.h"
54 : #include "cap-list.h"
55 : #include "signal-util.h"
56 : #include "bus-internal.h"
57 :
58 : #ifdef HAVE_SECCOMP
59 : #include "seccomp-util.h"
60 : #endif
61 :
62 0 : int config_parse_warn_compat(
63 : const char *unit,
64 : const char *filename,
65 : unsigned line,
66 : const char *section,
67 : unsigned section_line,
68 : const char *lvalue,
69 : int ltype,
70 : const char *rvalue,
71 : void *data,
72 : void *userdata) {
73 0 : Disabled reason = ltype;
74 :
75 0 : switch(reason) {
76 : case DISABLED_CONFIGURATION:
77 0 : log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
78 : "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
79 0 : break;
80 : case DISABLED_LEGACY:
81 0 : log_syntax(unit, LOG_INFO, filename, line, EINVAL,
82 : "Support for option %s= has been removed and it is ignored", lvalue);
83 0 : break;
84 : case DISABLED_EXPERIMENTAL:
85 0 : log_syntax(unit, LOG_INFO, filename, line, EINVAL,
86 : "Support for option %s= has not yet been enabled and it is ignored", lvalue);
87 0 : break;
88 : };
89 :
90 0 : return 0;
91 : }
92 :
93 82 : int config_parse_unit_deps(const char *unit,
94 : const char *filename,
95 : unsigned line,
96 : const char *section,
97 : unsigned section_line,
98 : const char *lvalue,
99 : int ltype,
100 : const char *rvalue,
101 : void *data,
102 : void *userdata) {
103 :
104 82 : UnitDependency d = ltype;
105 82 : Unit *u = userdata;
106 : const char *word, *state;
107 : size_t l;
108 :
109 82 : assert(filename);
110 82 : assert(lvalue);
111 82 : assert(rvalue);
112 :
113 274 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
114 384 : _cleanup_free_ char *t = NULL, *k = NULL;
115 : int r;
116 :
117 192 : t = strndup(word, l);
118 192 : if (!t)
119 0 : return log_oom();
120 :
121 192 : r = unit_name_printf(u, t, &k);
122 192 : if (r < 0) {
123 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
124 : "Failed to resolve specifiers, ignoring: %s", strerror(-r));
125 0 : continue;
126 : }
127 :
128 192 : r = unit_add_dependency_by_name(u, d, k, NULL, true);
129 192 : if (r < 0)
130 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
131 : "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
132 : }
133 82 : if (!isempty(state))
134 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring.");
135 :
136 82 : return 0;
137 : }
138 :
139 92 : int config_parse_unit_string_printf(
140 : const char *unit,
141 : const char *filename,
142 : unsigned line,
143 : const char *section,
144 : unsigned section_line,
145 : const char *lvalue,
146 : int ltype,
147 : const char *rvalue,
148 : void *data,
149 : void *userdata) {
150 :
151 184 : _cleanup_free_ char *k = NULL;
152 92 : Unit *u = userdata;
153 : int r;
154 :
155 92 : assert(filename);
156 92 : assert(lvalue);
157 92 : assert(rvalue);
158 92 : assert(u);
159 :
160 92 : r = unit_full_printf(u, rvalue, &k);
161 92 : if (r < 0) {
162 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
163 0 : return 0;
164 : }
165 :
166 92 : return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
167 : }
168 :
169 60 : int config_parse_unit_strv_printf(const char *unit,
170 : const char *filename,
171 : unsigned line,
172 : const char *section,
173 : unsigned section_line,
174 : const char *lvalue,
175 : int ltype,
176 : const char *rvalue,
177 : void *data,
178 : void *userdata) {
179 :
180 60 : Unit *u = userdata;
181 120 : _cleanup_free_ char *k = NULL;
182 : int r;
183 :
184 60 : assert(filename);
185 60 : assert(lvalue);
186 60 : assert(rvalue);
187 60 : assert(u);
188 :
189 60 : r = unit_full_printf(u, rvalue, &k);
190 60 : if (r < 0)
191 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
192 : "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
193 :
194 120 : return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
195 60 : k ? k : rvalue, data, userdata);
196 : }
197 :
198 0 : int config_parse_unit_path_printf(const char *unit,
199 : const char *filename,
200 : unsigned line,
201 : const char *section,
202 : unsigned section_line,
203 : const char *lvalue,
204 : int ltype,
205 : const char *rvalue,
206 : void *data,
207 : void *userdata) {
208 :
209 0 : _cleanup_free_ char *k = NULL;
210 0 : Unit *u = userdata;
211 : int r;
212 :
213 0 : assert(filename);
214 0 : assert(lvalue);
215 0 : assert(rvalue);
216 0 : assert(u);
217 :
218 0 : r = unit_full_printf(u, rvalue, &k);
219 0 : if (r < 0) {
220 0 : log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
221 0 : return 0;
222 : }
223 :
224 0 : return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
225 : }
226 :
227 0 : int config_parse_unit_path_strv_printf(
228 : const char *unit,
229 : const char *filename,
230 : unsigned line,
231 : const char *section,
232 : unsigned section_line,
233 : const char *lvalue,
234 : int ltype,
235 : const char *rvalue,
236 : void *data,
237 : void *userdata) {
238 :
239 0 : char ***x = data;
240 : const char *word, *state;
241 0 : Unit *u = userdata;
242 : size_t l;
243 : int r;
244 :
245 0 : assert(filename);
246 0 : assert(lvalue);
247 0 : assert(rvalue);
248 0 : assert(u);
249 :
250 0 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
251 0 : _cleanup_free_ char *k = NULL;
252 0 : char t[l+1];
253 :
254 0 : memcpy(t, word, l);
255 0 : t[l] = 0;
256 :
257 0 : r = unit_full_printf(u, t, &k);
258 0 : if (r < 0) {
259 0 : log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", t, strerror(-r));
260 0 : return 0;
261 : }
262 :
263 0 : if (!utf8_is_valid(k)) {
264 0 : log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
265 0 : return 0;
266 : }
267 :
268 0 : if (!path_is_absolute(k)) {
269 0 : log_syntax(unit, LOG_ERR, filename, line, -r, "Symlink path %s is not absolute, ignoring: %s", k, strerror(-r));
270 0 : return 0;
271 : }
272 :
273 0 : path_kill_slashes(k);
274 :
275 0 : r = strv_push(x, k);
276 0 : if (r < 0)
277 0 : return log_oom();
278 :
279 0 : k = NULL;
280 : }
281 0 : if (!isempty(state))
282 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring.");
283 :
284 0 : return 0;
285 : }
286 :
287 0 : int config_parse_socket_listen(const char *unit,
288 : const char *filename,
289 : unsigned line,
290 : const char *section,
291 : unsigned section_line,
292 : const char *lvalue,
293 : int ltype,
294 : const char *rvalue,
295 : void *data,
296 : void *userdata) {
297 :
298 0 : _cleanup_free_ SocketPort *p = NULL;
299 : SocketPort *tail;
300 : Socket *s;
301 : int r;
302 :
303 0 : assert(filename);
304 0 : assert(lvalue);
305 0 : assert(rvalue);
306 0 : assert(data);
307 :
308 0 : s = SOCKET(data);
309 :
310 0 : if (isempty(rvalue)) {
311 : /* An empty assignment removes all ports */
312 0 : socket_free_ports(s);
313 0 : return 0;
314 : }
315 :
316 0 : p = new0(SocketPort, 1);
317 0 : if (!p)
318 0 : return log_oom();
319 :
320 0 : if (ltype != SOCKET_SOCKET) {
321 :
322 0 : p->type = ltype;
323 0 : r = unit_full_printf(UNIT(s), rvalue, &p->path);
324 0 : if (r < 0) {
325 0 : p->path = strdup(rvalue);
326 0 : if (!p->path)
327 0 : return log_oom();
328 : else
329 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
330 : "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
331 : }
332 :
333 0 : path_kill_slashes(p->path);
334 :
335 0 : } else if (streq(lvalue, "ListenNetlink")) {
336 0 : _cleanup_free_ char *k = NULL;
337 :
338 0 : p->type = SOCKET_SOCKET;
339 0 : r = unit_full_printf(UNIT(s), rvalue, &k);
340 0 : if (r < 0)
341 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
342 : "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
343 :
344 0 : r = socket_address_parse_netlink(&p->address, k ?: rvalue);
345 0 : if (r < 0) {
346 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
347 : "Failed to parse address value, ignoring: %s", rvalue);
348 0 : return 0;
349 : }
350 :
351 : } else {
352 0 : _cleanup_free_ char *k = NULL;
353 :
354 0 : p->type = SOCKET_SOCKET;
355 0 : r = unit_full_printf(UNIT(s), rvalue, &k);
356 0 : if (r < 0)
357 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
358 : "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
359 :
360 0 : r = socket_address_parse_and_warn(&p->address, k ? k : rvalue);
361 0 : if (r < 0) {
362 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
363 : "Failed to parse address value, ignoring: %s", rvalue);
364 0 : return 0;
365 : }
366 :
367 0 : if (streq(lvalue, "ListenStream"))
368 0 : p->address.type = SOCK_STREAM;
369 0 : else if (streq(lvalue, "ListenDatagram"))
370 0 : p->address.type = SOCK_DGRAM;
371 : else {
372 0 : assert(streq(lvalue, "ListenSequentialPacket"));
373 0 : p->address.type = SOCK_SEQPACKET;
374 : }
375 :
376 0 : if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
377 0 : log_syntax(unit, LOG_ERR, filename, line, EOPNOTSUPP,
378 : "Address family not supported, ignoring: %s", rvalue);
379 0 : return 0;
380 : }
381 : }
382 :
383 0 : p->fd = -1;
384 0 : p->socket = s;
385 :
386 0 : if (s->ports) {
387 0 : LIST_FIND_TAIL(port, s->ports, tail);
388 0 : LIST_INSERT_AFTER(port, s->ports, tail, p);
389 : } else
390 0 : LIST_PREPEND(port, s->ports, p);
391 0 : p = NULL;
392 :
393 0 : return 0;
394 : }
395 :
396 0 : int config_parse_socket_bind(const char *unit,
397 : const char *filename,
398 : unsigned line,
399 : const char *section,
400 : unsigned section_line,
401 : const char *lvalue,
402 : int ltype,
403 : const char *rvalue,
404 : void *data,
405 : void *userdata) {
406 :
407 : Socket *s;
408 : SocketAddressBindIPv6Only b;
409 :
410 0 : assert(filename);
411 0 : assert(lvalue);
412 0 : assert(rvalue);
413 0 : assert(data);
414 :
415 0 : s = SOCKET(data);
416 :
417 0 : b = socket_address_bind_ipv6_only_from_string(rvalue);
418 0 : if (b < 0) {
419 : int r;
420 :
421 0 : r = parse_boolean(rvalue);
422 0 : if (r < 0) {
423 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
424 : "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
425 0 : return 0;
426 : }
427 :
428 0 : s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
429 : } else
430 0 : s->bind_ipv6_only = b;
431 :
432 0 : return 0;
433 : }
434 :
435 0 : int config_parse_exec_nice(const char *unit,
436 : const char *filename,
437 : unsigned line,
438 : const char *section,
439 : unsigned section_line,
440 : const char *lvalue,
441 : int ltype,
442 : const char *rvalue,
443 : void *data,
444 : void *userdata) {
445 :
446 0 : ExecContext *c = data;
447 : int priority, r;
448 :
449 0 : assert(filename);
450 0 : assert(lvalue);
451 0 : assert(rvalue);
452 0 : assert(data);
453 :
454 0 : r = safe_atoi(rvalue, &priority);
455 0 : if (r < 0) {
456 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
457 : "Failed to parse nice priority, ignoring: %s. ", rvalue);
458 0 : return 0;
459 : }
460 :
461 0 : if (priority < PRIO_MIN || priority >= PRIO_MAX) {
462 0 : log_syntax(unit, LOG_ERR, filename, line, ERANGE,
463 : "Nice priority out of range, ignoring: %s", rvalue);
464 0 : return 0;
465 : }
466 :
467 0 : c->nice = priority;
468 0 : c->nice_set = true;
469 :
470 0 : return 0;
471 : }
472 :
473 0 : int config_parse_exec_oom_score_adjust(const char* unit,
474 : const char *filename,
475 : unsigned line,
476 : const char *section,
477 : unsigned section_line,
478 : const char *lvalue,
479 : int ltype,
480 : const char *rvalue,
481 : void *data,
482 : void *userdata) {
483 :
484 0 : ExecContext *c = data;
485 : int oa, r;
486 :
487 0 : assert(filename);
488 0 : assert(lvalue);
489 0 : assert(rvalue);
490 0 : assert(data);
491 :
492 0 : r = safe_atoi(rvalue, &oa);
493 0 : if (r < 0) {
494 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
495 : "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
496 0 : return 0;
497 : }
498 :
499 0 : if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
500 0 : log_syntax(unit, LOG_ERR, filename, line, ERANGE,
501 : "OOM score adjust value out of range, ignoring: %s", rvalue);
502 0 : return 0;
503 : }
504 :
505 0 : c->oom_score_adjust = oa;
506 0 : c->oom_score_adjust_set = true;
507 :
508 0 : return 0;
509 : }
510 :
511 68 : int config_parse_exec(
512 : const char *unit,
513 : const char *filename,
514 : unsigned line,
515 : const char *section,
516 : unsigned section_line,
517 : const char *lvalue,
518 : int ltype,
519 : const char *rvalue,
520 : void *data,
521 : void *userdata) {
522 :
523 68 : ExecCommand **e = data;
524 : const char *p;
525 : bool semicolon;
526 : int r;
527 :
528 68 : assert(filename);
529 68 : assert(lvalue);
530 68 : assert(rvalue);
531 68 : assert(e);
532 :
533 68 : e += ltype;
534 :
535 68 : rvalue += strspn(rvalue, WHITESPACE);
536 68 : p = rvalue;
537 :
538 68 : if (isempty(rvalue)) {
539 : /* An empty assignment resets the list */
540 2 : *e = exec_command_free_list(*e);
541 2 : return 0;
542 : }
543 :
544 : do {
545 : int i;
546 140 : _cleanup_strv_free_ char **n = NULL;
547 70 : size_t nlen = 0, nbufsize = 0;
548 140 : _cleanup_free_ ExecCommand *nce = NULL;
549 140 : _cleanup_free_ char *path = NULL, *firstword = NULL;
550 : char *f;
551 70 : bool separate_argv0 = false, ignore = false;
552 :
553 70 : semicolon = false;
554 :
555 70 : r = unquote_first_word_and_warn(&p, &firstword, UNQUOTE_CUNESCAPE, unit, filename, line, rvalue);
556 70 : if (r <= 0)
557 2 : return 0;
558 :
559 68 : f = firstword;
560 89 : for (i = 0; i < 2; i++) {
561 : /* We accept an absolute path as first argument, or
562 : * alternatively an absolute prefixed with @ to allow
563 : * overriding of argv[0]. */
564 81 : if (*f == '-' && !ignore)
565 11 : ignore = true;
566 70 : else if (*f == '@' && !separate_argv0)
567 10 : separate_argv0 = true;
568 : else
569 : break;
570 21 : f ++;
571 : }
572 :
573 68 : if (isempty(f)) {
574 : /* First word is either "-" or "@" with no command. */
575 2 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
576 : "Empty path in command line, ignoring: \"%s\"", rvalue);
577 2 : return 0;
578 : }
579 :
580 66 : if (!string_is_safe(f)) {
581 12 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
582 : "Executable path contains special characters, ignoring: %s", rvalue);
583 12 : return 0;
584 : }
585 54 : if (!path_is_absolute(f)) {
586 3 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
587 : "Executable path is not absolute, ignoring: %s", rvalue);
588 3 : return 0;
589 : }
590 51 : if (endswith(f, "/")) {
591 1 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
592 : "Executable path specifies a directory, ignoring: %s", rvalue);
593 1 : return 0;
594 : }
595 :
596 50 : if (f == firstword) {
597 41 : path = firstword;
598 41 : firstword = NULL;
599 : } else {
600 9 : path = strdup(f);
601 9 : if (!path)
602 0 : return log_oom();
603 : }
604 :
605 50 : if (!separate_argv0) {
606 41 : if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
607 0 : return log_oom();
608 41 : f = strdup(path);
609 41 : if (!f)
610 0 : return log_oom();
611 41 : n[nlen++] = f;
612 41 : n[nlen] = NULL;
613 : }
614 :
615 50 : path_kill_slashes(path);
616 :
617 : for (;;) {
618 170 : _cleanup_free_ char *word = NULL;
619 :
620 : /* Check explicitly for an unquoted semicolon as
621 : * command separator token. */
622 85 : if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
623 4 : p ++;
624 4 : p += strspn(p, WHITESPACE);
625 4 : semicolon = true;
626 4 : break;
627 : }
628 :
629 : /* Check for \; explicitly, to not confuse it with \\;
630 : * or "\;" or "\\;" etc. unquote_first_word would
631 : * return the same for all of those. */
632 81 : if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
633 2 : p += 2;
634 2 : p += strspn(p, WHITESPACE);
635 2 : if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
636 0 : return log_oom();
637 2 : f = strdup(";");
638 2 : if (!f)
639 0 : return log_oom();
640 2 : n[nlen++] = f;
641 2 : n[nlen] = NULL;
642 2 : continue;
643 : }
644 :
645 79 : r = unquote_first_word_and_warn(&p, &word, UNQUOTE_CUNESCAPE, unit, filename, line, rvalue);
646 79 : if (r == 0)
647 44 : break;
648 35 : else if (r < 0)
649 2 : return 0;
650 :
651 33 : if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
652 0 : return log_oom();
653 33 : n[nlen++] = word;
654 33 : n[nlen] = NULL;
655 33 : word = NULL;
656 35 : }
657 :
658 95 : if (!n || !n[0]) {
659 1 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
660 : "Empty executable name or zeroeth argument, ignoring: %s", rvalue);
661 1 : return 0;
662 : }
663 :
664 47 : nce = new0(ExecCommand, 1);
665 47 : if (!nce)
666 0 : return log_oom();
667 :
668 47 : nce->argv = n;
669 47 : nce->path = path;
670 47 : nce->ignore = ignore;
671 :
672 47 : exec_command_append_list(e, nce);
673 :
674 : /* Do not _cleanup_free_ these. */
675 47 : n = NULL;
676 47 : path = NULL;
677 47 : nce = NULL;
678 :
679 47 : rvalue = p;
680 47 : } while (semicolon);
681 :
682 43 : return 0;
683 : }
684 :
685 10 : DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
686 0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
687 :
688 0 : int config_parse_socket_bindtodevice(const char* unit,
689 : const char *filename,
690 : unsigned line,
691 : const char *section,
692 : unsigned section_line,
693 : const char *lvalue,
694 : int ltype,
695 : const char *rvalue,
696 : void *data,
697 : void *userdata) {
698 :
699 0 : Socket *s = data;
700 : char *n;
701 :
702 0 : assert(filename);
703 0 : assert(lvalue);
704 0 : assert(rvalue);
705 0 : assert(data);
706 :
707 0 : if (rvalue[0] && !streq(rvalue, "*")) {
708 0 : n = strdup(rvalue);
709 0 : if (!n)
710 0 : return log_oom();
711 : } else
712 0 : n = NULL;
713 :
714 0 : free(s->bind_to_device);
715 0 : s->bind_to_device = n;
716 :
717 0 : return 0;
718 : }
719 :
720 0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
721 0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
722 :
723 0 : int config_parse_exec_io_class(const char *unit,
724 : const char *filename,
725 : unsigned line,
726 : const char *section,
727 : unsigned section_line,
728 : const char *lvalue,
729 : int ltype,
730 : const char *rvalue,
731 : void *data,
732 : void *userdata) {
733 :
734 0 : ExecContext *c = data;
735 : int x;
736 :
737 0 : assert(filename);
738 0 : assert(lvalue);
739 0 : assert(rvalue);
740 0 : assert(data);
741 :
742 0 : x = ioprio_class_from_string(rvalue);
743 0 : if (x < 0) {
744 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
745 : "Failed to parse IO scheduling class, ignoring: %s", rvalue);
746 0 : return 0;
747 : }
748 :
749 0 : c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
750 0 : c->ioprio_set = true;
751 :
752 0 : return 0;
753 : }
754 :
755 0 : int config_parse_exec_io_priority(const char *unit,
756 : const char *filename,
757 : unsigned line,
758 : const char *section,
759 : unsigned section_line,
760 : const char *lvalue,
761 : int ltype,
762 : const char *rvalue,
763 : void *data,
764 : void *userdata) {
765 :
766 0 : ExecContext *c = data;
767 : int i, r;
768 :
769 0 : assert(filename);
770 0 : assert(lvalue);
771 0 : assert(rvalue);
772 0 : assert(data);
773 :
774 0 : r = safe_atoi(rvalue, &i);
775 0 : if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
776 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
777 : "Failed to parse IO priority, ignoring: %s", rvalue);
778 0 : return 0;
779 : }
780 :
781 0 : c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
782 0 : c->ioprio_set = true;
783 :
784 0 : return 0;
785 : }
786 :
787 3 : int config_parse_exec_cpu_sched_policy(const char *unit,
788 : const char *filename,
789 : unsigned line,
790 : const char *section,
791 : unsigned section_line,
792 : const char *lvalue,
793 : int ltype,
794 : const char *rvalue,
795 : void *data,
796 : void *userdata) {
797 :
798 :
799 3 : ExecContext *c = data;
800 : int x;
801 :
802 3 : assert(filename);
803 3 : assert(lvalue);
804 3 : assert(rvalue);
805 3 : assert(data);
806 :
807 3 : x = sched_policy_from_string(rvalue);
808 3 : if (x < 0) {
809 0 : log_syntax(unit, LOG_ERR, filename, line, -x,
810 : "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
811 0 : return 0;
812 : }
813 :
814 3 : c->cpu_sched_policy = x;
815 : /* Moving to or from real-time policy? We need to adjust the priority */
816 3 : c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
817 3 : c->cpu_sched_set = true;
818 :
819 3 : return 0;
820 : }
821 :
822 7 : int config_parse_exec_cpu_sched_prio(const char *unit,
823 : const char *filename,
824 : unsigned line,
825 : const char *section,
826 : unsigned section_line,
827 : const char *lvalue,
828 : int ltype,
829 : const char *rvalue,
830 : void *data,
831 : void *userdata) {
832 :
833 7 : ExecContext *c = data;
834 : int i, min, max, r;
835 :
836 7 : assert(filename);
837 7 : assert(lvalue);
838 7 : assert(rvalue);
839 7 : assert(data);
840 :
841 7 : r = safe_atoi(rvalue, &i);
842 7 : if (r < 0) {
843 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
844 : "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
845 0 : return 0;
846 : }
847 :
848 : /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
849 7 : min = sched_get_priority_min(c->cpu_sched_policy);
850 7 : max = sched_get_priority_max(c->cpu_sched_policy);
851 :
852 7 : if (i < min || i > max) {
853 3 : log_syntax(unit, LOG_ERR, filename, line, ERANGE,
854 : "CPU scheduling priority is out of range, ignoring: %s", rvalue);
855 3 : return 0;
856 : }
857 :
858 4 : c->cpu_sched_priority = i;
859 4 : c->cpu_sched_set = true;
860 :
861 4 : return 0;
862 : }
863 :
864 0 : int config_parse_exec_cpu_affinity(const char *unit,
865 : const char *filename,
866 : unsigned line,
867 : const char *section,
868 : unsigned section_line,
869 : const char *lvalue,
870 : int ltype,
871 : const char *rvalue,
872 : void *data,
873 : void *userdata) {
874 :
875 0 : ExecContext *c = data;
876 : const char *word, *state;
877 : size_t l;
878 :
879 0 : assert(filename);
880 0 : assert(lvalue);
881 0 : assert(rvalue);
882 0 : assert(data);
883 :
884 0 : if (isempty(rvalue)) {
885 : /* An empty assignment resets the CPU list */
886 0 : if (c->cpuset)
887 0 : CPU_FREE(c->cpuset);
888 0 : c->cpuset = NULL;
889 0 : return 0;
890 : }
891 :
892 0 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
893 0 : _cleanup_free_ char *t = NULL;
894 : int r;
895 : unsigned cpu;
896 :
897 0 : t = strndup(word, l);
898 0 : if (!t)
899 0 : return log_oom();
900 :
901 0 : r = safe_atou(t, &cpu);
902 :
903 0 : if (!c->cpuset) {
904 0 : c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
905 0 : if (!c->cpuset)
906 0 : return log_oom();
907 : }
908 :
909 0 : if (r < 0 || cpu >= c->cpuset_ncpus) {
910 0 : log_syntax(unit, LOG_ERR, filename, line, ERANGE,
911 : "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
912 0 : return 0;
913 : }
914 :
915 0 : CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
916 : }
917 0 : if (!isempty(state))
918 0 : log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
919 : "Trailing garbage, ignoring.");
920 :
921 0 : return 0;
922 : }
923 :
924 0 : int config_parse_exec_capabilities(const char *unit,
925 : const char *filename,
926 : unsigned line,
927 : const char *section,
928 : unsigned section_line,
929 : const char *lvalue,
930 : int ltype,
931 : const char *rvalue,
932 : void *data,
933 : void *userdata) {
934 :
935 0 : ExecContext *c = data;
936 : cap_t cap;
937 :
938 0 : assert(filename);
939 0 : assert(lvalue);
940 0 : assert(rvalue);
941 0 : assert(data);
942 :
943 0 : cap = cap_from_text(rvalue);
944 0 : if (!cap) {
945 0 : log_syntax(unit, LOG_ERR, filename, line, errno,
946 : "Failed to parse capabilities, ignoring: %s", rvalue);
947 0 : return 0;
948 : }
949 :
950 0 : if (c->capabilities)
951 0 : cap_free(c->capabilities);
952 0 : c->capabilities = cap;
953 :
954 0 : return 0;
955 : }
956 :
957 0 : int config_parse_exec_secure_bits(const char *unit,
958 : const char *filename,
959 : unsigned line,
960 : const char *section,
961 : unsigned section_line,
962 : const char *lvalue,
963 : int ltype,
964 : const char *rvalue,
965 : void *data,
966 : void *userdata) {
967 :
968 0 : ExecContext *c = data;
969 : size_t l;
970 : const char *word, *state;
971 :
972 0 : assert(filename);
973 0 : assert(lvalue);
974 0 : assert(rvalue);
975 0 : assert(data);
976 :
977 0 : if (isempty(rvalue)) {
978 : /* An empty assignment resets the field */
979 0 : c->secure_bits = 0;
980 0 : return 0;
981 : }
982 :
983 0 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
984 0 : if (first_word(word, "keep-caps"))
985 0 : c->secure_bits |= 1<<SECURE_KEEP_CAPS;
986 0 : else if (first_word(word, "keep-caps-locked"))
987 0 : c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
988 0 : else if (first_word(word, "no-setuid-fixup"))
989 0 : c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
990 0 : else if (first_word(word, "no-setuid-fixup-locked"))
991 0 : c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
992 0 : else if (first_word(word, "noroot"))
993 0 : c->secure_bits |= 1<<SECURE_NOROOT;
994 0 : else if (first_word(word, "noroot-locked"))
995 0 : c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
996 : else {
997 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
998 : "Failed to parse secure bits, ignoring: %s", rvalue);
999 0 : return 0;
1000 : }
1001 : }
1002 0 : if (!isempty(state))
1003 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1004 : "Invalid syntax, garbage at the end, ignoring.");
1005 :
1006 0 : return 0;
1007 : }
1008 :
1009 0 : int config_parse_bounding_set(const char *unit,
1010 : const char *filename,
1011 : unsigned line,
1012 : const char *section,
1013 : unsigned section_line,
1014 : const char *lvalue,
1015 : int ltype,
1016 : const char *rvalue,
1017 : void *data,
1018 : void *userdata) {
1019 :
1020 0 : uint64_t *capability_bounding_set_drop = data;
1021 : const char *word, *state;
1022 : size_t l;
1023 0 : bool invert = false;
1024 0 : uint64_t sum = 0;
1025 :
1026 0 : assert(filename);
1027 0 : assert(lvalue);
1028 0 : assert(rvalue);
1029 0 : assert(data);
1030 :
1031 0 : if (rvalue[0] == '~') {
1032 0 : invert = true;
1033 0 : rvalue++;
1034 : }
1035 :
1036 : /* Note that we store this inverted internally, since the
1037 : * kernel wants it like this. But we actually expose it
1038 : * non-inverted everywhere to have a fully normalized
1039 : * interface. */
1040 :
1041 0 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
1042 0 : _cleanup_free_ char *t = NULL;
1043 : int cap;
1044 :
1045 0 : t = strndup(word, l);
1046 0 : if (!t)
1047 0 : return log_oom();
1048 :
1049 0 : cap = capability_from_name(t);
1050 0 : if (cap < 0) {
1051 0 : log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse capability in bounding set, ignoring: %s", t);
1052 0 : continue;
1053 : }
1054 :
1055 0 : sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
1056 : }
1057 0 : if (!isempty(state))
1058 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1059 : "Trailing garbage, ignoring.");
1060 :
1061 0 : if (invert)
1062 0 : *capability_bounding_set_drop |= sum;
1063 : else
1064 0 : *capability_bounding_set_drop |= ~sum;
1065 :
1066 0 : return 0;
1067 : }
1068 :
1069 0 : int config_parse_limit(const char *unit,
1070 : const char *filename,
1071 : unsigned line,
1072 : const char *section,
1073 : unsigned section_line,
1074 : const char *lvalue,
1075 : int ltype,
1076 : const char *rvalue,
1077 : void *data,
1078 : void *userdata) {
1079 :
1080 0 : struct rlimit **rl = data;
1081 : unsigned long long u;
1082 :
1083 0 : assert(filename);
1084 0 : assert(lvalue);
1085 0 : assert(rvalue);
1086 0 : assert(data);
1087 :
1088 0 : rl += ltype;
1089 :
1090 0 : if (streq(rvalue, "infinity"))
1091 0 : u = (unsigned long long) RLIM_INFINITY;
1092 : else {
1093 : int r;
1094 :
1095 0 : r = safe_atollu(rvalue, &u);
1096 0 : if (r < 0) {
1097 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1098 : "Failed to parse resource value, ignoring: %s", rvalue);
1099 0 : return 0;
1100 : }
1101 : }
1102 :
1103 0 : if (!*rl) {
1104 0 : *rl = new(struct rlimit, 1);
1105 0 : if (!*rl)
1106 0 : return log_oom();
1107 : }
1108 :
1109 0 : (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
1110 0 : return 0;
1111 : }
1112 :
1113 : #ifdef HAVE_SYSV_COMPAT
1114 0 : int config_parse_sysv_priority(const char *unit,
1115 : const char *filename,
1116 : unsigned line,
1117 : const char *section,
1118 : unsigned section_line,
1119 : const char *lvalue,
1120 : int ltype,
1121 : const char *rvalue,
1122 : void *data,
1123 : void *userdata) {
1124 :
1125 0 : int *priority = data;
1126 : int i, r;
1127 :
1128 0 : assert(filename);
1129 0 : assert(lvalue);
1130 0 : assert(rvalue);
1131 0 : assert(data);
1132 :
1133 0 : r = safe_atoi(rvalue, &i);
1134 0 : if (r < 0 || i < 0) {
1135 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1136 : "Failed to parse SysV start priority, ignoring: %s", rvalue);
1137 0 : return 0;
1138 : }
1139 :
1140 0 : *priority = (int) i;
1141 0 : return 0;
1142 : }
1143 : #endif
1144 :
1145 0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
1146 :
1147 0 : int config_parse_kill_signal(const char *unit,
1148 : const char *filename,
1149 : unsigned line,
1150 : const char *section,
1151 : unsigned section_line,
1152 : const char *lvalue,
1153 : int ltype,
1154 : const char *rvalue,
1155 : void *data,
1156 : void *userdata) {
1157 :
1158 0 : int *sig = data;
1159 : int r;
1160 :
1161 0 : assert(filename);
1162 0 : assert(lvalue);
1163 0 : assert(rvalue);
1164 0 : assert(sig);
1165 :
1166 0 : r = signal_from_string_try_harder(rvalue);
1167 0 : if (r <= 0) {
1168 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1169 : "Failed to parse kill signal, ignoring: %s", rvalue);
1170 0 : return 0;
1171 : }
1172 :
1173 0 : *sig = r;
1174 0 : return 0;
1175 : }
1176 :
1177 0 : int config_parse_exec_mount_flags(const char *unit,
1178 : const char *filename,
1179 : unsigned line,
1180 : const char *section,
1181 : unsigned section_line,
1182 : const char *lvalue,
1183 : int ltype,
1184 : const char *rvalue,
1185 : void *data,
1186 : void *userdata) {
1187 :
1188 0 : ExecContext *c = data;
1189 : const char *word, *state;
1190 : size_t l;
1191 0 : unsigned long flags = 0;
1192 :
1193 0 : assert(filename);
1194 0 : assert(lvalue);
1195 0 : assert(rvalue);
1196 0 : assert(data);
1197 :
1198 0 : FOREACH_WORD_SEPARATOR(word, l, rvalue, ", ", state) {
1199 0 : _cleanup_free_ char *t;
1200 :
1201 0 : t = strndup(word, l);
1202 0 : if (!t)
1203 0 : return log_oom();
1204 :
1205 0 : if (streq(t, "shared"))
1206 0 : flags = MS_SHARED;
1207 0 : else if (streq(t, "slave"))
1208 0 : flags = MS_SLAVE;
1209 0 : else if (streq(t, "private"))
1210 0 : flags = MS_PRIVATE;
1211 : else {
1212 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse mount flag %s, ignoring: %s", t, rvalue);
1213 0 : return 0;
1214 : }
1215 : }
1216 0 : if (!isempty(state))
1217 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
1218 :
1219 0 : c->mount_flags = flags;
1220 0 : return 0;
1221 : }
1222 :
1223 0 : int config_parse_exec_selinux_context(
1224 : const char *unit,
1225 : const char *filename,
1226 : unsigned line,
1227 : const char *section,
1228 : unsigned section_line,
1229 : const char *lvalue,
1230 : int ltype,
1231 : const char *rvalue,
1232 : void *data,
1233 : void *userdata) {
1234 :
1235 0 : ExecContext *c = data;
1236 0 : Unit *u = userdata;
1237 : bool ignore;
1238 : char *k;
1239 : int r;
1240 :
1241 0 : assert(filename);
1242 0 : assert(lvalue);
1243 0 : assert(rvalue);
1244 0 : assert(data);
1245 :
1246 0 : if (isempty(rvalue)) {
1247 0 : free(c->selinux_context);
1248 0 : c->selinux_context = NULL;
1249 0 : c->selinux_context_ignore = false;
1250 0 : return 0;
1251 : }
1252 :
1253 0 : if (rvalue[0] == '-') {
1254 0 : ignore = true;
1255 0 : rvalue++;
1256 : } else
1257 0 : ignore = false;
1258 :
1259 0 : r = unit_name_printf(u, rvalue, &k);
1260 0 : if (r < 0) {
1261 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1262 : "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1263 0 : return 0;
1264 : }
1265 :
1266 0 : free(c->selinux_context);
1267 0 : c->selinux_context = k;
1268 0 : c->selinux_context_ignore = ignore;
1269 :
1270 0 : return 0;
1271 : }
1272 :
1273 0 : int config_parse_exec_apparmor_profile(
1274 : const char *unit,
1275 : const char *filename,
1276 : unsigned line,
1277 : const char *section,
1278 : unsigned section_line,
1279 : const char *lvalue,
1280 : int ltype,
1281 : const char *rvalue,
1282 : void *data,
1283 : void *userdata) {
1284 :
1285 0 : ExecContext *c = data;
1286 0 : Unit *u = userdata;
1287 : bool ignore;
1288 : char *k;
1289 : int r;
1290 :
1291 0 : assert(filename);
1292 0 : assert(lvalue);
1293 0 : assert(rvalue);
1294 0 : assert(data);
1295 :
1296 0 : if (isempty(rvalue)) {
1297 0 : free(c->apparmor_profile);
1298 0 : c->apparmor_profile = NULL;
1299 0 : c->apparmor_profile_ignore = false;
1300 0 : return 0;
1301 : }
1302 :
1303 0 : if (rvalue[0] == '-') {
1304 0 : ignore = true;
1305 0 : rvalue++;
1306 : } else
1307 0 : ignore = false;
1308 :
1309 0 : r = unit_name_printf(u, rvalue, &k);
1310 0 : if (r < 0) {
1311 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1312 : "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1313 0 : return 0;
1314 : }
1315 :
1316 0 : free(c->apparmor_profile);
1317 0 : c->apparmor_profile = k;
1318 0 : c->apparmor_profile_ignore = ignore;
1319 :
1320 0 : return 0;
1321 : }
1322 :
1323 0 : int config_parse_exec_smack_process_label(
1324 : const char *unit,
1325 : const char *filename,
1326 : unsigned line,
1327 : const char *section,
1328 : unsigned section_line,
1329 : const char *lvalue,
1330 : int ltype,
1331 : const char *rvalue,
1332 : void *data,
1333 : void *userdata) {
1334 :
1335 0 : ExecContext *c = data;
1336 0 : Unit *u = userdata;
1337 : bool ignore;
1338 : char *k;
1339 : int r;
1340 :
1341 0 : assert(filename);
1342 0 : assert(lvalue);
1343 0 : assert(rvalue);
1344 0 : assert(data);
1345 :
1346 0 : if (isempty(rvalue)) {
1347 0 : free(c->smack_process_label);
1348 0 : c->smack_process_label = NULL;
1349 0 : c->smack_process_label_ignore = false;
1350 0 : return 0;
1351 : }
1352 :
1353 0 : if (rvalue[0] == '-') {
1354 0 : ignore = true;
1355 0 : rvalue++;
1356 : } else
1357 0 : ignore = false;
1358 :
1359 0 : r = unit_name_printf(u, rvalue, &k);
1360 0 : if (r < 0) {
1361 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1362 : "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1363 0 : return 0;
1364 : }
1365 :
1366 0 : free(c->smack_process_label);
1367 0 : c->smack_process_label = k;
1368 0 : c->smack_process_label_ignore = ignore;
1369 :
1370 0 : return 0;
1371 : }
1372 :
1373 0 : int config_parse_timer(const char *unit,
1374 : const char *filename,
1375 : unsigned line,
1376 : const char *section,
1377 : unsigned section_line,
1378 : const char *lvalue,
1379 : int ltype,
1380 : const char *rvalue,
1381 : void *data,
1382 : void *userdata) {
1383 :
1384 0 : Timer *t = data;
1385 0 : usec_t u = 0;
1386 : TimerValue *v;
1387 : TimerBase b;
1388 0 : CalendarSpec *c = NULL;
1389 :
1390 0 : assert(filename);
1391 0 : assert(lvalue);
1392 0 : assert(rvalue);
1393 0 : assert(data);
1394 :
1395 0 : if (isempty(rvalue)) {
1396 : /* Empty assignment resets list */
1397 0 : timer_free_values(t);
1398 0 : return 0;
1399 : }
1400 :
1401 0 : b = timer_base_from_string(lvalue);
1402 0 : if (b < 0) {
1403 0 : log_syntax(unit, LOG_ERR, filename, line, -b,
1404 : "Failed to parse timer base, ignoring: %s", lvalue);
1405 0 : return 0;
1406 : }
1407 :
1408 0 : if (b == TIMER_CALENDAR) {
1409 0 : if (calendar_spec_from_string(rvalue, &c) < 0) {
1410 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1411 : "Failed to parse calendar specification, ignoring: %s",
1412 : rvalue);
1413 0 : return 0;
1414 : }
1415 : } else {
1416 0 : if (parse_sec(rvalue, &u) < 0) {
1417 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1418 : "Failed to parse timer value, ignoring: %s",
1419 : rvalue);
1420 0 : return 0;
1421 : }
1422 : }
1423 :
1424 0 : v = new0(TimerValue, 1);
1425 0 : if (!v) {
1426 0 : calendar_spec_free(c);
1427 0 : return log_oom();
1428 : }
1429 :
1430 0 : v->base = b;
1431 0 : v->value = u;
1432 0 : v->calendar_spec = c;
1433 :
1434 0 : LIST_PREPEND(value, t->values, v);
1435 :
1436 0 : return 0;
1437 : }
1438 :
1439 1 : int config_parse_trigger_unit(
1440 : const char *unit,
1441 : const char *filename,
1442 : unsigned line,
1443 : const char *section,
1444 : unsigned section_line,
1445 : const char *lvalue,
1446 : int ltype,
1447 : const char *rvalue,
1448 : void *data,
1449 : void *userdata) {
1450 :
1451 2 : _cleanup_free_ char *p = NULL;
1452 1 : Unit *u = data;
1453 : UnitType type;
1454 : int r;
1455 :
1456 1 : assert(filename);
1457 1 : assert(lvalue);
1458 1 : assert(rvalue);
1459 1 : assert(data);
1460 :
1461 1 : if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1462 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1463 : "Multiple units to trigger specified, ignoring: %s", rvalue);
1464 0 : return 0;
1465 : }
1466 :
1467 1 : r = unit_name_printf(u, rvalue, &p);
1468 1 : if (r < 0)
1469 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1470 : "Failed to resolve specifiers, ignoring: %s", strerror(-r));
1471 :
1472 1 : type = unit_name_to_type(p ?: rvalue);
1473 1 : if (type < 0) {
1474 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1475 : "Unit type not valid, ignoring: %s", rvalue);
1476 0 : return 0;
1477 : }
1478 :
1479 1 : if (type == u->type) {
1480 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1481 : "Trigger cannot be of same type, ignoring: %s", rvalue);
1482 0 : return 0;
1483 : }
1484 :
1485 1 : r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
1486 1 : if (r < 0) {
1487 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1488 : "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
1489 0 : return 0;
1490 : }
1491 :
1492 1 : return 0;
1493 : }
1494 :
1495 7 : int config_parse_path_spec(const char *unit,
1496 : const char *filename,
1497 : unsigned line,
1498 : const char *section,
1499 : unsigned section_line,
1500 : const char *lvalue,
1501 : int ltype,
1502 : const char *rvalue,
1503 : void *data,
1504 : void *userdata) {
1505 :
1506 7 : Path *p = data;
1507 : PathSpec *s;
1508 : PathType b;
1509 14 : _cleanup_free_ char *k = NULL;
1510 : int r;
1511 :
1512 7 : assert(filename);
1513 7 : assert(lvalue);
1514 7 : assert(rvalue);
1515 7 : assert(data);
1516 :
1517 7 : if (isempty(rvalue)) {
1518 : /* Empty assignment clears list */
1519 0 : path_free_specs(p);
1520 0 : return 0;
1521 : }
1522 :
1523 7 : b = path_type_from_string(lvalue);
1524 7 : if (b < 0) {
1525 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1526 : "Failed to parse path type, ignoring: %s", lvalue);
1527 0 : return 0;
1528 : }
1529 :
1530 7 : r = unit_full_printf(UNIT(p), rvalue, &k);
1531 7 : if (r < 0) {
1532 0 : k = strdup(rvalue);
1533 0 : if (!k)
1534 0 : return log_oom();
1535 : else
1536 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1537 : "Failed to resolve unit specifiers on %s. Ignoring.",
1538 : rvalue);
1539 : }
1540 :
1541 7 : if (!path_is_absolute(k)) {
1542 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1543 : "Path is not absolute, ignoring: %s", k);
1544 0 : return 0;
1545 : }
1546 :
1547 7 : s = new0(PathSpec, 1);
1548 7 : if (!s)
1549 0 : return log_oom();
1550 :
1551 7 : s->unit = UNIT(p);
1552 7 : s->path = path_kill_slashes(k);
1553 7 : k = NULL;
1554 7 : s->type = b;
1555 7 : s->inotify_fd = -1;
1556 :
1557 7 : LIST_PREPEND(spec, p->specs, s);
1558 :
1559 7 : return 0;
1560 : }
1561 :
1562 0 : int config_parse_socket_service(
1563 : const char *unit,
1564 : const char *filename,
1565 : unsigned line,
1566 : const char *section,
1567 : unsigned section_line,
1568 : const char *lvalue,
1569 : int ltype,
1570 : const char *rvalue,
1571 : void *data,
1572 : void *userdata) {
1573 :
1574 0 : _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1575 0 : Socket *s = data;
1576 : int r;
1577 : Unit *x;
1578 0 : _cleanup_free_ char *p = NULL;
1579 :
1580 0 : assert(filename);
1581 0 : assert(lvalue);
1582 0 : assert(rvalue);
1583 0 : assert(data);
1584 :
1585 0 : r = unit_name_printf(UNIT(s), rvalue, &p);
1586 0 : if (r < 0) {
1587 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1588 0 : return 0;
1589 : }
1590 :
1591 0 : if (!endswith(p, ".service")) {
1592 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
1593 0 : return 0;
1594 : }
1595 :
1596 0 : r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
1597 0 : if (r < 0) {
1598 0 : log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1599 0 : return 0;
1600 : }
1601 :
1602 0 : unit_ref_set(&s->service, x);
1603 :
1604 0 : return 0;
1605 : }
1606 :
1607 0 : int config_parse_service_sockets(
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 0 : Service *s = data;
1620 : const char *word, *state;
1621 : size_t l;
1622 : int r;
1623 :
1624 0 : assert(filename);
1625 0 : assert(lvalue);
1626 0 : assert(rvalue);
1627 0 : assert(data);
1628 :
1629 0 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
1630 0 : _cleanup_free_ char *t = NULL, *k = NULL;
1631 :
1632 0 : t = strndup(word, l);
1633 0 : if (!t)
1634 0 : return log_oom();
1635 :
1636 0 : r = unit_name_printf(UNIT(s), t, &k);
1637 0 : if (r < 0) {
1638 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1639 0 : continue;
1640 : }
1641 :
1642 0 : if (!endswith(k, ".socket")) {
1643 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type socket, ignoring: %s", k);
1644 0 : continue;
1645 : }
1646 :
1647 0 : r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
1648 0 : if (r < 0)
1649 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
1650 :
1651 0 : r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
1652 0 : if (r < 0)
1653 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
1654 : }
1655 0 : if (!isempty(state))
1656 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
1657 :
1658 0 : return 0;
1659 : }
1660 :
1661 0 : int config_parse_bus_name(
1662 : const char *unit,
1663 : const char *filename,
1664 : unsigned line,
1665 : const char *section,
1666 : unsigned section_line,
1667 : const char *lvalue,
1668 : int ltype,
1669 : const char *rvalue,
1670 : void *data,
1671 : void *userdata) {
1672 :
1673 0 : _cleanup_free_ char *k = NULL;
1674 0 : Unit *u = userdata;
1675 : int r;
1676 :
1677 0 : assert(filename);
1678 0 : assert(lvalue);
1679 0 : assert(rvalue);
1680 0 : assert(u);
1681 :
1682 0 : r = unit_full_printf(u, rvalue, &k);
1683 0 : if (r < 0) {
1684 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
1685 0 : return 0;
1686 : }
1687 :
1688 0 : if (!service_name_is_valid(k)) {
1689 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bus name %s, ignoring.", k);
1690 0 : return 0;
1691 : }
1692 :
1693 0 : return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
1694 : }
1695 :
1696 0 : int config_parse_service_timeout(const char *unit,
1697 : const char *filename,
1698 : unsigned line,
1699 : const char *section,
1700 : unsigned section_line,
1701 : const char *lvalue,
1702 : int ltype,
1703 : const char *rvalue,
1704 : void *data,
1705 : void *userdata) {
1706 :
1707 0 : Service *s = userdata;
1708 : int r;
1709 :
1710 0 : assert(filename);
1711 0 : assert(lvalue);
1712 0 : assert(rvalue);
1713 0 : assert(s);
1714 :
1715 0 : r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
1716 : rvalue, data, userdata);
1717 0 : if (r < 0)
1718 0 : return r;
1719 :
1720 0 : if (streq(lvalue, "TimeoutSec")) {
1721 0 : s->start_timeout_defined = true;
1722 0 : s->timeout_stop_usec = s->timeout_start_usec;
1723 0 : } else if (streq(lvalue, "TimeoutStartSec"))
1724 0 : s->start_timeout_defined = true;
1725 :
1726 0 : return 0;
1727 : }
1728 :
1729 0 : int config_parse_busname_service(
1730 : const char *unit,
1731 : const char *filename,
1732 : unsigned line,
1733 : const char *section,
1734 : unsigned section_line,
1735 : const char *lvalue,
1736 : int ltype,
1737 : const char *rvalue,
1738 : void *data,
1739 : void *userdata) {
1740 :
1741 0 : _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1742 0 : BusName *n = data;
1743 : int r;
1744 : Unit *x;
1745 0 : _cleanup_free_ char *p = NULL;
1746 :
1747 0 : assert(filename);
1748 0 : assert(lvalue);
1749 0 : assert(rvalue);
1750 0 : assert(data);
1751 :
1752 0 : r = unit_name_printf(UNIT(n), rvalue, &p);
1753 0 : if (r < 0) {
1754 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1755 : "Failed to resolve specifiers, ignoring: %s", rvalue);
1756 0 : return 0;
1757 : }
1758 :
1759 0 : if (!endswith(p, ".service")) {
1760 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1761 : "Unit must be of type service, ignoring: %s", rvalue);
1762 0 : return 0;
1763 : }
1764 :
1765 0 : r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1766 0 : if (r < 0) {
1767 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1768 : "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
1769 0 : return 0;
1770 : }
1771 :
1772 0 : unit_ref_set(&n->service, x);
1773 :
1774 0 : return 0;
1775 : }
1776 :
1777 0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world, bus_policy_access, BusPolicyAccess, "Failed to parse bus name policy access");
1778 :
1779 0 : int config_parse_bus_policy(
1780 : const char *unit,
1781 : const char *filename,
1782 : unsigned line,
1783 : const char *section,
1784 : unsigned section_line,
1785 : const char *lvalue,
1786 : int ltype,
1787 : const char *rvalue,
1788 : void *data,
1789 : void *userdata) {
1790 :
1791 0 : _cleanup_free_ BusNamePolicy *p = NULL;
1792 0 : _cleanup_free_ char *id_str = NULL;
1793 0 : BusName *busname = data;
1794 : char *access_str;
1795 :
1796 0 : assert(filename);
1797 0 : assert(lvalue);
1798 0 : assert(rvalue);
1799 0 : assert(data);
1800 :
1801 0 : p = new0(BusNamePolicy, 1);
1802 0 : if (!p)
1803 0 : return log_oom();
1804 :
1805 0 : if (streq(lvalue, "AllowUser"))
1806 0 : p->type = BUSNAME_POLICY_TYPE_USER;
1807 0 : else if (streq(lvalue, "AllowGroup"))
1808 0 : p->type = BUSNAME_POLICY_TYPE_GROUP;
1809 : else
1810 0 : assert_not_reached("Unknown lvalue");
1811 :
1812 0 : id_str = strdup(rvalue);
1813 0 : if (!id_str)
1814 0 : return log_oom();
1815 :
1816 0 : access_str = strpbrk(id_str, WHITESPACE);
1817 0 : if (!access_str) {
1818 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1819 : "Invalid busname policy value '%s'", rvalue);
1820 0 : return 0;
1821 : }
1822 :
1823 0 : *access_str = '\0';
1824 0 : access_str++;
1825 0 : access_str += strspn(access_str, WHITESPACE);
1826 :
1827 0 : p->access = bus_policy_access_from_string(access_str);
1828 0 : if (p->access < 0) {
1829 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1830 : "Invalid busname policy access type '%s'", access_str);
1831 0 : return 0;
1832 : }
1833 :
1834 0 : p->name = id_str;
1835 0 : id_str = NULL;
1836 :
1837 0 : LIST_PREPEND(policy, busname->policy, p);
1838 0 : p = NULL;
1839 :
1840 0 : return 0;
1841 : }
1842 :
1843 0 : int config_parse_bus_endpoint_policy(
1844 : const char *unit,
1845 : const char *filename,
1846 : unsigned line,
1847 : const char *section,
1848 : unsigned section_line,
1849 : const char *lvalue,
1850 : int ltype,
1851 : const char *rvalue,
1852 : void *data,
1853 : void *userdata) {
1854 :
1855 0 : _cleanup_free_ char *name = NULL;
1856 : BusPolicyAccess access;
1857 0 : ExecContext *c = data;
1858 : char *access_str;
1859 : int r;
1860 :
1861 0 : assert(filename);
1862 0 : assert(lvalue);
1863 0 : assert(rvalue);
1864 0 : assert(data);
1865 :
1866 0 : name = strdup(rvalue);
1867 0 : if (!name)
1868 0 : return log_oom();
1869 :
1870 0 : access_str = strpbrk(name, WHITESPACE);
1871 0 : if (!access_str) {
1872 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1873 : "Invalid endpoint policy value '%s'", rvalue);
1874 0 : return 0;
1875 : }
1876 :
1877 0 : *access_str = '\0';
1878 0 : access_str++;
1879 0 : access_str += strspn(access_str, WHITESPACE);
1880 :
1881 0 : access = bus_policy_access_from_string(access_str);
1882 0 : if (access <= _BUS_POLICY_ACCESS_INVALID ||
1883 : access >= _BUS_POLICY_ACCESS_MAX) {
1884 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1885 : "Invalid endpoint policy access type '%s'", access_str);
1886 0 : return 0;
1887 : }
1888 :
1889 0 : if (!c->bus_endpoint) {
1890 0 : r = bus_endpoint_new(&c->bus_endpoint);
1891 :
1892 0 : if (r < 0)
1893 0 : return r;
1894 : }
1895 :
1896 0 : return bus_endpoint_add_policy(c->bus_endpoint, name, access);
1897 : }
1898 :
1899 0 : int config_parse_unit_env_file(const char *unit,
1900 : const char *filename,
1901 : unsigned line,
1902 : const char *section,
1903 : unsigned section_line,
1904 : const char *lvalue,
1905 : int ltype,
1906 : const char *rvalue,
1907 : void *data,
1908 : void *userdata) {
1909 :
1910 0 : char ***env = data;
1911 0 : Unit *u = userdata;
1912 0 : _cleanup_free_ char *n = NULL;
1913 : const char *s;
1914 : int r;
1915 :
1916 0 : assert(filename);
1917 0 : assert(lvalue);
1918 0 : assert(rvalue);
1919 0 : assert(data);
1920 :
1921 0 : if (isempty(rvalue)) {
1922 : /* Empty assignment frees the list */
1923 0 : strv_free(*env);
1924 0 : *env = NULL;
1925 0 : return 0;
1926 : }
1927 :
1928 0 : r = unit_full_printf(u, rvalue, &n);
1929 0 : if (r < 0)
1930 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
1931 : "Failed to resolve specifiers, ignoring: %s", rvalue);
1932 :
1933 0 : s = n ?: rvalue;
1934 0 : if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1935 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1936 : "Path '%s' is not absolute, ignoring.", s);
1937 0 : return 0;
1938 : }
1939 :
1940 0 : r = strv_extend(env, s);
1941 0 : if (r < 0)
1942 0 : return log_oom();
1943 :
1944 0 : return 0;
1945 : }
1946 :
1947 0 : int config_parse_environ(const char *unit,
1948 : const char *filename,
1949 : unsigned line,
1950 : const char *section,
1951 : unsigned section_line,
1952 : const char *lvalue,
1953 : int ltype,
1954 : const char *rvalue,
1955 : void *data,
1956 : void *userdata) {
1957 :
1958 0 : Unit *u = userdata;
1959 0 : char*** env = data;
1960 : const char *word, *state;
1961 : size_t l;
1962 0 : _cleanup_free_ char *k = NULL;
1963 : int r;
1964 :
1965 0 : assert(filename);
1966 0 : assert(lvalue);
1967 0 : assert(rvalue);
1968 0 : assert(data);
1969 :
1970 0 : if (isempty(rvalue)) {
1971 : /* Empty assignment resets the list */
1972 0 : strv_free(*env);
1973 0 : *env = NULL;
1974 0 : return 0;
1975 : }
1976 :
1977 0 : if (u) {
1978 0 : r = unit_full_printf(u, rvalue, &k);
1979 0 : if (r < 0)
1980 0 : log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1981 : }
1982 :
1983 0 : if (!k)
1984 0 : k = strdup(rvalue);
1985 0 : if (!k)
1986 0 : return log_oom();
1987 :
1988 0 : FOREACH_WORD_QUOTED(word, l, k, state) {
1989 0 : _cleanup_free_ char *n;
1990 : char **x;
1991 :
1992 0 : r = cunescape_length(word, l, 0, &n);
1993 0 : if (r < 0) {
1994 0 : log_syntax(unit, LOG_ERR, filename, line, r, "Couldn't unescape assignment, ignoring: %s", rvalue);
1995 0 : continue;
1996 : }
1997 :
1998 0 : if (!env_assignment_is_valid(n)) {
1999 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid environment assignment, ignoring: %s", rvalue);
2000 0 : continue;
2001 : }
2002 :
2003 0 : x = strv_env_set(*env, n);
2004 0 : if (!x)
2005 0 : return log_oom();
2006 :
2007 0 : strv_free(*env);
2008 0 : *env = x;
2009 : }
2010 0 : if (!isempty(state))
2011 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2012 : "Trailing garbage, ignoring.");
2013 :
2014 0 : return 0;
2015 : }
2016 :
2017 0 : int config_parse_ip_tos(const char *unit,
2018 : const char *filename,
2019 : unsigned line,
2020 : const char *section,
2021 : unsigned section_line,
2022 : const char *lvalue,
2023 : int ltype,
2024 : const char *rvalue,
2025 : void *data,
2026 : void *userdata) {
2027 :
2028 0 : int *ip_tos = data, x;
2029 :
2030 0 : assert(filename);
2031 0 : assert(lvalue);
2032 0 : assert(rvalue);
2033 0 : assert(data);
2034 :
2035 0 : x = ip_tos_from_string(rvalue);
2036 0 : if (x < 0) {
2037 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2038 : "Failed to parse IP TOS value, ignoring: %s", rvalue);
2039 0 : return 0;
2040 : }
2041 :
2042 0 : *ip_tos = x;
2043 0 : return 0;
2044 : }
2045 :
2046 0 : int config_parse_unit_condition_path(
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 : _cleanup_free_ char *p = NULL;
2059 0 : Condition **list = data, *c;
2060 0 : ConditionType t = ltype;
2061 : bool trigger, negate;
2062 0 : Unit *u = userdata;
2063 : int r;
2064 :
2065 0 : assert(filename);
2066 0 : assert(lvalue);
2067 0 : assert(rvalue);
2068 0 : assert(data);
2069 :
2070 0 : if (isempty(rvalue)) {
2071 : /* Empty assignment resets the list */
2072 0 : *list = condition_free_list(*list);
2073 0 : return 0;
2074 : }
2075 :
2076 0 : trigger = rvalue[0] == '|';
2077 0 : if (trigger)
2078 0 : rvalue++;
2079 :
2080 0 : negate = rvalue[0] == '!';
2081 0 : if (negate)
2082 0 : rvalue++;
2083 :
2084 0 : r = unit_full_printf(u, rvalue, &p);
2085 0 : if (r < 0) {
2086 0 : log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2087 0 : return 0;
2088 : }
2089 :
2090 0 : if (!path_is_absolute(p)) {
2091 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path in condition not absolute, ignoring: %s", p);
2092 0 : return 0;
2093 : }
2094 :
2095 0 : c = condition_new(t, p, trigger, negate);
2096 0 : if (!c)
2097 0 : return log_oom();
2098 :
2099 0 : LIST_PREPEND(conditions, *list, c);
2100 0 : return 0;
2101 : }
2102 :
2103 0 : int config_parse_unit_condition_string(
2104 : const char *unit,
2105 : const char *filename,
2106 : unsigned line,
2107 : const char *section,
2108 : unsigned section_line,
2109 : const char *lvalue,
2110 : int ltype,
2111 : const char *rvalue,
2112 : void *data,
2113 : void *userdata) {
2114 :
2115 0 : _cleanup_free_ char *s = NULL;
2116 0 : Condition **list = data, *c;
2117 0 : ConditionType t = ltype;
2118 : bool trigger, negate;
2119 0 : Unit *u = userdata;
2120 : int r;
2121 :
2122 0 : assert(filename);
2123 0 : assert(lvalue);
2124 0 : assert(rvalue);
2125 0 : assert(data);
2126 :
2127 0 : if (isempty(rvalue)) {
2128 : /* Empty assignment resets the list */
2129 0 : *list = condition_free_list(*list);
2130 0 : return 0;
2131 : }
2132 :
2133 0 : trigger = rvalue[0] == '|';
2134 0 : if (trigger)
2135 0 : rvalue++;
2136 :
2137 0 : negate = rvalue[0] == '!';
2138 0 : if (negate)
2139 0 : rvalue++;
2140 :
2141 0 : r = unit_full_printf(u, rvalue, &s);
2142 0 : if (r < 0) {
2143 0 : log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2144 0 : return 0;
2145 : }
2146 :
2147 0 : c = condition_new(t, s, trigger, negate);
2148 0 : if (!c)
2149 0 : return log_oom();
2150 :
2151 0 : LIST_PREPEND(conditions, *list, c);
2152 0 : return 0;
2153 : }
2154 :
2155 0 : int config_parse_unit_condition_null(
2156 : const char *unit,
2157 : const char *filename,
2158 : unsigned line,
2159 : const char *section,
2160 : unsigned section_line,
2161 : const char *lvalue,
2162 : int ltype,
2163 : const char *rvalue,
2164 : void *data,
2165 : void *userdata) {
2166 :
2167 0 : Condition **list = data, *c;
2168 : bool trigger, negate;
2169 : int b;
2170 :
2171 0 : assert(filename);
2172 0 : assert(lvalue);
2173 0 : assert(rvalue);
2174 0 : assert(data);
2175 :
2176 0 : if (isempty(rvalue)) {
2177 : /* Empty assignment resets the list */
2178 0 : *list = condition_free_list(*list);
2179 0 : return 0;
2180 : }
2181 :
2182 0 : trigger = rvalue[0] == '|';
2183 0 : if (trigger)
2184 0 : rvalue++;
2185 :
2186 0 : negate = rvalue[0] == '!';
2187 0 : if (negate)
2188 0 : rvalue++;
2189 :
2190 0 : b = parse_boolean(rvalue);
2191 0 : if (b < 0) {
2192 0 : log_syntax(unit, LOG_ERR, filename, line, -b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
2193 0 : return 0;
2194 : }
2195 :
2196 0 : if (!b)
2197 0 : negate = !negate;
2198 :
2199 0 : c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2200 0 : if (!c)
2201 0 : return log_oom();
2202 :
2203 0 : LIST_PREPEND(conditions, *list, c);
2204 0 : return 0;
2205 : }
2206 :
2207 0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
2208 0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action, failure_action, FailureAction, "Failed to parse failure action specifier");
2209 :
2210 10 : int config_parse_unit_requires_mounts_for(
2211 : const char *unit,
2212 : const char *filename,
2213 : unsigned line,
2214 : const char *section,
2215 : unsigned section_line,
2216 : const char *lvalue,
2217 : int ltype,
2218 : const char *rvalue,
2219 : void *data,
2220 : void *userdata) {
2221 :
2222 10 : Unit *u = userdata;
2223 : const char *word, *state;
2224 : size_t l;
2225 :
2226 10 : assert(filename);
2227 10 : assert(lvalue);
2228 10 : assert(rvalue);
2229 10 : assert(data);
2230 :
2231 40 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
2232 : int r;
2233 30 : _cleanup_free_ char *n;
2234 :
2235 30 : n = strndup(word, l);
2236 30 : if (!n)
2237 0 : return log_oom();
2238 :
2239 30 : if (!utf8_is_valid(n)) {
2240 0 : log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
2241 0 : continue;
2242 : }
2243 :
2244 30 : r = unit_require_mounts_for(u, n);
2245 30 : if (r < 0) {
2246 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
2247 : "Failed to add required mount for, ignoring: %s", rvalue);
2248 0 : continue;
2249 : }
2250 : }
2251 10 : if (!isempty(state))
2252 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2253 : "Trailing garbage, ignoring.");
2254 :
2255 10 : return 0;
2256 : }
2257 :
2258 60 : int config_parse_documentation(const char *unit,
2259 : const char *filename,
2260 : unsigned line,
2261 : const char *section,
2262 : unsigned section_line,
2263 : const char *lvalue,
2264 : int ltype,
2265 : const char *rvalue,
2266 : void *data,
2267 : void *userdata) {
2268 :
2269 60 : Unit *u = userdata;
2270 : int r;
2271 : char **a, **b;
2272 :
2273 60 : assert(filename);
2274 60 : assert(lvalue);
2275 60 : assert(rvalue);
2276 60 : assert(u);
2277 :
2278 60 : if (isempty(rvalue)) {
2279 : /* Empty assignment resets the list */
2280 0 : strv_free(u->documentation);
2281 0 : u->documentation = NULL;
2282 0 : return 0;
2283 : }
2284 :
2285 60 : r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
2286 : rvalue, data, userdata);
2287 60 : if (r < 0)
2288 0 : return r;
2289 :
2290 120 : for (a = b = u->documentation; a && *a; a++) {
2291 :
2292 60 : if (documentation_url_is_valid(*a))
2293 60 : *(b++) = *a;
2294 : else {
2295 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2296 : "Invalid URL, ignoring: %s", *a);
2297 0 : free(*a);
2298 : }
2299 : }
2300 60 : if (b)
2301 60 : *b = NULL;
2302 :
2303 60 : return r;
2304 : }
2305 :
2306 : #ifdef HAVE_SECCOMP
2307 0 : int config_parse_syscall_filter(
2308 : const char *unit,
2309 : const char *filename,
2310 : unsigned line,
2311 : const char *section,
2312 : unsigned section_line,
2313 : const char *lvalue,
2314 : int ltype,
2315 : const char *rvalue,
2316 : void *data,
2317 : void *userdata) {
2318 :
2319 : static const char default_syscalls[] =
2320 : "execve\0"
2321 : "exit\0"
2322 : "exit_group\0"
2323 : "rt_sigreturn\0"
2324 : "sigreturn\0";
2325 :
2326 0 : ExecContext *c = data;
2327 0 : Unit *u = userdata;
2328 0 : bool invert = false;
2329 : const char *word, *state;
2330 : size_t l;
2331 : int r;
2332 :
2333 0 : assert(filename);
2334 0 : assert(lvalue);
2335 0 : assert(rvalue);
2336 0 : assert(u);
2337 :
2338 0 : if (isempty(rvalue)) {
2339 : /* Empty assignment resets the list */
2340 0 : set_free(c->syscall_filter);
2341 0 : c->syscall_filter = NULL;
2342 0 : c->syscall_whitelist = false;
2343 0 : return 0;
2344 : }
2345 :
2346 0 : if (rvalue[0] == '~') {
2347 0 : invert = true;
2348 0 : rvalue++;
2349 : }
2350 :
2351 0 : if (!c->syscall_filter) {
2352 0 : c->syscall_filter = set_new(NULL);
2353 0 : if (!c->syscall_filter)
2354 0 : return log_oom();
2355 :
2356 0 : if (invert)
2357 : /* Allow everything but the ones listed */
2358 0 : c->syscall_whitelist = false;
2359 : else {
2360 : const char *i;
2361 :
2362 : /* Allow nothing but the ones listed */
2363 0 : c->syscall_whitelist = true;
2364 :
2365 : /* Accept default syscalls if we are on a whitelist */
2366 0 : NULSTR_FOREACH(i, default_syscalls) {
2367 : int id;
2368 :
2369 0 : id = seccomp_syscall_resolve_name(i);
2370 0 : if (id < 0)
2371 0 : continue;
2372 :
2373 0 : r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2374 0 : if (r == 0)
2375 0 : continue;
2376 0 : if (r < 0)
2377 0 : return log_oom();
2378 : }
2379 : }
2380 : }
2381 :
2382 0 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
2383 0 : _cleanup_free_ char *t = NULL;
2384 : int id;
2385 :
2386 0 : t = strndup(word, l);
2387 0 : if (!t)
2388 0 : return log_oom();
2389 :
2390 0 : id = seccomp_syscall_resolve_name(t);
2391 0 : if (id < 0) {
2392 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2393 : "Failed to parse system call, ignoring: %s", t);
2394 0 : continue;
2395 : }
2396 :
2397 : /* If we previously wanted to forbid a syscall and now
2398 : * we want to allow it, then remove it from the list
2399 : */
2400 0 : if (!invert == c->syscall_whitelist) {
2401 0 : r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2402 0 : if (r == 0)
2403 0 : continue;
2404 0 : if (r < 0)
2405 0 : return log_oom();
2406 : } else
2407 0 : set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
2408 : }
2409 0 : if (!isempty(state))
2410 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2411 : "Trailing garbage, ignoring.");
2412 :
2413 : /* Turn on NNP, but only if it wasn't configured explicitly
2414 : * before, and only if we are in user mode. */
2415 0 : if (!c->no_new_privileges_set && u->manager->running_as == MANAGER_USER)
2416 0 : c->no_new_privileges = true;
2417 :
2418 0 : return 0;
2419 : }
2420 :
2421 0 : int config_parse_syscall_archs(
2422 : const char *unit,
2423 : const char *filename,
2424 : unsigned line,
2425 : const char *section,
2426 : unsigned section_line,
2427 : const char *lvalue,
2428 : int ltype,
2429 : const char *rvalue,
2430 : void *data,
2431 : void *userdata) {
2432 :
2433 0 : Set **archs = data;
2434 : const char *word, *state;
2435 : size_t l;
2436 : int r;
2437 :
2438 0 : if (isempty(rvalue)) {
2439 0 : set_free(*archs);
2440 0 : *archs = NULL;
2441 0 : return 0;
2442 : }
2443 :
2444 0 : r = set_ensure_allocated(archs, NULL);
2445 0 : if (r < 0)
2446 0 : return log_oom();
2447 :
2448 0 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
2449 0 : _cleanup_free_ char *t = NULL;
2450 : uint32_t a;
2451 :
2452 0 : t = strndup(word, l);
2453 0 : if (!t)
2454 0 : return log_oom();
2455 :
2456 0 : r = seccomp_arch_from_string(t, &a);
2457 0 : if (r < 0) {
2458 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2459 : "Failed to parse system call architecture, ignoring: %s", t);
2460 0 : continue;
2461 : }
2462 :
2463 0 : r = set_put(*archs, UINT32_TO_PTR(a + 1));
2464 0 : if (r == 0)
2465 0 : continue;
2466 0 : if (r < 0)
2467 0 : return log_oom();
2468 : }
2469 0 : if (!isempty(state))
2470 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2471 : "Trailing garbage, ignoring.");
2472 :
2473 0 : return 0;
2474 : }
2475 :
2476 0 : int config_parse_syscall_errno(
2477 : const char *unit,
2478 : const char *filename,
2479 : unsigned line,
2480 : const char *section,
2481 : unsigned section_line,
2482 : const char *lvalue,
2483 : int ltype,
2484 : const char *rvalue,
2485 : void *data,
2486 : void *userdata) {
2487 :
2488 0 : ExecContext *c = data;
2489 : int e;
2490 :
2491 0 : assert(filename);
2492 0 : assert(lvalue);
2493 0 : assert(rvalue);
2494 :
2495 0 : if (isempty(rvalue)) {
2496 : /* Empty assignment resets to KILL */
2497 0 : c->syscall_errno = 0;
2498 0 : return 0;
2499 : }
2500 :
2501 0 : e = errno_from_name(rvalue);
2502 0 : if (e < 0) {
2503 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2504 : "Failed to parse error number, ignoring: %s", rvalue);
2505 0 : return 0;
2506 : }
2507 :
2508 0 : c->syscall_errno = e;
2509 0 : return 0;
2510 : }
2511 :
2512 0 : int config_parse_address_families(
2513 : const char *unit,
2514 : const char *filename,
2515 : unsigned line,
2516 : const char *section,
2517 : unsigned section_line,
2518 : const char *lvalue,
2519 : int ltype,
2520 : const char *rvalue,
2521 : void *data,
2522 : void *userdata) {
2523 :
2524 0 : ExecContext *c = data;
2525 0 : bool invert = false;
2526 : const char *word, *state;
2527 : size_t l;
2528 : int r;
2529 :
2530 0 : assert(filename);
2531 0 : assert(lvalue);
2532 0 : assert(rvalue);
2533 :
2534 0 : if (isempty(rvalue)) {
2535 : /* Empty assignment resets the list */
2536 0 : set_free(c->address_families);
2537 0 : c->address_families = NULL;
2538 0 : c->address_families_whitelist = false;
2539 0 : return 0;
2540 : }
2541 :
2542 0 : if (rvalue[0] == '~') {
2543 0 : invert = true;
2544 0 : rvalue++;
2545 : }
2546 :
2547 0 : if (!c->address_families) {
2548 0 : c->address_families = set_new(NULL);
2549 0 : if (!c->address_families)
2550 0 : return log_oom();
2551 :
2552 0 : c->address_families_whitelist = !invert;
2553 : }
2554 :
2555 0 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
2556 0 : _cleanup_free_ char *t = NULL;
2557 : int af;
2558 :
2559 0 : t = strndup(word, l);
2560 0 : if (!t)
2561 0 : return log_oom();
2562 :
2563 0 : af = af_from_name(t);
2564 0 : if (af <= 0) {
2565 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2566 : "Failed to parse address family, ignoring: %s", t);
2567 0 : continue;
2568 : }
2569 :
2570 : /* If we previously wanted to forbid an address family and now
2571 : * we want to allow it, then remove it from the list
2572 : */
2573 0 : if (!invert == c->address_families_whitelist) {
2574 0 : r = set_put(c->address_families, INT_TO_PTR(af));
2575 0 : if (r == 0)
2576 0 : continue;
2577 0 : if (r < 0)
2578 0 : return log_oom();
2579 : } else
2580 0 : set_remove(c->address_families, INT_TO_PTR(af));
2581 : }
2582 0 : if (!isempty(state))
2583 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2584 : "Trailing garbage, ignoring.");
2585 :
2586 0 : return 0;
2587 : }
2588 : #endif
2589 :
2590 3 : int config_parse_unit_slice(
2591 : const char *unit,
2592 : const char *filename,
2593 : unsigned line,
2594 : const char *section,
2595 : unsigned section_line,
2596 : const char *lvalue,
2597 : int ltype,
2598 : const char *rvalue,
2599 : void *data,
2600 : void *userdata) {
2601 :
2602 6 : _cleanup_free_ char *k = NULL;
2603 3 : Unit *u = userdata, *slice;
2604 : int r;
2605 :
2606 3 : assert(filename);
2607 3 : assert(lvalue);
2608 3 : assert(rvalue);
2609 3 : assert(u);
2610 :
2611 3 : r = unit_name_printf(u, rvalue, &k);
2612 3 : if (r < 0)
2613 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
2614 : "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2615 3 : if (!k) {
2616 0 : k = strdup(rvalue);
2617 0 : if (!k)
2618 0 : return log_oom();
2619 : }
2620 :
2621 3 : r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
2622 3 : if (r < 0) {
2623 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
2624 : "Failed to load slice unit %s. Ignoring.", k);
2625 0 : return 0;
2626 : }
2627 :
2628 3 : if (slice->type != UNIT_SLICE) {
2629 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2630 : "Slice unit %s is not a slice. Ignoring.", k);
2631 0 : return 0;
2632 : }
2633 :
2634 3 : unit_ref_set(&u->slice, slice);
2635 3 : return 0;
2636 : }
2637 :
2638 0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2639 :
2640 1 : int config_parse_cpu_shares(
2641 : const char *unit,
2642 : const char *filename,
2643 : unsigned line,
2644 : const char *section,
2645 : unsigned section_line,
2646 : const char *lvalue,
2647 : int ltype,
2648 : const char *rvalue,
2649 : void *data,
2650 : void *userdata) {
2651 :
2652 1 : unsigned long *shares = data, lu;
2653 : int r;
2654 :
2655 1 : assert(filename);
2656 1 : assert(lvalue);
2657 1 : assert(rvalue);
2658 :
2659 1 : if (isempty(rvalue)) {
2660 0 : *shares = (unsigned long) -1;
2661 0 : return 0;
2662 : }
2663 :
2664 1 : r = safe_atolu(rvalue, &lu);
2665 1 : if (r < 0 || lu <= 0) {
2666 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2667 : "CPU shares '%s' invalid. Ignoring.", rvalue);
2668 0 : return 0;
2669 : }
2670 :
2671 1 : *shares = lu;
2672 1 : return 0;
2673 : }
2674 :
2675 0 : int config_parse_cpu_quota(
2676 : const char *unit,
2677 : const char *filename,
2678 : unsigned line,
2679 : const char *section,
2680 : unsigned section_line,
2681 : const char *lvalue,
2682 : int ltype,
2683 : const char *rvalue,
2684 : void *data,
2685 : void *userdata) {
2686 :
2687 0 : CGroupContext *c = data;
2688 : double percent;
2689 :
2690 0 : assert(filename);
2691 0 : assert(lvalue);
2692 0 : assert(rvalue);
2693 :
2694 0 : if (isempty(rvalue)) {
2695 0 : c->cpu_quota_per_sec_usec = USEC_INFINITY;
2696 0 : return 0;
2697 : }
2698 :
2699 0 : if (!endswith(rvalue, "%")) {
2700 :
2701 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2702 : "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue);
2703 0 : return 0;
2704 : }
2705 :
2706 0 : if (sscanf(rvalue, "%lf%%", &percent) != 1 || percent <= 0) {
2707 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2708 : "CPU quota '%s' invalid. Ignoring.", rvalue);
2709 0 : return 0;
2710 : }
2711 :
2712 0 : c->cpu_quota_per_sec_usec = (usec_t) (percent * USEC_PER_SEC / 100);
2713 :
2714 0 : return 0;
2715 : }
2716 :
2717 1 : int config_parse_memory_limit(
2718 : 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 1 : CGroupContext *c = data;
2730 : off_t bytes;
2731 : int r;
2732 :
2733 1 : if (isempty(rvalue)) {
2734 0 : c->memory_limit = (uint64_t) -1;
2735 0 : return 0;
2736 : }
2737 :
2738 : assert_cc(sizeof(uint64_t) == sizeof(off_t));
2739 :
2740 1 : r = parse_size(rvalue, 1024, &bytes);
2741 1 : if (r < 0) {
2742 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2743 : "Memory limit '%s' invalid. Ignoring.", rvalue);
2744 0 : return 0;
2745 : }
2746 :
2747 1 : c->memory_limit = (uint64_t) bytes;
2748 1 : return 0;
2749 : }
2750 :
2751 0 : int config_parse_device_allow(
2752 : const char *unit,
2753 : const char *filename,
2754 : unsigned line,
2755 : const char *section,
2756 : unsigned section_line,
2757 : const char *lvalue,
2758 : int ltype,
2759 : const char *rvalue,
2760 : void *data,
2761 : void *userdata) {
2762 :
2763 0 : _cleanup_free_ char *path = NULL;
2764 0 : CGroupContext *c = data;
2765 : CGroupDeviceAllow *a;
2766 : const char *m;
2767 : size_t n;
2768 :
2769 0 : if (isempty(rvalue)) {
2770 0 : while (c->device_allow)
2771 0 : cgroup_context_free_device_allow(c, c->device_allow);
2772 :
2773 0 : return 0;
2774 : }
2775 :
2776 0 : n = strcspn(rvalue, WHITESPACE);
2777 0 : path = strndup(rvalue, n);
2778 0 : if (!path)
2779 0 : return log_oom();
2780 :
2781 0 : if (!startswith(path, "/dev/") &&
2782 0 : !startswith(path, "block-") &&
2783 0 : !startswith(path, "char-")) {
2784 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2785 : "Invalid device node path '%s'. Ignoring.", path);
2786 0 : return 0;
2787 : }
2788 :
2789 0 : m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2790 0 : if (isempty(m))
2791 0 : m = "rwm";
2792 :
2793 0 : if (!in_charset(m, "rwm")) {
2794 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2795 : "Invalid device rights '%s'. Ignoring.", m);
2796 0 : return 0;
2797 : }
2798 :
2799 0 : a = new0(CGroupDeviceAllow, 1);
2800 0 : if (!a)
2801 0 : return log_oom();
2802 :
2803 0 : a->path = path;
2804 0 : path = NULL;
2805 0 : a->r = !!strchr(m, 'r');
2806 0 : a->w = !!strchr(m, 'w');
2807 0 : a->m = !!strchr(m, 'm');
2808 :
2809 0 : LIST_PREPEND(device_allow, c->device_allow, a);
2810 0 : return 0;
2811 : }
2812 :
2813 1 : int config_parse_blockio_weight(
2814 : const char *unit,
2815 : const char *filename,
2816 : unsigned line,
2817 : const char *section,
2818 : unsigned section_line,
2819 : const char *lvalue,
2820 : int ltype,
2821 : const char *rvalue,
2822 : void *data,
2823 : void *userdata) {
2824 :
2825 1 : unsigned long *weight = data, lu;
2826 : int r;
2827 :
2828 1 : assert(filename);
2829 1 : assert(lvalue);
2830 1 : assert(rvalue);
2831 :
2832 1 : if (isempty(rvalue)) {
2833 0 : *weight = (unsigned long) -1;
2834 0 : return 0;
2835 : }
2836 :
2837 1 : r = safe_atolu(rvalue, &lu);
2838 1 : if (r < 0 || lu < 10 || lu > 1000) {
2839 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2840 : "Block IO weight '%s' invalid. Ignoring.", rvalue);
2841 0 : return 0;
2842 : }
2843 :
2844 1 : *weight = lu;
2845 1 : return 0;
2846 : }
2847 :
2848 0 : int config_parse_blockio_device_weight(
2849 : const char *unit,
2850 : const char *filename,
2851 : unsigned line,
2852 : const char *section,
2853 : unsigned section_line,
2854 : const char *lvalue,
2855 : int ltype,
2856 : const char *rvalue,
2857 : void *data,
2858 : void *userdata) {
2859 :
2860 0 : _cleanup_free_ char *path = NULL;
2861 : CGroupBlockIODeviceWeight *w;
2862 0 : CGroupContext *c = data;
2863 : unsigned long lu;
2864 : const char *weight;
2865 : size_t n;
2866 : int r;
2867 :
2868 0 : assert(filename);
2869 0 : assert(lvalue);
2870 0 : assert(rvalue);
2871 :
2872 0 : if (isempty(rvalue)) {
2873 0 : while (c->blockio_device_weights)
2874 0 : cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2875 :
2876 0 : return 0;
2877 : }
2878 :
2879 0 : n = strcspn(rvalue, WHITESPACE);
2880 0 : weight = rvalue + n;
2881 0 : if (!*weight) {
2882 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2883 : "Expected block device and device weight. Ignoring.");
2884 0 : return 0;
2885 : }
2886 :
2887 0 : path = strndup(rvalue, n);
2888 0 : if (!path)
2889 0 : return log_oom();
2890 :
2891 0 : if (!path_startswith(path, "/dev")) {
2892 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2893 : "Invalid device node path '%s'. Ignoring.", path);
2894 0 : return 0;
2895 : }
2896 :
2897 0 : weight += strspn(weight, WHITESPACE);
2898 0 : r = safe_atolu(weight, &lu);
2899 0 : if (r < 0 || lu < 10 || lu > 1000) {
2900 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2901 : "Block IO weight '%s' invalid. Ignoring.", rvalue);
2902 0 : return 0;
2903 : }
2904 :
2905 0 : w = new0(CGroupBlockIODeviceWeight, 1);
2906 0 : if (!w)
2907 0 : return log_oom();
2908 :
2909 0 : w->path = path;
2910 0 : path = NULL;
2911 :
2912 0 : w->weight = lu;
2913 :
2914 0 : LIST_PREPEND(device_weights, c->blockio_device_weights, w);
2915 0 : return 0;
2916 : }
2917 :
2918 0 : int config_parse_blockio_bandwidth(
2919 : const char *unit,
2920 : const char *filename,
2921 : unsigned line,
2922 : const char *section,
2923 : unsigned section_line,
2924 : const char *lvalue,
2925 : int ltype,
2926 : const char *rvalue,
2927 : void *data,
2928 : void *userdata) {
2929 :
2930 0 : _cleanup_free_ char *path = NULL;
2931 : CGroupBlockIODeviceBandwidth *b;
2932 0 : CGroupContext *c = data;
2933 : const char *bandwidth;
2934 : off_t bytes;
2935 : bool read;
2936 : size_t n;
2937 : int r;
2938 :
2939 0 : assert(filename);
2940 0 : assert(lvalue);
2941 0 : assert(rvalue);
2942 :
2943 0 : read = streq("BlockIOReadBandwidth", lvalue);
2944 :
2945 0 : if (isempty(rvalue)) {
2946 : CGroupBlockIODeviceBandwidth *next;
2947 :
2948 0 : LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2949 0 : if (b->read == read)
2950 0 : cgroup_context_free_blockio_device_bandwidth(c, b);
2951 :
2952 0 : return 0;
2953 : }
2954 :
2955 0 : n = strcspn(rvalue, WHITESPACE);
2956 0 : bandwidth = rvalue + n;
2957 0 : bandwidth += strspn(bandwidth, WHITESPACE);
2958 :
2959 0 : if (!*bandwidth) {
2960 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2961 : "Expected space separated pair of device node and bandwidth. Ignoring.");
2962 0 : return 0;
2963 : }
2964 :
2965 0 : path = strndup(rvalue, n);
2966 0 : if (!path)
2967 0 : return log_oom();
2968 :
2969 0 : if (!path_startswith(path, "/dev")) {
2970 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2971 : "Invalid device node path '%s'. Ignoring.", path);
2972 0 : return 0;
2973 : }
2974 :
2975 0 : r = parse_size(bandwidth, 1000, &bytes);
2976 0 : if (r < 0 || bytes <= 0) {
2977 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2978 : "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2979 0 : return 0;
2980 : }
2981 :
2982 0 : b = new0(CGroupBlockIODeviceBandwidth, 1);
2983 0 : if (!b)
2984 0 : return log_oom();
2985 :
2986 0 : b->path = path;
2987 0 : path = NULL;
2988 0 : b->bandwidth = (uint64_t) bytes;
2989 0 : b->read = read;
2990 :
2991 0 : LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
2992 :
2993 0 : return 0;
2994 : }
2995 :
2996 0 : DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2997 :
2998 0 : int config_parse_job_mode_isolate(
2999 : const char *unit,
3000 : const char *filename,
3001 : unsigned line,
3002 : const char *section,
3003 : unsigned section_line,
3004 : const char *lvalue,
3005 : int ltype,
3006 : const char *rvalue,
3007 : void *data,
3008 : void *userdata) {
3009 :
3010 0 : JobMode *m = data;
3011 : int r;
3012 :
3013 0 : assert(filename);
3014 0 : assert(lvalue);
3015 0 : assert(rvalue);
3016 :
3017 0 : r = parse_boolean(rvalue);
3018 0 : if (r < 0) {
3019 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3020 : "Failed to parse boolean, ignoring: %s", rvalue);
3021 0 : return 0;
3022 : }
3023 :
3024 0 : *m = r ? JOB_ISOLATE : JOB_REPLACE;
3025 0 : return 0;
3026 : }
3027 :
3028 0 : int config_parse_personality(
3029 : const char *unit,
3030 : const char *filename,
3031 : unsigned line,
3032 : const char *section,
3033 : unsigned section_line,
3034 : const char *lvalue,
3035 : int ltype,
3036 : const char *rvalue,
3037 : void *data,
3038 : void *userdata) {
3039 :
3040 0 : unsigned long *personality = data, p;
3041 :
3042 0 : assert(filename);
3043 0 : assert(lvalue);
3044 0 : assert(rvalue);
3045 0 : assert(personality);
3046 :
3047 0 : p = personality_from_string(rvalue);
3048 0 : if (p == PERSONALITY_INVALID) {
3049 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3050 : "Failed to parse personality, ignoring: %s", rvalue);
3051 0 : return 0;
3052 : }
3053 :
3054 0 : *personality = p;
3055 0 : return 0;
3056 : }
3057 :
3058 0 : int config_parse_runtime_directory(
3059 : const char *unit,
3060 : const char *filename,
3061 : unsigned line,
3062 : const char *section,
3063 : unsigned section_line,
3064 : const char *lvalue,
3065 : int ltype,
3066 : const char *rvalue,
3067 : void *data,
3068 : void *userdata) {
3069 :
3070 0 : char***rt = data;
3071 : const char *word, *state;
3072 : size_t l;
3073 : int r;
3074 :
3075 0 : assert(filename);
3076 0 : assert(lvalue);
3077 0 : assert(rvalue);
3078 0 : assert(data);
3079 :
3080 0 : if (isempty(rvalue)) {
3081 : /* Empty assignment resets the list */
3082 0 : strv_free(*rt);
3083 0 : *rt = NULL;
3084 0 : return 0;
3085 : }
3086 :
3087 0 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
3088 0 : _cleanup_free_ char *n;
3089 :
3090 0 : n = strndup(word, l);
3091 0 : if (!n)
3092 0 : return log_oom();
3093 :
3094 0 : if (!filename_is_valid(n)) {
3095 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3096 : "Runtime directory is not valid, ignoring assignment: %s", rvalue);
3097 0 : continue;
3098 : }
3099 :
3100 0 : r = strv_push(rt, n);
3101 0 : if (r < 0)
3102 0 : return log_oom();
3103 :
3104 0 : n = NULL;
3105 : }
3106 0 : if (!isempty(state))
3107 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3108 : "Trailing garbage, ignoring.");
3109 :
3110 0 : return 0;
3111 : }
3112 :
3113 0 : int config_parse_set_status(
3114 : const char *unit,
3115 : const char *filename,
3116 : unsigned line,
3117 : const char *section,
3118 : unsigned section_line,
3119 : const char *lvalue,
3120 : int ltype,
3121 : const char *rvalue,
3122 : void *data,
3123 : void *userdata) {
3124 :
3125 : size_t l;
3126 : const char *word, *state;
3127 : int r;
3128 0 : ExitStatusSet *status_set = data;
3129 :
3130 0 : assert(filename);
3131 0 : assert(lvalue);
3132 0 : assert(rvalue);
3133 0 : assert(data);
3134 :
3135 : /* Empty assignment resets the list */
3136 0 : if (isempty(rvalue)) {
3137 0 : exit_status_set_free(status_set);
3138 0 : return 0;
3139 : }
3140 :
3141 0 : FOREACH_WORD(word, l, rvalue, state) {
3142 0 : _cleanup_free_ char *temp;
3143 : int val;
3144 : Set **set;
3145 :
3146 0 : temp = strndup(word, l);
3147 0 : if (!temp)
3148 0 : return log_oom();
3149 :
3150 0 : r = safe_atoi(temp, &val);
3151 0 : if (r < 0) {
3152 0 : val = signal_from_string_try_harder(temp);
3153 :
3154 0 : if (val <= 0) {
3155 0 : log_syntax(unit, LOG_ERR, filename, line, -val,
3156 : "Failed to parse value, ignoring: %s", word);
3157 0 : continue;
3158 : }
3159 0 : set = &status_set->signal;
3160 : } else {
3161 0 : if (val < 0 || val > 255) {
3162 0 : log_syntax(unit, LOG_ERR, filename, line, ERANGE,
3163 : "Value %d is outside range 0-255, ignoring", val);
3164 0 : continue;
3165 : }
3166 0 : set = &status_set->status;
3167 : }
3168 :
3169 0 : r = set_ensure_allocated(set, NULL);
3170 0 : if (r < 0)
3171 0 : return log_oom();
3172 :
3173 0 : r = set_put(*set, INT_TO_PTR(val));
3174 0 : if (r < 0) {
3175 0 : log_syntax(unit, LOG_ERR, filename, line, -r,
3176 : "Unable to store: %s", word);
3177 0 : return r;
3178 : }
3179 : }
3180 0 : if (!isempty(state))
3181 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3182 : "Trailing garbage, ignoring.");
3183 :
3184 0 : return 0;
3185 : }
3186 :
3187 0 : int config_parse_namespace_path_strv(
3188 : const char *unit,
3189 : const char *filename,
3190 : unsigned line,
3191 : const char *section,
3192 : unsigned section_line,
3193 : const char *lvalue,
3194 : int ltype,
3195 : const char *rvalue,
3196 : void *data,
3197 : void *userdata) {
3198 :
3199 0 : char*** sv = data;
3200 : const char *word, *state;
3201 : size_t l;
3202 : int r;
3203 :
3204 0 : assert(filename);
3205 0 : assert(lvalue);
3206 0 : assert(rvalue);
3207 0 : assert(data);
3208 :
3209 0 : if (isempty(rvalue)) {
3210 : /* Empty assignment resets the list */
3211 0 : strv_free(*sv);
3212 0 : *sv = NULL;
3213 0 : return 0;
3214 : }
3215 :
3216 0 : FOREACH_WORD_QUOTED(word, l, rvalue, state) {
3217 0 : _cleanup_free_ char *n;
3218 : int offset;
3219 :
3220 0 : n = strndup(word, l);
3221 0 : if (!n)
3222 0 : return log_oom();
3223 :
3224 0 : if (!utf8_is_valid(n)) {
3225 0 : log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
3226 0 : continue;
3227 : }
3228 :
3229 0 : offset = n[0] == '-';
3230 0 : if (!path_is_absolute(n + offset)) {
3231 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3232 : "Not an absolute path, ignoring: %s", rvalue);
3233 0 : continue;
3234 : }
3235 :
3236 0 : path_kill_slashes(n);
3237 :
3238 0 : r = strv_push(sv, n);
3239 0 : if (r < 0)
3240 0 : return log_oom();
3241 :
3242 0 : n = NULL;
3243 : }
3244 0 : if (!isempty(state))
3245 0 : log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3246 : "Trailing garbage, ignoring.");
3247 :
3248 0 : return 0;
3249 : }
3250 :
3251 0 : int config_parse_no_new_privileges(
3252 : const char* unit,
3253 : const char *filename,
3254 : unsigned line,
3255 : const char *section,
3256 : unsigned section_line,
3257 : const char *lvalue,
3258 : int ltype,
3259 : const char *rvalue,
3260 : void *data,
3261 : void *userdata) {
3262 :
3263 0 : ExecContext *c = data;
3264 : int k;
3265 :
3266 0 : assert(filename);
3267 0 : assert(lvalue);
3268 0 : assert(rvalue);
3269 0 : assert(data);
3270 :
3271 0 : k = parse_boolean(rvalue);
3272 0 : if (k < 0) {
3273 0 : log_syntax(unit, LOG_ERR, filename, line, -k,
3274 : "Failed to parse boolean value, ignoring: %s", rvalue);
3275 0 : return 0;
3276 : }
3277 :
3278 0 : c->no_new_privileges = !!k;
3279 0 : c->no_new_privileges_set = true;
3280 :
3281 0 : return 0;
3282 : }
3283 :
3284 0 : int config_parse_protect_home(
3285 : const char* unit,
3286 : const char *filename,
3287 : unsigned line,
3288 : const char *section,
3289 : unsigned section_line,
3290 : const char *lvalue,
3291 : int ltype,
3292 : const char *rvalue,
3293 : void *data,
3294 : void *userdata) {
3295 :
3296 0 : ExecContext *c = data;
3297 : int k;
3298 :
3299 0 : assert(filename);
3300 0 : assert(lvalue);
3301 0 : assert(rvalue);
3302 0 : assert(data);
3303 :
3304 : /* Our enum shall be a superset of booleans, hence first try
3305 : * to parse as as boolean, and then as enum */
3306 :
3307 0 : k = parse_boolean(rvalue);
3308 0 : if (k > 0)
3309 0 : c->protect_home = PROTECT_HOME_YES;
3310 0 : else if (k == 0)
3311 0 : c->protect_home = PROTECT_HOME_NO;
3312 : else {
3313 : ProtectHome h;
3314 :
3315 0 : h = protect_home_from_string(rvalue);
3316 0 : if (h < 0){
3317 0 : log_syntax(unit, LOG_ERR, filename, line, -h,
3318 : "Failed to parse protect home value, ignoring: %s", rvalue);
3319 0 : return 0;
3320 : }
3321 :
3322 0 : c->protect_home = h;
3323 : }
3324 :
3325 0 : return 0;
3326 : }
3327 :
3328 0 : int config_parse_protect_system(
3329 : const char* unit,
3330 : const char *filename,
3331 : unsigned line,
3332 : const char *section,
3333 : unsigned section_line,
3334 : const char *lvalue,
3335 : int ltype,
3336 : const char *rvalue,
3337 : void *data,
3338 : void *userdata) {
3339 :
3340 0 : ExecContext *c = data;
3341 : int k;
3342 :
3343 0 : assert(filename);
3344 0 : assert(lvalue);
3345 0 : assert(rvalue);
3346 0 : assert(data);
3347 :
3348 : /* Our enum shall be a superset of booleans, hence first try
3349 : * to parse as as boolean, and then as enum */
3350 :
3351 0 : k = parse_boolean(rvalue);
3352 0 : if (k > 0)
3353 0 : c->protect_system = PROTECT_SYSTEM_YES;
3354 0 : else if (k == 0)
3355 0 : c->protect_system = PROTECT_SYSTEM_NO;
3356 : else {
3357 : ProtectSystem s;
3358 :
3359 0 : s = protect_system_from_string(rvalue);
3360 0 : if (s < 0){
3361 0 : log_syntax(unit, LOG_ERR, filename, line, -s,
3362 : "Failed to parse protect system value, ignoring: %s", rvalue);
3363 0 : return 0;
3364 : }
3365 :
3366 0 : c->protect_system = s;
3367 : }
3368 :
3369 0 : return 0;
3370 : }
3371 :
3372 : #define FOLLOW_MAX 8
3373 :
3374 92 : static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
3375 92 : unsigned c = 0;
3376 : int fd, r;
3377 : FILE *f;
3378 92 : char *id = NULL;
3379 :
3380 92 : assert(filename);
3381 92 : assert(*filename);
3382 92 : assert(_f);
3383 92 : assert(names);
3384 :
3385 : /* This will update the filename pointer if the loaded file is
3386 : * reached by a symlink. The old string will be freed. */
3387 :
3388 : for (;;) {
3389 : char *target, *name;
3390 :
3391 158 : if (c++ >= FOLLOW_MAX)
3392 0 : return -ELOOP;
3393 :
3394 158 : path_kill_slashes(*filename);
3395 :
3396 : /* Add the file name we are currently looking at to
3397 : * the names of this unit, but only if it is a valid
3398 : * unit name. */
3399 158 : name = basename(*filename);
3400 :
3401 158 : if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
3402 :
3403 158 : id = set_get(names, name);
3404 158 : if (!id) {
3405 98 : id = strdup(name);
3406 98 : if (!id)
3407 0 : return -ENOMEM;
3408 :
3409 98 : r = set_consume(names, id);
3410 98 : if (r < 0)
3411 0 : return r;
3412 : }
3413 : }
3414 :
3415 : /* Try to open the file name, but don't if its a symlink */
3416 158 : fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
3417 158 : if (fd >= 0)
3418 92 : break;
3419 :
3420 66 : if (errno != ELOOP)
3421 0 : return -errno;
3422 :
3423 : /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
3424 66 : r = readlink_and_make_absolute(*filename, &target);
3425 66 : if (r < 0)
3426 0 : return r;
3427 :
3428 66 : free(*filename);
3429 66 : *filename = target;
3430 66 : }
3431 :
3432 92 : f = fdopen(fd, "re");
3433 92 : if (!f) {
3434 0 : safe_close(fd);
3435 0 : return -errno;
3436 : }
3437 :
3438 92 : *_f = f;
3439 92 : *_final = id;
3440 92 : return 0;
3441 : }
3442 :
3443 92 : static int merge_by_names(Unit **u, Set *names, const char *id) {
3444 : char *k;
3445 : int r;
3446 :
3447 92 : assert(u);
3448 92 : assert(*u);
3449 92 : assert(names);
3450 :
3451 : /* Let's try to add in all symlink names we found */
3452 282 : while ((k = set_steal_first(names))) {
3453 :
3454 : /* First try to merge in the other name into our
3455 : * unit */
3456 98 : r = unit_merge_by_name(*u, k);
3457 98 : if (r < 0) {
3458 : Unit *other;
3459 :
3460 : /* Hmm, we couldn't merge the other unit into
3461 : * ours? Then let's try it the other way
3462 : * round */
3463 :
3464 0 : other = manager_get_unit((*u)->manager, k);
3465 0 : free(k);
3466 :
3467 0 : if (other) {
3468 0 : r = unit_merge(other, *u);
3469 0 : if (r >= 0) {
3470 0 : *u = other;
3471 0 : return merge_by_names(u, names, NULL);
3472 : }
3473 : }
3474 :
3475 0 : return r;
3476 : }
3477 :
3478 98 : if (id == k)
3479 92 : unit_choose_id(*u, id);
3480 :
3481 98 : free(k);
3482 : }
3483 :
3484 92 : return 0;
3485 : }
3486 :
3487 782 : static int load_from_path(Unit *u, const char *path) {
3488 : int r;
3489 1564 : _cleanup_set_free_free_ Set *symlink_names = NULL;
3490 1564 : _cleanup_fclose_ FILE *f = NULL;
3491 1564 : _cleanup_free_ char *filename = NULL;
3492 782 : char *id = NULL;
3493 : Unit *merged;
3494 : struct stat st;
3495 :
3496 782 : assert(u);
3497 782 : assert(path);
3498 :
3499 782 : symlink_names = set_new(&string_hash_ops);
3500 782 : if (!symlink_names)
3501 0 : return -ENOMEM;
3502 :
3503 782 : if (path_is_absolute(path)) {
3504 :
3505 0 : filename = strdup(path);
3506 0 : if (!filename)
3507 0 : return -ENOMEM;
3508 :
3509 0 : r = open_follow(&filename, &f, symlink_names, &id);
3510 0 : if (r < 0) {
3511 0 : free(filename);
3512 0 : filename = NULL;
3513 :
3514 0 : if (r != -ENOENT)
3515 0 : return r;
3516 : }
3517 :
3518 : } else {
3519 : char **p;
3520 :
3521 2944 : STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
3522 :
3523 : /* Instead of opening the path right away, we manually
3524 : * follow all symlinks and add their name to our unit
3525 : * name set while doing so */
3526 782 : filename = path_make_absolute(path, *p);
3527 782 : if (!filename)
3528 0 : return -ENOMEM;
3529 :
3530 1564 : if (u->manager->unit_path_cache &&
3531 782 : !set_get(u->manager->unit_path_cache, filename))
3532 690 : r = -ENOENT;
3533 : else
3534 92 : r = open_follow(&filename, &f, symlink_names, &id);
3535 :
3536 782 : if (r < 0) {
3537 690 : free(filename);
3538 690 : filename = NULL;
3539 :
3540 690 : if (r != -ENOENT)
3541 0 : return r;
3542 :
3543 : /* Empty the symlink names for the next run */
3544 690 : set_clear_free(symlink_names);
3545 690 : continue;
3546 : }
3547 :
3548 92 : break;
3549 : }
3550 : }
3551 :
3552 782 : if (!filename)
3553 : /* Hmm, no suitable file found? */
3554 690 : return 0;
3555 :
3556 92 : merged = u;
3557 92 : r = merge_by_names(&merged, symlink_names, id);
3558 92 : if (r < 0)
3559 0 : return r;
3560 :
3561 92 : if (merged != u) {
3562 0 : u->load_state = UNIT_MERGED;
3563 0 : return 0;
3564 : }
3565 :
3566 92 : if (fstat(fileno(f), &st) < 0)
3567 0 : return -errno;
3568 :
3569 92 : if (null_or_empty(&st))
3570 0 : u->load_state = UNIT_MASKED;
3571 : else {
3572 92 : u->load_state = UNIT_LOADED;
3573 :
3574 : /* Now, parse the file contents */
3575 92 : r = config_parse(u->id, filename, f,
3576 92 : UNIT_VTABLE(u)->sections,
3577 : config_item_perf_lookup, load_fragment_gperf_lookup,
3578 : false, true, false, u);
3579 92 : if (r < 0)
3580 0 : return r;
3581 : }
3582 :
3583 92 : free(u->fragment_path);
3584 92 : u->fragment_path = filename;
3585 92 : filename = NULL;
3586 :
3587 92 : u->fragment_mtime = timespec_load(&st.st_mtim);
3588 :
3589 92 : if (u->source_path) {
3590 0 : if (stat(u->source_path, &st) >= 0)
3591 0 : u->source_mtime = timespec_load(&st.st_mtim);
3592 : else
3593 0 : u->source_mtime = 0;
3594 : }
3595 :
3596 92 : return 0;
3597 : }
3598 :
3599 782 : int unit_load_fragment(Unit *u) {
3600 : int r;
3601 : Iterator i;
3602 : const char *t;
3603 :
3604 782 : assert(u);
3605 782 : assert(u->load_state == UNIT_STUB);
3606 782 : assert(u->id);
3607 :
3608 : /* First, try to find the unit under its id. We always look
3609 : * for unit files in the default directories, to make it easy
3610 : * to override things by placing things in /etc/systemd/system */
3611 782 : r = load_from_path(u, u->id);
3612 782 : if (r < 0)
3613 0 : return r;
3614 :
3615 : /* Try to find an alias we can load this with */
3616 782 : if (u->load_state == UNIT_STUB) {
3617 2070 : SET_FOREACH(t, u->names, i) {
3618 :
3619 690 : if (t == u->id)
3620 690 : continue;
3621 :
3622 0 : r = load_from_path(u, t);
3623 0 : if (r < 0)
3624 0 : return r;
3625 :
3626 0 : if (u->load_state != UNIT_STUB)
3627 0 : break;
3628 : }
3629 : }
3630 :
3631 : /* And now, try looking for it under the suggested (originally linked) path */
3632 782 : if (u->load_state == UNIT_STUB && u->fragment_path) {
3633 :
3634 0 : r = load_from_path(u, u->fragment_path);
3635 0 : if (r < 0)
3636 0 : return r;
3637 :
3638 0 : if (u->load_state == UNIT_STUB) {
3639 : /* Hmm, this didn't work? Then let's get rid
3640 : * of the fragment path stored for us, so that
3641 : * we don't point to an invalid location. */
3642 0 : free(u->fragment_path);
3643 0 : u->fragment_path = NULL;
3644 : }
3645 : }
3646 :
3647 : /* Look for a template */
3648 782 : if (u->load_state == UNIT_STUB && u->instance) {
3649 0 : _cleanup_free_ char *k = NULL;
3650 :
3651 0 : r = unit_name_template(u->id, &k);
3652 0 : if (r < 0)
3653 0 : return r;
3654 :
3655 0 : r = load_from_path(u, k);
3656 0 : if (r < 0)
3657 0 : return r;
3658 :
3659 0 : if (u->load_state == UNIT_STUB) {
3660 0 : SET_FOREACH(t, u->names, i) {
3661 0 : _cleanup_free_ char *z = NULL;
3662 :
3663 0 : if (t == u->id)
3664 0 : continue;
3665 :
3666 0 : r = unit_name_template(t, &z);
3667 0 : if (r < 0)
3668 0 : return r;
3669 :
3670 0 : r = load_from_path(u, z);
3671 0 : if (r < 0)
3672 0 : return r;
3673 :
3674 0 : if (u->load_state != UNIT_STUB)
3675 0 : break;
3676 : }
3677 : }
3678 : }
3679 :
3680 782 : return 0;
3681 : }
3682 :
3683 0 : void unit_dump_config_items(FILE *f) {
3684 : static const struct {
3685 : const ConfigParserCallback callback;
3686 : const char *rvalue;
3687 : } table[] = {
3688 : #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
3689 : { config_parse_warn_compat, "NOTSUPPORTED" },
3690 : #endif
3691 : { config_parse_int, "INTEGER" },
3692 : { config_parse_unsigned, "UNSIGNED" },
3693 : { config_parse_iec_size, "SIZE" },
3694 : { config_parse_iec_off, "SIZE" },
3695 : { config_parse_si_size, "SIZE" },
3696 : { config_parse_bool, "BOOLEAN" },
3697 : { config_parse_string, "STRING" },
3698 : { config_parse_path, "PATH" },
3699 : { config_parse_unit_path_printf, "PATH" },
3700 : { config_parse_strv, "STRING [...]" },
3701 : { config_parse_exec_nice, "NICE" },
3702 : { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
3703 : { config_parse_exec_io_class, "IOCLASS" },
3704 : { config_parse_exec_io_priority, "IOPRIORITY" },
3705 : { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
3706 : { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
3707 : { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
3708 : { config_parse_mode, "MODE" },
3709 : { config_parse_unit_env_file, "FILE" },
3710 : { config_parse_output, "OUTPUT" },
3711 : { config_parse_input, "INPUT" },
3712 : { config_parse_log_facility, "FACILITY" },
3713 : { config_parse_log_level, "LEVEL" },
3714 : { config_parse_exec_capabilities, "CAPABILITIES" },
3715 : { config_parse_exec_secure_bits, "SECUREBITS" },
3716 : { config_parse_bounding_set, "BOUNDINGSET" },
3717 : { config_parse_limit, "LIMIT" },
3718 : { config_parse_unit_deps, "UNIT [...]" },
3719 : { config_parse_exec, "PATH [ARGUMENT [...]]" },
3720 : { config_parse_service_type, "SERVICETYPE" },
3721 : { config_parse_service_restart, "SERVICERESTART" },
3722 : #ifdef HAVE_SYSV_COMPAT
3723 : { config_parse_sysv_priority, "SYSVPRIORITY" },
3724 : #endif
3725 : { config_parse_kill_mode, "KILLMODE" },
3726 : { config_parse_kill_signal, "SIGNAL" },
3727 : { config_parse_socket_listen, "SOCKET [...]" },
3728 : { config_parse_socket_bind, "SOCKETBIND" },
3729 : { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
3730 : { config_parse_sec, "SECONDS" },
3731 : { config_parse_nsec, "NANOSECONDS" },
3732 : { config_parse_namespace_path_strv, "PATH [...]" },
3733 : { config_parse_unit_requires_mounts_for, "PATH [...]" },
3734 : { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
3735 : { config_parse_unit_string_printf, "STRING" },
3736 : { config_parse_trigger_unit, "UNIT" },
3737 : { config_parse_timer, "TIMER" },
3738 : { config_parse_path_spec, "PATH" },
3739 : { config_parse_notify_access, "ACCESS" },
3740 : { config_parse_ip_tos, "TOS" },
3741 : { config_parse_unit_condition_path, "CONDITION" },
3742 : { config_parse_unit_condition_string, "CONDITION" },
3743 : { config_parse_unit_condition_null, "CONDITION" },
3744 : { config_parse_unit_slice, "SLICE" },
3745 : { config_parse_documentation, "URL" },
3746 : { config_parse_service_timeout, "SECONDS" },
3747 : { config_parse_failure_action, "ACTION" },
3748 : { config_parse_set_status, "STATUS" },
3749 : { config_parse_service_sockets, "SOCKETS" },
3750 : { config_parse_environ, "ENVIRON" },
3751 : #ifdef HAVE_SECCOMP
3752 : { config_parse_syscall_filter, "SYSCALLS" },
3753 : { config_parse_syscall_archs, "ARCHS" },
3754 : { config_parse_syscall_errno, "ERRNO" },
3755 : { config_parse_address_families, "FAMILIES" },
3756 : #endif
3757 : { config_parse_cpu_shares, "SHARES" },
3758 : { config_parse_memory_limit, "LIMIT" },
3759 : { config_parse_device_allow, "DEVICE" },
3760 : { config_parse_device_policy, "POLICY" },
3761 : { config_parse_blockio_bandwidth, "BANDWIDTH" },
3762 : { config_parse_blockio_weight, "WEIGHT" },
3763 : { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
3764 : { config_parse_long, "LONG" },
3765 : { config_parse_socket_service, "SERVICE" },
3766 : #ifdef HAVE_SELINUX
3767 : { config_parse_exec_selinux_context, "LABEL" },
3768 : #endif
3769 : { config_parse_job_mode, "MODE" },
3770 : { config_parse_job_mode_isolate, "BOOLEAN" },
3771 : { config_parse_personality, "PERSONALITY" },
3772 : };
3773 :
3774 0 : const char *prev = NULL;
3775 : const char *i;
3776 :
3777 0 : assert(f);
3778 :
3779 0 : NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
3780 0 : const char *rvalue = "OTHER", *lvalue;
3781 : unsigned j;
3782 : size_t prefix_len;
3783 : const char *dot;
3784 : const ConfigPerfItem *p;
3785 :
3786 0 : assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
3787 :
3788 0 : dot = strchr(i, '.');
3789 0 : lvalue = dot ? dot + 1 : i;
3790 0 : prefix_len = dot-i;
3791 :
3792 0 : if (dot)
3793 0 : if (!prev || !strneq(prev, i, prefix_len+1)) {
3794 0 : if (prev)
3795 0 : fputc('\n', f);
3796 :
3797 0 : fprintf(f, "[%.*s]\n", (int) prefix_len, i);
3798 : }
3799 :
3800 0 : for (j = 0; j < ELEMENTSOF(table); j++)
3801 0 : if (p->parse == table[j].callback) {
3802 0 : rvalue = table[j].rvalue;
3803 0 : break;
3804 : }
3805 :
3806 0 : fprintf(f, "%s=%s\n", lvalue, rvalue);
3807 0 : prev = i;
3808 : }
3809 0 : }
|