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