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