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