]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
cap-list: add capability_set_{from_string,to_string_alloc}()
[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;
dd1f5bd0 1160 int r;
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
dd1f5bd0
YW
1176 r = capability_set_from_string(rvalue, &sum);
1177 if (r == -ENOMEM)
1178 return log_oom();
1179 if (r < 0) {
1180 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse word: %s", rvalue);
1181 return 0;
94f04347 1182 }
9eba9da4 1183
a103496c
IP
1184 sum = invert ? ~sum : sum;
1185
1186 if (sum == 0 || *capability_set == initial)
1187 /* "" or uninitialized data -> replace */
1188 *capability_set = sum;
260abb78 1189 else
a103496c
IP
1190 /* previous data -> merge */
1191 *capability_set |= sum;
260abb78 1192
9eba9da4
LP
1193 return 0;
1194}
1195
91518d20 1196int config_parse_limit(
d580265e
LP
1197 const char *unit,
1198 const char *filename,
1199 unsigned line,
1200 const char *section,
1201 unsigned section_line,
1202 const char *lvalue,
1203 int ltype,
1204 const char *rvalue,
1205 void *data,
1206 void *userdata) {
412ea7a9 1207
d0a7c5f6
LP
1208 struct rlimit **rl = data, d = {};
1209 int r;
a4c18002
LP
1210
1211 assert(filename);
1212 assert(lvalue);
1213 assert(rvalue);
1214 assert(data);
1215
d0a7c5f6
LP
1216 r = rlimit_parse(ltype, rvalue, &d);
1217 if (r == -EILSEQ) {
1218 log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue);
1219 return 0;
1220 }
1221 if (r < 0) {
1222 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
1223 return 0;
1224 }
a4c18002 1225
d0a7c5f6
LP
1226 if (rl[ltype])
1227 *rl[ltype] = d;
1228 else {
1229 rl[ltype] = newdup(struct rlimit, &d, 1);
1230 if (!rl[ltype])
1231 return log_oom();
1232 }
a4c18002 1233
d0a7c5f6 1234 return 0;
91518d20 1235}
a4c18002 1236
07459bb6 1237#ifdef HAVE_SYSV_COMPAT
e8e581bf
ZJS
1238int config_parse_sysv_priority(const char *unit,
1239 const char *filename,
1240 unsigned line,
1241 const char *section,
71a61510 1242 unsigned section_line,
e8e581bf
ZJS
1243 const char *lvalue,
1244 int ltype,
1245 const char *rvalue,
1246 void *data,
1247 void *userdata) {
a9a1e00a
LP
1248
1249 int *priority = data;
e8e581bf 1250 int i, r;
a9a1e00a
LP
1251
1252 assert(filename);
1253 assert(lvalue);
1254 assert(rvalue);
1255 assert(data);
1256
e8e581bf
ZJS
1257 r = safe_atoi(rvalue, &i);
1258 if (r < 0 || i < 0) {
12ca818f 1259 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue);
c0b34696 1260 return 0;
a9a1e00a
LP
1261 }
1262
1263 *priority = (int) i;
1264 return 0;
1265}
07459bb6 1266#endif
a9a1e00a 1267
023a4f67 1268DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
f975e971 1269DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
50159e6a 1270
83555251
LP
1271int config_parse_exec_mount_flags(
1272 const char *unit,
1273 const char *filename,
1274 unsigned line,
1275 const char *section,
1276 unsigned section_line,
1277 const char *lvalue,
1278 int ltype,
1279 const char *rvalue,
1280 void *data,
1281 void *userdata) {
15ae422b 1282
e28bb14a 1283
e28bb14a 1284 ExecContext *c = data;
c7383828 1285 int r;
15ae422b
LP
1286
1287 assert(filename);
1288 assert(lvalue);
1289 assert(rvalue);
1290 assert(data);
1291
c7383828
ZJS
1292 r = mount_propagation_flags_from_string(rvalue, &c->mount_flags);
1293 if (r < 0)
1294 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring.", rvalue);
e28bb14a 1295
15ae422b
LP
1296 return 0;
1297}
1298
5f8640fb
LP
1299int config_parse_exec_selinux_context(
1300 const char *unit,
1301 const char *filename,
1302 unsigned line,
1303 const char *section,
1304 unsigned section_line,
1305 const char *lvalue,
1306 int ltype,
1307 const char *rvalue,
1308 void *data,
1309 void *userdata) {
1310
1311 ExecContext *c = data;
1312 Unit *u = userdata;
1313 bool ignore;
1314 char *k;
1315 int r;
1316
1317 assert(filename);
1318 assert(lvalue);
1319 assert(rvalue);
1320 assert(data);
1321
1322 if (isempty(rvalue)) {
a1e58e8e 1323 c->selinux_context = mfree(c->selinux_context);
5f8640fb
LP
1324 c->selinux_context_ignore = false;
1325 return 0;
1326 }
1327
1328 if (rvalue[0] == '-') {
1329 ignore = true;
1330 rvalue++;
1331 } else
1332 ignore = false;
1333
18913df9 1334 r = unit_full_printf(u, rvalue, &k);
5f8640fb 1335 if (r < 0) {
bb28e684
ZJS
1336 log_syntax(unit, LOG_ERR, filename, line, r,
1337 "Failed to resolve specifiers%s: %m",
1338 ignore ? ", ignoring" : "");
1339 return ignore ? 0 : -ENOEXEC;
5f8640fb
LP
1340 }
1341
1342 free(c->selinux_context);
1343 c->selinux_context = k;
1344 c->selinux_context_ignore = ignore;
1345
1346 return 0;
1347}
1348
eef65bf3
MS
1349int config_parse_exec_apparmor_profile(
1350 const char *unit,
1351 const char *filename,
1352 unsigned line,
1353 const char *section,
1354 unsigned section_line,
1355 const char *lvalue,
1356 int ltype,
1357 const char *rvalue,
1358 void *data,
1359 void *userdata) {
1360
1361 ExecContext *c = data;
1362 Unit *u = userdata;
1363 bool ignore;
1364 char *k;
1365 int r;
1366
1367 assert(filename);
1368 assert(lvalue);
1369 assert(rvalue);
1370 assert(data);
1371
1372 if (isempty(rvalue)) {
a1e58e8e 1373 c->apparmor_profile = mfree(c->apparmor_profile);
eef65bf3
MS
1374 c->apparmor_profile_ignore = false;
1375 return 0;
1376 }
1377
1378 if (rvalue[0] == '-') {
1379 ignore = true;
1380 rvalue++;
1381 } else
1382 ignore = false;
1383
18913df9 1384 r = unit_full_printf(u, rvalue, &k);
eef65bf3 1385 if (r < 0) {
bb28e684
ZJS
1386 log_syntax(unit, LOG_ERR, filename, line, r,
1387 "Failed to resolve specifiers%s: %m",
1388 ignore ? ", ignoring" : "");
1389 return ignore ? 0 : -ENOEXEC;
eef65bf3
MS
1390 }
1391
1392 free(c->apparmor_profile);
1393 c->apparmor_profile = k;
1394 c->apparmor_profile_ignore = ignore;
1395
1396 return 0;
1397}
1398
2ca620c4
WC
1399int config_parse_exec_smack_process_label(
1400 const char *unit,
1401 const char *filename,
1402 unsigned line,
1403 const char *section,
1404 unsigned section_line,
1405 const char *lvalue,
1406 int ltype,
1407 const char *rvalue,
1408 void *data,
1409 void *userdata) {
1410
1411 ExecContext *c = data;
1412 Unit *u = userdata;
1413 bool ignore;
1414 char *k;
1415 int r;
1416
1417 assert(filename);
1418 assert(lvalue);
1419 assert(rvalue);
1420 assert(data);
1421
1422 if (isempty(rvalue)) {
a1e58e8e 1423 c->smack_process_label = mfree(c->smack_process_label);
2ca620c4
WC
1424 c->smack_process_label_ignore = false;
1425 return 0;
1426 }
1427
1428 if (rvalue[0] == '-') {
1429 ignore = true;
1430 rvalue++;
1431 } else
1432 ignore = false;
1433
18913df9 1434 r = unit_full_printf(u, rvalue, &k);
2ca620c4 1435 if (r < 0) {
bb28e684
ZJS
1436 log_syntax(unit, LOG_ERR, filename, line, r,
1437 "Failed to resolve specifiers%s: %m",
1438 ignore ? ", ignoring" : "");
1439 return ignore ? 0 : -ENOEXEC;
2ca620c4
WC
1440 }
1441
1442 free(c->smack_process_label);
1443 c->smack_process_label = k;
1444 c->smack_process_label_ignore = ignore;
1445
1446 return 0;
1447}
1448
e8e581bf
ZJS
1449int config_parse_timer(const char *unit,
1450 const char *filename,
1451 unsigned line,
1452 const char *section,
71a61510 1453 unsigned section_line,
e8e581bf
ZJS
1454 const char *lvalue,
1455 int ltype,
1456 const char *rvalue,
1457 void *data,
1458 void *userdata) {
871d7de4
LP
1459
1460 Timer *t = data;
2507992f 1461 usec_t usec = 0;
871d7de4
LP
1462 TimerValue *v;
1463 TimerBase b;
36697dc0 1464 CalendarSpec *c = NULL;
2507992f
DC
1465 Unit *u = userdata;
1466 _cleanup_free_ char *k = NULL;
1467 int r;
871d7de4
LP
1468
1469 assert(filename);
1470 assert(lvalue);
1471 assert(rvalue);
1472 assert(data);
1473
74051b9b
LP
1474 if (isempty(rvalue)) {
1475 /* Empty assignment resets list */
1476 timer_free_values(t);
1477 return 0;
1478 }
1479
36697dc0
LP
1480 b = timer_base_from_string(lvalue);
1481 if (b < 0) {
12ca818f 1482 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer base, ignoring: %s", lvalue);
c0b34696 1483 return 0;
871d7de4
LP
1484 }
1485
2507992f
DC
1486 r = unit_full_printf(u, rvalue, &k);
1487 if (r < 0) {
1488 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
1489 return 0;
1490 }
1491
36697dc0 1492 if (b == TIMER_CALENDAR) {
2507992f
DC
1493 if (calendar_spec_from_string(k, &c) < 0) {
1494 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", k);
36697dc0
LP
1495 return 0;
1496 }
36697dc0 1497 } else {
2507992f
DC
1498 if (parse_sec(k, &usec) < 0) {
1499 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", k);
36697dc0
LP
1500 return 0;
1501 }
871d7de4
LP
1502 }
1503
36697dc0 1504 v = new0(TimerValue, 1);
4d5e13a1 1505 if (!v) {
0b76b4d8 1506 calendar_spec_free(c);
74051b9b 1507 return log_oom();
4d5e13a1 1508 }
871d7de4
LP
1509
1510 v->base = b;
2507992f 1511 v->value = usec;
36697dc0 1512 v->calendar_spec = c;
871d7de4 1513
71fda00f 1514 LIST_PREPEND(value, t->values, v);
871d7de4
LP
1515
1516 return 0;
1517}
1518
3ecaa09b
LP
1519int config_parse_trigger_unit(
1520 const char *unit,
1521 const char *filename,
1522 unsigned line,
1523 const char *section,
71a61510 1524 unsigned section_line,
3ecaa09b
LP
1525 const char *lvalue,
1526 int ltype,
1527 const char *rvalue,
1528 void *data,
1529 void *userdata) {
871d7de4 1530
74051b9b 1531 _cleanup_free_ char *p = NULL;
3ecaa09b
LP
1532 Unit *u = data;
1533 UnitType type;
1534 int r;
398ef8ba
LP
1535
1536 assert(filename);
1537 assert(lvalue);
1538 assert(rvalue);
1539 assert(data);
1540
3ecaa09b 1541 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
12ca818f 1542 log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
3ecaa09b
LP
1543 return 0;
1544 }
871d7de4 1545
19f6d710 1546 r = unit_name_printf(u, rvalue, &p);
12ca818f
LP
1547 if (r < 0) {
1548 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1549 return 0;
1550 }
74051b9b 1551
12ca818f 1552 type = unit_name_to_type(p);
3ecaa09b 1553 if (type < 0) {
12ca818f 1554 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue);
c0b34696 1555 return 0;
871d7de4
LP
1556 }
1557
3ecaa09b 1558 if (type == u->type) {
12ca818f 1559 log_syntax(unit, LOG_ERR, filename, line, 0, "Trigger cannot be of same type, ignoring: %s", rvalue);
3ecaa09b
LP
1560 return 0;
1561 }
1562
12ca818f 1563 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
57020a3a 1564 if (r < 0) {
12ca818f 1565 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
c0b34696 1566 return 0;
871d7de4
LP
1567 }
1568
1569 return 0;
1570}
1571
e8e581bf
ZJS
1572int config_parse_path_spec(const char *unit,
1573 const char *filename,
1574 unsigned line,
1575 const char *section,
71a61510 1576 unsigned section_line,
e8e581bf
ZJS
1577 const char *lvalue,
1578 int ltype,
1579 const char *rvalue,
1580 void *data,
1581 void *userdata) {
01f78473
LP
1582
1583 Path *p = data;
1584 PathSpec *s;
1585 PathType b;
7fd1b19b 1586 _cleanup_free_ char *k = NULL;
19f6d710 1587 int r;
01f78473
LP
1588
1589 assert(filename);
1590 assert(lvalue);
1591 assert(rvalue);
1592 assert(data);
1593
74051b9b
LP
1594 if (isempty(rvalue)) {
1595 /* Empty assignment clears list */
1596 path_free_specs(p);
1597 return 0;
1598 }
1599
93e4c84b
LP
1600 b = path_type_from_string(lvalue);
1601 if (b < 0) {
12ca818f 1602 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue);
c0b34696 1603 return 0;
01f78473
LP
1604 }
1605
19f6d710
LP
1606 r = unit_full_printf(UNIT(p), rvalue, &k);
1607 if (r < 0) {
12ca818f
LP
1608 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
1609 return 0;
487060c2 1610 }
93e4c84b
LP
1611
1612 if (!path_is_absolute(k)) {
12ca818f 1613 log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k);
c0b34696 1614 return 0;
01f78473
LP
1615 }
1616
93e4c84b 1617 s = new0(PathSpec, 1);
543295ad 1618 if (!s)
93e4c84b 1619 return log_oom();
01f78473 1620
718db961 1621 s->unit = UNIT(p);
93e4c84b 1622 s->path = path_kill_slashes(k);
543295ad 1623 k = NULL;
01f78473
LP
1624 s->type = b;
1625 s->inotify_fd = -1;
1626
71fda00f 1627 LIST_PREPEND(spec, p->specs, s);
01f78473
LP
1628
1629 return 0;
1630}
1631
b02cb41c
LP
1632int config_parse_socket_service(
1633 const char *unit,
1634 const char *filename,
1635 unsigned line,
1636 const char *section,
1637 unsigned section_line,
1638 const char *lvalue,
1639 int ltype,
1640 const char *rvalue,
1641 void *data,
1642 void *userdata) {
d9ff321a 1643
4afd3348 1644 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
8dd4c05b 1645 _cleanup_free_ char *p = NULL;
d9ff321a 1646 Socket *s = data;
4ff77f66 1647 Unit *x;
8dd4c05b 1648 int r;
d9ff321a
LP
1649
1650 assert(filename);
1651 assert(lvalue);
1652 assert(rvalue);
1653 assert(data);
1654
19f6d710 1655 r = unit_name_printf(UNIT(s), rvalue, &p);
613b411c 1656 if (r < 0) {
bb28e684
ZJS
1657 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", rvalue);
1658 return -ENOEXEC;
613b411c 1659 }
74051b9b 1660
613b411c 1661 if (!endswith(p, ".service")) {
bb28e684
ZJS
1662 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service: %s", rvalue);
1663 return -ENOEXEC;
d9ff321a
LP
1664 }
1665
613b411c 1666 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
4ff77f66 1667 if (r < 0) {
bb28e684
ZJS
1668 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s: %s", rvalue, bus_error_message(&error, r));
1669 return -ENOEXEC;
d9ff321a
LP
1670 }
1671
4ff77f66
LP
1672 unit_ref_set(&s->service, x);
1673
d9ff321a
LP
1674 return 0;
1675}
1676
8dd4c05b
LP
1677int config_parse_fdname(
1678 const char *unit,
1679 const char *filename,
1680 unsigned line,
1681 const char *section,
1682 unsigned section_line,
1683 const char *lvalue,
1684 int ltype,
1685 const char *rvalue,
1686 void *data,
1687 void *userdata) {
1688
1689 _cleanup_free_ char *p = NULL;
1690 Socket *s = data;
1691 int r;
1692
1693 assert(filename);
1694 assert(lvalue);
1695 assert(rvalue);
1696 assert(data);
1697
1698 if (isempty(rvalue)) {
1699 s->fdname = mfree(s->fdname);
1700 return 0;
1701 }
1702
18913df9 1703 r = unit_full_printf(UNIT(s), rvalue, &p);
8dd4c05b
LP
1704 if (r < 0) {
1705 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1706 return 0;
1707 }
1708
1709 if (!fdname_is_valid(p)) {
1710 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
1711 return 0;
1712 }
1713
3b319885 1714 return free_and_replace(s->fdname, p);
8dd4c05b
LP
1715}
1716
b02cb41c
LP
1717int config_parse_service_sockets(
1718 const char *unit,
1719 const char *filename,
1720 unsigned line,
1721 const char *section,
1722 unsigned section_line,
1723 const char *lvalue,
1724 int ltype,
1725 const char *rvalue,
1726 void *data,
1727 void *userdata) {
f976f3f6
LP
1728
1729 Service *s = data;
7b2313f5 1730 const char *p;
b02cb41c 1731 int r;
f976f3f6
LP
1732
1733 assert(filename);
1734 assert(lvalue);
1735 assert(rvalue);
1736 assert(data);
1737
7b2313f5 1738 p = rvalue;
9ed794a3 1739 for (;;) {
6a0f3175 1740 _cleanup_free_ char *word = NULL, *k = NULL;
f976f3f6 1741
7b2313f5
SS
1742 r = extract_first_word(&p, &word, NULL, 0);
1743 if (r == 0)
1744 break;
1745 if (r == -ENOMEM)
74051b9b 1746 return log_oom();
7b2313f5
SS
1747 if (r < 0) {
1748 log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
1749 break;
1750 }
f976f3f6 1751
7b2313f5 1752 r = unit_name_printf(UNIT(s), word, &k);
b02cb41c
LP
1753 if (r < 0) {
1754 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1755 continue;
1756 }
57020a3a 1757
b02cb41c 1758 if (!endswith(k, ".socket")) {
12ca818f 1759 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
f976f3f6
LP
1760 continue;
1761 }
1762
b02cb41c 1763 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
57020a3a 1764 if (r < 0)
b02cb41c 1765 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
f976f3f6 1766
b02cb41c 1767 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
57020a3a 1768 if (r < 0)
b02cb41c 1769 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
f976f3f6
LP
1770 }
1771
1772 return 0;
1773}
1774
b02cb41c
LP
1775int config_parse_bus_name(
1776 const char *unit,
1777 const char *filename,
1778 unsigned line,
1779 const char *section,
1780 unsigned section_line,
1781 const char *lvalue,
1782 int ltype,
1783 const char *rvalue,
1784 void *data,
1785 void *userdata) {
1786
1787 _cleanup_free_ char *k = NULL;
1788 Unit *u = userdata;
1789 int r;
1790
1791 assert(filename);
1792 assert(lvalue);
1793 assert(rvalue);
1794 assert(u);
1795
1796 r = unit_full_printf(u, rvalue, &k);
1797 if (r < 0) {
1798 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
1799 return 0;
1800 }
1801
1802 if (!service_name_is_valid(k)) {
12ca818f 1803 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k);
b02cb41c
LP
1804 return 0;
1805 }
1806
1807 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
1808}
1809
aad41f08
LP
1810int config_parse_service_timeout(
1811 const char *unit,
1812 const char *filename,
1813 unsigned line,
1814 const char *section,
1815 unsigned section_line,
1816 const char *lvalue,
1817 int ltype,
1818 const char *rvalue,
1819 void *data,
1820 void *userdata) {
98709151
LN
1821
1822 Service *s = userdata;
aad41f08 1823 usec_t usec;
98709151
LN
1824 int r;
1825
1826 assert(filename);
1827 assert(lvalue);
1828 assert(rvalue);
1829 assert(s);
1830
aad41f08 1831 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
98709151 1832
aad41f08
LP
1833 r = parse_sec(rvalue, &usec);
1834 if (r < 0) {
1835 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
1836 return 0;
1837 }
d568a335 1838
36c16a7c
LP
1839 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1840 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1841 * all other timeouts. */
aad41f08
LP
1842 if (usec <= 0)
1843 usec = USEC_INFINITY;
1844
1845 if (!streq(lvalue, "TimeoutStopSec")) {
1846 s->start_timeout_defined = true;
1847 s->timeout_start_usec = usec;
1848 }
36c16a7c 1849
aad41f08
LP
1850 if (!streq(lvalue, "TimeoutStartSec"))
1851 s->timeout_stop_usec = usec;
36c16a7c 1852
d568a335 1853 return 0;
98709151
LN
1854}
1855
89beff89
LP
1856int config_parse_sec_fix_0(
1857 const char *unit,
1858 const char *filename,
1859 unsigned line,
1860 const char *section,
1861 unsigned section_line,
1862 const char *lvalue,
1863 int ltype,
1864 const char *rvalue,
1865 void *data,
1866 void *userdata) {
1867
1868 usec_t *usec = data;
1869 int r;
1870
1871 assert(filename);
1872 assert(lvalue);
1873 assert(rvalue);
1874 assert(usec);
1875
1876 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1877 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1878 * timeout. */
1879
0004f698 1880 r = parse_sec_fix_0(rvalue, usec);
89beff89
LP
1881 if (r < 0) {
1882 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
1883 return 0;
1884 }
1885
89beff89
LP
1886 return 0;
1887}
1888
66dccd8d
LP
1889int config_parse_user_group(
1890 const char *unit,
1891 const char *filename,
1892 unsigned line,
1893 const char *section,
1894 unsigned section_line,
1895 const char *lvalue,
1896 int ltype,
1897 const char *rvalue,
1898 void *data,
1899 void *userdata) {
1900
1901 char **user = data, *n;
1902 Unit *u = userdata;
1903 int r;
1904
1905 assert(filename);
1906 assert(lvalue);
1907 assert(rvalue);
1908 assert(u);
1909
1910 if (isempty(rvalue))
1911 n = NULL;
1912 else {
1913 _cleanup_free_ char *k = NULL;
1914
1915 r = unit_full_printf(u, rvalue, &k);
1916 if (r < 0) {
bb28e684
ZJS
1917 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue);
1918 return -ENOEXEC;
66dccd8d
LP
1919 }
1920
1921 if (!valid_user_group_name_or_id(k)) {
bb28e684
ZJS
1922 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
1923 return -ENOEXEC;
66dccd8d
LP
1924 }
1925
1926 n = k;
1927 k = NULL;
1928 }
1929
1930 free(*user);
1931 *user = n;
1932
1933 return 0;
1934}
1935
1936int config_parse_user_group_strv(
1937 const char *unit,
1938 const char *filename,
1939 unsigned line,
1940 const char *section,
1941 unsigned section_line,
1942 const char *lvalue,
1943 int ltype,
1944 const char *rvalue,
1945 void *data,
1946 void *userdata) {
1947
1948 char ***users = data;
1949 Unit *u = userdata;
1950 const char *p;
1951 int r;
1952
1953 assert(filename);
1954 assert(lvalue);
1955 assert(rvalue);
1956 assert(u);
1957
1958 if (isempty(rvalue)) {
1959 char **empty;
1960
1961 empty = new0(char*, 1);
1962 if (!empty)
1963 return log_oom();
1964
1965 strv_free(*users);
1966 *users = empty;
1967
1968 return 0;
1969 }
1970
1971 p = rvalue;
1972 for (;;) {
1973 _cleanup_free_ char *word = NULL, *k = NULL;
1974
9a82ab95 1975 r = extract_first_word(&p, &word, NULL, 0);
66dccd8d
LP
1976 if (r == 0)
1977 break;
1978 if (r == -ENOMEM)
1979 return log_oom();
1980 if (r < 0) {
bb28e684
ZJS
1981 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue);
1982 return -ENOEXEC;
66dccd8d
LP
1983 }
1984
1985 r = unit_full_printf(u, word, &k);
1986 if (r < 0) {
bb28e684
ZJS
1987 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word);
1988 return -ENOEXEC;
66dccd8d
LP
1989 }
1990
1991 if (!valid_user_group_name_or_id(k)) {
bb28e684
ZJS
1992 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
1993 return -ENOEXEC;
66dccd8d
LP
1994 }
1995
1996 r = strv_push(users, k);
1997 if (r < 0)
1998 return log_oom();
1999
2000 k = NULL;
2001 }
2002
2003 return 0;
2004}
2005
5f5d8eab
LP
2006int config_parse_working_directory(
2007 const char *unit,
2008 const char *filename,
2009 unsigned line,
2010 const char *section,
2011 unsigned section_line,
2012 const char *lvalue,
2013 int ltype,
2014 const char *rvalue,
2015 void *data,
2016 void *userdata) {
2017
2018 ExecContext *c = data;
2019 Unit *u = userdata;
2020 bool missing_ok;
2021 int r;
2022
2023 assert(filename);
2024 assert(lvalue);
2025 assert(rvalue);
2026 assert(c);
2027 assert(u);
2028
2029 if (rvalue[0] == '-') {
2030 missing_ok = true;
2031 rvalue++;
2032 } else
2033 missing_ok = false;
2034
2035 if (streq(rvalue, "~")) {
2036 c->working_directory_home = true;
2037 c->working_directory = mfree(c->working_directory);
2038 } else {
2039 _cleanup_free_ char *k = NULL;
2040
2041 r = unit_full_printf(u, rvalue, &k);
2042 if (r < 0) {
bb28e684
ZJS
2043 log_syntax(unit, LOG_ERR, filename, line, r,
2044 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2045 rvalue, missing_ok ? ", ignoring" : "");
2046 return missing_ok ? 0 : -ENOEXEC;
5f5d8eab
LP
2047 }
2048
2049 path_kill_slashes(k);
2050
2051 if (!utf8_is_valid(k)) {
0e05ee04 2052 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
bb28e684 2053 return missing_ok ? 0 : -ENOEXEC;
5f5d8eab
LP
2054 }
2055
2056 if (!path_is_absolute(k)) {
bb28e684
ZJS
2057 log_syntax(unit, LOG_ERR, filename, line, 0,
2058 "Working directory path '%s' is not absolute%s.",
2059 rvalue, missing_ok ? ", ignoring" : "");
2060 return missing_ok ? 0 : -ENOEXEC;
5f5d8eab
LP
2061 }
2062
5f5d8eab 2063 c->working_directory_home = false;
bb28e684 2064 free_and_replace(c->working_directory, k);
5f5d8eab
LP
2065 }
2066
2067 c->working_directory_missing_ok = missing_ok;
2068 return 0;
2069}
2070
e8e581bf
ZJS
2071int config_parse_unit_env_file(const char *unit,
2072 const char *filename,
2073 unsigned line,
2074 const char *section,
71a61510 2075 unsigned section_line,
e8e581bf
ZJS
2076 const char *lvalue,
2077 int ltype,
2078 const char *rvalue,
2079 void *data,
2080 void *userdata) {
ddb26e18 2081
853b8397 2082 char ***env = data;
8fef7659 2083 Unit *u = userdata;
19f6d710 2084 _cleanup_free_ char *n = NULL;
853b8397 2085 int r;
ddb26e18
LP
2086
2087 assert(filename);
2088 assert(lvalue);
2089 assert(rvalue);
2090 assert(data);
2091
74051b9b
LP
2092 if (isempty(rvalue)) {
2093 /* Empty assignment frees the list */
6796073e 2094 *env = strv_free(*env);
74051b9b
LP
2095 return 0;
2096 }
2097
19f6d710 2098 r = unit_full_printf(u, rvalue, &n);
12ca818f
LP
2099 if (r < 0) {
2100 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2101 return 0;
2102 }
8fef7659 2103
12ca818f
LP
2104 if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) {
2105 log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n);
afe4bfe2
LP
2106 return 0;
2107 }
2108
12ca818f 2109 r = strv_extend(env, n);
853b8397
LP
2110 if (r < 0)
2111 return log_oom();
2112
2113 return 0;
2114}
2115
e8e581bf
ZJS
2116int config_parse_environ(const char *unit,
2117 const char *filename,
2118 unsigned line,
2119 const char *section,
71a61510 2120 unsigned section_line,
e8e581bf
ZJS
2121 const char *lvalue,
2122 int ltype,
2123 const char *rvalue,
2124 void *data,
2125 void *userdata) {
853b8397
LP
2126
2127 Unit *u = userdata;
035fe294
ZJS
2128 char ***env = data;
2129 const char *p;
19f6d710 2130 int r;
853b8397
LP
2131
2132 assert(filename);
2133 assert(lvalue);
2134 assert(rvalue);
97d0e5f8 2135 assert(data);
853b8397
LP
2136
2137 if (isempty(rvalue)) {
2138 /* Empty assignment resets the list */
6796073e 2139 *env = strv_free(*env);
853b8397
LP
2140 return 0;
2141 }
2142
035fe294
ZJS
2143 for (p = rvalue;; ) {
2144 _cleanup_free_ char *word = NULL, *k = NULL;
035fe294
ZJS
2145
2146 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
2147 if (r == 0)
2148 return 0;
2149 if (r == -ENOMEM)
2150 return log_oom();
12ca818f 2151 if (r < 0) {
035fe294
ZJS
2152 log_syntax(unit, LOG_WARNING, filename, line, r,
2153 "Invalid syntax, ignoring: %s", rvalue);
12ca818f
LP
2154 return 0;
2155 }
97d0e5f8 2156
035fe294
ZJS
2157 if (u) {
2158 r = unit_full_printf(u, word, &k);
2159 if (r < 0) {
2160 log_syntax(unit, LOG_ERR, filename, line, r,
2161 "Failed to resolve specifiers, ignoring: %s", k);
2162 continue;
2163 }
2164 } else {
2165 k = word;
2166 word = NULL;
527b7a42 2167 }
853b8397 2168
035fe294
ZJS
2169 if (!env_assignment_is_valid(k)) {
2170 log_syntax(unit, LOG_ERR, filename, line, 0,
2171 "Invalid environment assignment, ignoring: %s", k);
853b8397
LP
2172 continue;
2173 }
2174
54ac3494
ZJS
2175 r = strv_env_replace(env, k);
2176 if (r < 0)
853b8397 2177 return log_oom();
54ac3494 2178 k = NULL;
853b8397 2179 }
ddb26e18
LP
2180}
2181
b4c14404
FB
2182int config_parse_pass_environ(const char *unit,
2183 const char *filename,
2184 unsigned line,
2185 const char *section,
2186 unsigned section_line,
2187 const char *lvalue,
2188 int ltype,
2189 const char *rvalue,
2190 void *data,
2191 void *userdata) {
2192
2193 const char *whole_rvalue = rvalue;
2194 char*** passenv = data;
2195 _cleanup_strv_free_ char **n = NULL;
2196 size_t nlen = 0, nbufsize = 0;
2197 int r;
2198
2199 assert(filename);
2200 assert(lvalue);
2201 assert(rvalue);
2202 assert(data);
2203
2204 if (isempty(rvalue)) {
2205 /* Empty assignment resets the list */
2206 *passenv = strv_free(*passenv);
2207 return 0;
2208 }
2209
2210 for (;;) {
2211 _cleanup_free_ char *word = NULL;
2212
9a82ab95 2213 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
b4c14404
FB
2214 if (r == 0)
2215 break;
2216 if (r == -ENOMEM)
2217 return log_oom();
2218 if (r < 0) {
2219 log_syntax(unit, LOG_ERR, filename, line, r,
2220 "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
2221 break;
2222 }
2223
2224 if (!env_name_is_valid(word)) {
2225 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2226 "Invalid environment name for %s, ignoring: %s", lvalue, word);
2227 continue;
2228 }
2229
2230 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2231 return log_oom();
2232 n[nlen++] = word;
2233 n[nlen] = NULL;
2234 word = NULL;
2235 }
2236
2237 if (n) {
2238 r = strv_extend_strv(passenv, n, true);
2239 if (r < 0)
2240 return r;
2241 }
2242
2243 return 0;
2244}
2245
e8e581bf
ZJS
2246int config_parse_ip_tos(const char *unit,
2247 const char *filename,
2248 unsigned line,
2249 const char *section,
71a61510 2250 unsigned section_line,
e8e581bf
ZJS
2251 const char *lvalue,
2252 int ltype,
2253 const char *rvalue,
2254 void *data,
2255 void *userdata) {
4fd5948e
LP
2256
2257 int *ip_tos = data, x;
4fd5948e
LP
2258
2259 assert(filename);
2260 assert(lvalue);
2261 assert(rvalue);
2262 assert(data);
2263
f8b69d1d
MS
2264 x = ip_tos_from_string(rvalue);
2265 if (x < 0) {
12ca818f 2266 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue);
f8b69d1d
MS
2267 return 0;
2268 }
4fd5948e
LP
2269
2270 *ip_tos = x;
2271 return 0;
2272}
2273
59fccdc5
LP
2274int config_parse_unit_condition_path(
2275 const char *unit,
2276 const char *filename,
2277 unsigned line,
2278 const char *section,
2279 unsigned section_line,
2280 const char *lvalue,
2281 int ltype,
2282 const char *rvalue,
2283 void *data,
2284 void *userdata) {
52661efd 2285
2fbe635a 2286 _cleanup_free_ char *p = NULL;
59fccdc5
LP
2287 Condition **list = data, *c;
2288 ConditionType t = ltype;
2289 bool trigger, negate;
2290 Unit *u = userdata;
19f6d710 2291 int r;
52661efd
LP
2292
2293 assert(filename);
2294 assert(lvalue);
2295 assert(rvalue);
2296 assert(data);
2297
74051b9b
LP
2298 if (isempty(rvalue)) {
2299 /* Empty assignment resets the list */
447021aa 2300 *list = condition_free_list(*list);
74051b9b
LP
2301 return 0;
2302 }
2303
ab7f148f
LP
2304 trigger = rvalue[0] == '|';
2305 if (trigger)
267632f0
LP
2306 rvalue++;
2307
ab7f148f
LP
2308 negate = rvalue[0] == '!';
2309 if (negate)
52661efd
LP
2310 rvalue++;
2311
19f6d710 2312 r = unit_full_printf(u, rvalue, &p);
59fccdc5 2313 if (r < 0) {
12ca818f 2314 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
59fccdc5 2315 return 0;
19f6d710 2316 }
095b2d7a
AK
2317
2318 if (!path_is_absolute(p)) {
12ca818f 2319 log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p);
52661efd
LP
2320 return 0;
2321 }
2322
59fccdc5 2323 c = condition_new(t, p, trigger, negate);
ab7f148f 2324 if (!c)
74051b9b 2325 return log_oom();
52661efd 2326
59fccdc5 2327 LIST_PREPEND(conditions, *list, c);
52661efd
LP
2328 return 0;
2329}
2330
59fccdc5
LP
2331int config_parse_unit_condition_string(
2332 const char *unit,
2333 const char *filename,
2334 unsigned line,
2335 const char *section,
2336 unsigned section_line,
2337 const char *lvalue,
2338 int ltype,
2339 const char *rvalue,
2340 void *data,
2341 void *userdata) {
039655a4 2342
2fbe635a 2343 _cleanup_free_ char *s = NULL;
59fccdc5
LP
2344 Condition **list = data, *c;
2345 ConditionType t = ltype;
2346 bool trigger, negate;
2347 Unit *u = userdata;
19f6d710 2348 int r;
039655a4
LP
2349
2350 assert(filename);
2351 assert(lvalue);
2352 assert(rvalue);
2353 assert(data);
2354
74051b9b
LP
2355 if (isempty(rvalue)) {
2356 /* Empty assignment resets the list */
447021aa 2357 *list = condition_free_list(*list);
74051b9b
LP
2358 return 0;
2359 }
2360
c0d6e764
LP
2361 trigger = rvalue[0] == '|';
2362 if (trigger)
267632f0
LP
2363 rvalue++;
2364
c0d6e764
LP
2365 negate = rvalue[0] == '!';
2366 if (negate)
039655a4
LP
2367 rvalue++;
2368
19f6d710 2369 r = unit_full_printf(u, rvalue, &s);
59fccdc5 2370 if (r < 0) {
12ca818f 2371 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
59fccdc5 2372 return 0;
19f6d710 2373 }
095b2d7a 2374
59fccdc5 2375 c = condition_new(t, s, trigger, negate);
c0d6e764
LP
2376 if (!c)
2377 return log_oom();
039655a4 2378
59fccdc5 2379 LIST_PREPEND(conditions, *list, c);
039655a4
LP
2380 return 0;
2381}
2382
59fccdc5
LP
2383int config_parse_unit_condition_null(
2384 const char *unit,
2385 const char *filename,
2386 unsigned line,
2387 const char *section,
2388 unsigned section_line,
2389 const char *lvalue,
2390 int ltype,
2391 const char *rvalue,
2392 void *data,
2393 void *userdata) {
d257ddef 2394
59fccdc5 2395 Condition **list = data, *c;
267632f0 2396 bool trigger, negate;
d257ddef
LP
2397 int b;
2398
2399 assert(filename);
2400 assert(lvalue);
2401 assert(rvalue);
2402 assert(data);
2403
74051b9b
LP
2404 if (isempty(rvalue)) {
2405 /* Empty assignment resets the list */
447021aa 2406 *list = condition_free_list(*list);
74051b9b
LP
2407 return 0;
2408 }
2409
2410 trigger = rvalue[0] == '|';
2411 if (trigger)
267632f0
LP
2412 rvalue++;
2413
74051b9b
LP
2414 negate = rvalue[0] == '!';
2415 if (negate)
d257ddef
LP
2416 rvalue++;
2417
74051b9b
LP
2418 b = parse_boolean(rvalue);
2419 if (b < 0) {
12ca818f 2420 log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
d257ddef
LP
2421 return 0;
2422 }
2423
2424 if (!b)
2425 negate = !negate;
2426
74051b9b
LP
2427 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2428 if (!c)
2429 return log_oom();
d257ddef 2430
59fccdc5 2431 LIST_PREPEND(conditions, *list, c);
d257ddef
LP
2432 return 0;
2433}
2434
f975e971 2435DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
87a47f99 2436DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier");
c952c6ec 2437
a57f7e2c
LP
2438int config_parse_unit_requires_mounts_for(
2439 const char *unit,
2440 const char *filename,
2441 unsigned line,
2442 const char *section,
71a61510 2443 unsigned section_line,
a57f7e2c
LP
2444 const char *lvalue,
2445 int ltype,
2446 const char *rvalue,
2447 void *data,
2448 void *userdata) {
7c8fa05c
LP
2449
2450 Unit *u = userdata;
035fe294
ZJS
2451 const char *p;
2452 int r;
7c8fa05c
LP
2453
2454 assert(filename);
2455 assert(lvalue);
2456 assert(rvalue);
2457 assert(data);
2458
035fe294 2459 for (p = rvalue;; ) {
744bb5b1 2460 _cleanup_free_ char *word = NULL, *resolved = NULL;
a57f7e2c 2461
035fe294
ZJS
2462 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2463 if (r == 0)
2464 return 0;
2465 if (r == -ENOMEM)
a57f7e2c 2466 return log_oom();
035fe294
ZJS
2467 if (r < 0) {
2468 log_syntax(unit, LOG_WARNING, filename, line, r,
2469 "Invalid syntax, ignoring: %s", rvalue);
2470 return 0;
2471 }
7c8fa05c 2472
035fe294 2473 if (!utf8_is_valid(word)) {
0e05ee04 2474 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
a57f7e2c
LP
2475 continue;
2476 }
7c8fa05c 2477
744bb5b1
LP
2478 r = unit_full_printf(u, word, &resolved);
2479 if (r < 0) {
2480 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit name \"%s\", ignoring: %m", word);
2481 continue;
2482 }
2483
2484 r = unit_require_mounts_for(u, resolved);
a57f7e2c 2485 if (r < 0) {
744bb5b1 2486 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount \"%s\", ignoring: %m", resolved);
a57f7e2c
LP
2487 continue;
2488 }
2489 }
7c8fa05c 2490}
9e372868 2491
e8e581bf
ZJS
2492int config_parse_documentation(const char *unit,
2493 const char *filename,
2494 unsigned line,
2495 const char *section,
71a61510 2496 unsigned section_line,
e8e581bf
ZJS
2497 const char *lvalue,
2498 int ltype,
2499 const char *rvalue,
2500 void *data,
2501 void *userdata) {
49dbfa7b
LP
2502
2503 Unit *u = userdata;
2504 int r;
2505 char **a, **b;
2506
2507 assert(filename);
2508 assert(lvalue);
2509 assert(rvalue);
2510 assert(u);
2511
74051b9b
LP
2512 if (isempty(rvalue)) {
2513 /* Empty assignment resets the list */
6796073e 2514 u->documentation = strv_free(u->documentation);
74051b9b
LP
2515 return 0;
2516 }
2517
71a61510 2518 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 2519 rvalue, data, userdata);
49dbfa7b
LP
2520 if (r < 0)
2521 return r;
2522
2523 for (a = b = u->documentation; a && *a; a++) {
2524
a2e03378 2525 if (documentation_url_is_valid(*a))
49dbfa7b
LP
2526 *(b++) = *a;
2527 else {
12ca818f 2528 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a);
49dbfa7b
LP
2529 free(*a);
2530 }
2531 }
f6d2d421
ZJS
2532 if (b)
2533 *b = NULL;
49dbfa7b
LP
2534
2535 return r;
2536}
2537
c0467cf3 2538#ifdef HAVE_SECCOMP
8130926d 2539
201c1cc2
TM
2540static int syscall_filter_parse_one(
2541 const char *unit,
2542 const char *filename,
2543 unsigned line,
2544 ExecContext *c,
2545 bool invert,
2546 const char *t,
2547 bool warn) {
2548 int r;
2549
8130926d
LP
2550 if (t[0] == '@') {
2551 const SyscallFilterSet *set;
2552 const char *i;
201c1cc2 2553
8130926d
LP
2554 set = syscall_filter_set_find(t);
2555 if (!set) {
2556 if (warn)
2557 log_syntax(unit, LOG_WARNING, filename, line, 0, "Don't know system call group, ignoring: %s", t);
2558 return 0;
2559 }
201c1cc2 2560
8130926d
LP
2561 NULSTR_FOREACH(i, set->value) {
2562 r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false);
2563 if (r < 0)
2564 return r;
2565 }
201c1cc2
TM
2566 } else {
2567 int id;
2568
2569 id = seccomp_syscall_resolve_name(t);
391b81cd 2570 if (id == __NR_SCMP_ERROR) {
201c1cc2 2571 if (warn)
8130926d 2572 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
201c1cc2
TM
2573 return 0;
2574 }
2575
2576 /* If we previously wanted to forbid a syscall and now
2577 * we want to allow it, then remove it from the list
2578 */
2579 if (!invert == c->syscall_whitelist) {
2580 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2581 if (r == 0)
2582 return 0;
2583 if (r < 0)
2584 return log_oom();
2585 } else
8130926d 2586 (void) set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
201c1cc2 2587 }
8130926d 2588
201c1cc2
TM
2589 return 0;
2590}
2591
17df7223
LP
2592int config_parse_syscall_filter(
2593 const char *unit,
2594 const char *filename,
2595 unsigned line,
2596 const char *section,
2597 unsigned section_line,
2598 const char *lvalue,
2599 int ltype,
2600 const char *rvalue,
2601 void *data,
2602 void *userdata) {
2603
8351ceae
LP
2604 ExecContext *c = data;
2605 Unit *u = userdata;
b5fb3789 2606 bool invert = false;
8130926d 2607 const char *p;
17df7223 2608 int r;
8351ceae
LP
2609
2610 assert(filename);
2611 assert(lvalue);
2612 assert(rvalue);
2613 assert(u);
2614
74051b9b
LP
2615 if (isempty(rvalue)) {
2616 /* Empty assignment resets the list */
525d3cc7 2617 c->syscall_filter = set_free(c->syscall_filter);
17df7223 2618 c->syscall_whitelist = false;
74051b9b
LP
2619 return 0;
2620 }
2621
8351ceae
LP
2622 if (rvalue[0] == '~') {
2623 invert = true;
2624 rvalue++;
2625 }
2626
17df7223 2627 if (!c->syscall_filter) {
d5099efc 2628 c->syscall_filter = set_new(NULL);
17df7223
LP
2629 if (!c->syscall_filter)
2630 return log_oom();
2631
c0467cf3 2632 if (invert)
17df7223
LP
2633 /* Allow everything but the ones listed */
2634 c->syscall_whitelist = false;
c0467cf3 2635 else {
17df7223
LP
2636 /* Allow nothing but the ones listed */
2637 c->syscall_whitelist = true;
8351ceae 2638
17df7223 2639 /* Accept default syscalls if we are on a whitelist */
201c1cc2
TM
2640 r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false);
2641 if (r < 0)
2642 return r;
c0467cf3 2643 }
8351ceae
LP
2644 }
2645
8130926d
LP
2646 p = rvalue;
2647 for (;;) {
2648 _cleanup_free_ char *word = NULL;
8351ceae 2649
8130926d
LP
2650 r = extract_first_word(&p, &word, NULL, 0);
2651 if (r == 0)
2652 break;
2653 if (r == -ENOMEM)
74051b9b 2654 return log_oom();
8130926d
LP
2655 if (r < 0) {
2656 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
2657 break;
2658 }
8351ceae 2659
8130926d 2660 r = syscall_filter_parse_one(unit, filename, line, c, invert, word, true);
201c1cc2
TM
2661 if (r < 0)
2662 return r;
c0467cf3
RC
2663 }
2664
17df7223
LP
2665 return 0;
2666}
2667
57183d11
LP
2668int config_parse_syscall_archs(
2669 const char *unit,
2670 const char *filename,
2671 unsigned line,
2672 const char *section,
2673 unsigned section_line,
2674 const char *lvalue,
2675 int ltype,
2676 const char *rvalue,
2677 void *data,
2678 void *userdata) {
2679
d3b1c508 2680 Set **archs = data;
035fe294 2681 const char *p;
57183d11
LP
2682 int r;
2683
2684 if (isempty(rvalue)) {
525d3cc7 2685 *archs = set_free(*archs);
57183d11
LP
2686 return 0;
2687 }
2688
d5099efc 2689 r = set_ensure_allocated(archs, NULL);
57183d11
LP
2690 if (r < 0)
2691 return log_oom();
2692
035fe294
ZJS
2693 for (p = rvalue;;) {
2694 _cleanup_free_ char *word = NULL;
57183d11
LP
2695 uint32_t a;
2696
035fe294
ZJS
2697 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2698 if (r == 0)
2699 return 0;
2700 if (r == -ENOMEM)
57183d11 2701 return log_oom();
035fe294
ZJS
2702 if (r < 0) {
2703 log_syntax(unit, LOG_WARNING, filename, line, r,
2704 "Invalid syntax, ignoring: %s", rvalue);
2705 return 0;
2706 }
57183d11 2707
035fe294 2708 r = seccomp_arch_from_string(word, &a);
57183d11 2709 if (r < 0) {
035fe294
ZJS
2710 log_syntax(unit, LOG_ERR, filename, line, r,
2711 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
57183d11
LP
2712 continue;
2713 }
2714
d3b1c508 2715 r = set_put(*archs, UINT32_TO_PTR(a + 1));
57183d11
LP
2716 if (r < 0)
2717 return log_oom();
2718 }
57183d11
LP
2719}
2720
17df7223
LP
2721int config_parse_syscall_errno(
2722 const char *unit,
2723 const char *filename,
2724 unsigned line,
2725 const char *section,
2726 unsigned section_line,
2727 const char *lvalue,
2728 int ltype,
2729 const char *rvalue,
2730 void *data,
2731 void *userdata) {
2732
2733 ExecContext *c = data;
2734 int e;
2735
2736 assert(filename);
2737 assert(lvalue);
2738 assert(rvalue);
2739
2740 if (isempty(rvalue)) {
2741 /* Empty assignment resets to KILL */
2742 c->syscall_errno = 0;
2743 return 0;
8351ceae
LP
2744 }
2745
17df7223
LP
2746 e = errno_from_name(rvalue);
2747 if (e < 0) {
12ca818f 2748 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
17df7223
LP
2749 return 0;
2750 }
8351ceae 2751
17df7223 2752 c->syscall_errno = e;
8351ceae
LP
2753 return 0;
2754}
4298d0b5
LP
2755
2756int config_parse_address_families(
2757 const char *unit,
2758 const char *filename,
2759 unsigned line,
2760 const char *section,
2761 unsigned section_line,
2762 const char *lvalue,
2763 int ltype,
2764 const char *rvalue,
2765 void *data,
2766 void *userdata) {
2767
2768 ExecContext *c = data;
4298d0b5 2769 bool invert = false;
035fe294 2770 const char *p;
4298d0b5
LP
2771 int r;
2772
2773 assert(filename);
2774 assert(lvalue);
2775 assert(rvalue);
4298d0b5
LP
2776
2777 if (isempty(rvalue)) {
2778 /* Empty assignment resets the list */
525d3cc7 2779 c->address_families = set_free(c->address_families);
4298d0b5
LP
2780 c->address_families_whitelist = false;
2781 return 0;
2782 }
2783
2784 if (rvalue[0] == '~') {
2785 invert = true;
2786 rvalue++;
2787 }
2788
2789 if (!c->address_families) {
d5099efc 2790 c->address_families = set_new(NULL);
4298d0b5
LP
2791 if (!c->address_families)
2792 return log_oom();
2793
2794 c->address_families_whitelist = !invert;
2795 }
2796
035fe294
ZJS
2797 for (p = rvalue;;) {
2798 _cleanup_free_ char *word = NULL;
4298d0b5
LP
2799 int af;
2800
035fe294
ZJS
2801 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2802 if (r == 0)
2803 return 0;
2804 if (r == -ENOMEM)
4298d0b5 2805 return log_oom();
035fe294
ZJS
2806 if (r < 0) {
2807 log_syntax(unit, LOG_WARNING, filename, line, r,
2808 "Invalid syntax, ignoring: %s", rvalue);
2809 return 0;
2810 }
4298d0b5 2811
035fe294 2812 af = af_from_name(word);
4298d0b5 2813 if (af <= 0) {
035fe294
ZJS
2814 log_syntax(unit, LOG_ERR, filename, line, 0,
2815 "Failed to parse address family \"%s\", ignoring: %m", word);
4298d0b5
LP
2816 continue;
2817 }
2818
2819 /* If we previously wanted to forbid an address family and now
035fe294 2820 * we want to allow it, then just remove it from the list.
4298d0b5
LP
2821 */
2822 if (!invert == c->address_families_whitelist) {
2823 r = set_put(c->address_families, INT_TO_PTR(af));
4298d0b5
LP
2824 if (r < 0)
2825 return log_oom();
2826 } else
2827 set_remove(c->address_families, INT_TO_PTR(af));
2828 }
4298d0b5 2829}
add00535
LP
2830
2831int config_parse_restrict_namespaces(
2832 const char *unit,
2833 const char *filename,
2834 unsigned line,
2835 const char *section,
2836 unsigned section_line,
2837 const char *lvalue,
2838 int ltype,
2839 const char *rvalue,
2840 void *data,
2841 void *userdata) {
2842
2843 ExecContext *c = data;
2844 bool invert = false;
2845 int r;
2846
2847 if (isempty(rvalue)) {
2848 /* Reset to the default. */
2849 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
2850 return 0;
2851 }
2852
2853 if (rvalue[0] == '~') {
2854 invert = true;
2855 rvalue++;
2856 }
2857
2858 r = parse_boolean(rvalue);
2859 if (r > 0)
2860 c->restrict_namespaces = 0;
2861 else if (r == 0)
2862 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
2863 else {
2864 /* Not a boolean argument, in this case it's a list of namespace types. */
2865
2866 r = namespace_flag_from_string_many(rvalue, &c->restrict_namespaces);
2867 if (r < 0) {
2868 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
2869 return 0;
2870 }
2871 }
2872
2873 if (invert)
2874 c->restrict_namespaces = (~c->restrict_namespaces) & NAMESPACE_FLAGS_ALL;
2875
2876 return 0;
2877}
c0467cf3 2878#endif
8351ceae 2879
a016b922
LP
2880int config_parse_unit_slice(
2881 const char *unit,
2882 const char *filename,
2883 unsigned line,
2884 const char *section,
71a61510 2885 unsigned section_line,
a016b922
LP
2886 const char *lvalue,
2887 int ltype,
2888 const char *rvalue,
2889 void *data,
2890 void *userdata) {
2891
2892 _cleanup_free_ char *k = NULL;
d79200e2 2893 Unit *u = userdata, *slice = NULL;
a016b922
LP
2894 int r;
2895
2896 assert(filename);
2897 assert(lvalue);
2898 assert(rvalue);
2899 assert(u);
2900
19f6d710 2901 r = unit_name_printf(u, rvalue, &k);
d79200e2
LP
2902 if (r < 0) {
2903 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2904 return 0;
19f6d710 2905 }
a016b922 2906
19f6d710 2907 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
a016b922 2908 if (r < 0) {
d79200e2 2909 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k);
a016b922
LP
2910 return 0;
2911 }
2912
d79200e2
LP
2913 r = unit_set_slice(u, slice);
2914 if (r < 0) {
2915 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id);
a016b922
LP
2916 return 0;
2917 }
2918
a016b922
LP
2919 return 0;
2920}
2921
4ad49000
LP
2922DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2923
66ebf6c0
TH
2924int config_parse_cpu_weight(
2925 const char *unit,
2926 const char *filename,
2927 unsigned line,
2928 const char *section,
2929 unsigned section_line,
2930 const char *lvalue,
2931 int ltype,
2932 const char *rvalue,
2933 void *data,
2934 void *userdata) {
2935
2936 uint64_t *weight = data;
2937 int r;
2938
2939 assert(filename);
2940 assert(lvalue);
2941 assert(rvalue);
2942
2943 r = cg_weight_parse(rvalue, weight);
2944 if (r < 0) {
2945 log_syntax(unit, LOG_ERR, filename, line, r, "CPU weight '%s' invalid. Ignoring.", rvalue);
2946 return 0;
2947 }
2948
2949 return 0;
2950}
2951
4ad49000
LP
2952int config_parse_cpu_shares(
2953 const char *unit,
2954 const char *filename,
2955 unsigned line,
2956 const char *section,
71a61510 2957 unsigned section_line,
4ad49000
LP
2958 const char *lvalue,
2959 int ltype,
2960 const char *rvalue,
2961 void *data,
2962 void *userdata) {
2963
d53d9474 2964 uint64_t *shares = data;
95ae05c0
WC
2965 int r;
2966
2967 assert(filename);
2968 assert(lvalue);
2969 assert(rvalue);
2970
d53d9474
LP
2971 r = cg_cpu_shares_parse(rvalue, shares);
2972 if (r < 0) {
2973 log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
2974 return 0;
2975 }
2976
4ad49000
LP
2977 return 0;
2978}
2979
b2f8b02e
LP
2980int config_parse_cpu_quota(
2981 const char *unit,
2982 const char *filename,
2983 unsigned line,
2984 const char *section,
2985 unsigned section_line,
2986 const char *lvalue,
2987 int ltype,
2988 const char *rvalue,
2989 void *data,
2990 void *userdata) {
2991
2992 CGroupContext *c = data;
9184ca48 2993 int r;
b2f8b02e
LP
2994
2995 assert(filename);
2996 assert(lvalue);
2997 assert(rvalue);
2998
2999 if (isempty(rvalue)) {
3a43da28 3000 c->cpu_quota_per_sec_usec = USEC_INFINITY;
b2f8b02e
LP
3001 return 0;
3002 }
3003
5124866d 3004 r = parse_percent_unbounded(rvalue);
9184ca48
LP
3005 if (r <= 0) {
3006 log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
9a054909 3007 return 0;
b2f8b02e
LP
3008 }
3009
9184ca48 3010 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
b2f8b02e
LP
3011 return 0;
3012}
3013
4ad49000
LP
3014int config_parse_memory_limit(
3015 const char *unit,
3016 const char *filename,
3017 unsigned line,
3018 const char *section,
71a61510 3019 unsigned section_line,
4ad49000
LP
3020 const char *lvalue,
3021 int ltype,
3022 const char *rvalue,
3023 void *data,
3024 void *userdata) {
3025
3026 CGroupContext *c = data;
da4d897e 3027 uint64_t bytes = CGROUP_LIMIT_MAX;
4ad49000
LP
3028 int r;
3029
e57c9ce1 3030 if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
875ae566
LP
3031
3032 r = parse_percent(rvalue);
3033 if (r < 0) {
3034 r = parse_size(rvalue, 1024, &bytes);
3035 if (r < 0) {
3036 log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
3037 return 0;
3038 }
3039 } else
d8cf2ac7 3040 bytes = physical_memory_scale(r, 100U);
875ae566 3041
b3785cd5
LP
3042 if (bytes <= 0 || bytes >= UINT64_MAX) {
3043 log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range. Ignoring.", rvalue);
da4d897e
TH
3044 return 0;
3045 }
4ad49000
LP
3046 }
3047
da4d897e
TH
3048 if (streq(lvalue, "MemoryLow"))
3049 c->memory_low = bytes;
3050 else if (streq(lvalue, "MemoryHigh"))
3051 c->memory_high = bytes;
3052 else if (streq(lvalue, "MemoryMax"))
3053 c->memory_max = bytes;
96e131ea
WC
3054 else if (streq(lvalue, "MemorySwapMax"))
3055 c->memory_swap_max = bytes;
3056 else if (streq(lvalue, "MemoryLimit"))
da4d897e 3057 c->memory_limit = bytes;
96e131ea
WC
3058 else
3059 return -EINVAL;
4ad49000 3060
4ad49000
LP
3061 return 0;
3062}
3063
03a7b521
LP
3064int config_parse_tasks_max(
3065 const char *unit,
3066 const char *filename,
3067 unsigned line,
3068 const char *section,
3069 unsigned section_line,
3070 const char *lvalue,
3071 int ltype,
3072 const char *rvalue,
3073 void *data,
3074 void *userdata) {
3075
f5058264
TH
3076 uint64_t *tasks_max = data, v;
3077 Unit *u = userdata;
03a7b521
LP
3078 int r;
3079
f5058264
TH
3080 if (isempty(rvalue)) {
3081 *tasks_max = u->manager->default_tasks_max;
3082 return 0;
3083 }
3084
3085 if (streq(rvalue, "infinity")) {
3086 *tasks_max = CGROUP_LIMIT_MAX;
03a7b521
LP
3087 return 0;
3088 }
3089
83f8e808
LP
3090 r = parse_percent(rvalue);
3091 if (r < 0) {
f5058264 3092 r = safe_atou64(rvalue, &v);
83f8e808
LP
3093 if (r < 0) {
3094 log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
3095 return 0;
3096 }
3097 } else
f5058264 3098 v = system_tasks_max_scale(r, 100U);
83f8e808 3099
f5058264 3100 if (v <= 0 || v >= UINT64_MAX) {
83f8e808 3101 log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue);
03a7b521
LP
3102 return 0;
3103 }
3104
f5058264 3105 *tasks_max = v;
03a7b521
LP
3106 return 0;
3107}
3108
4ad49000
LP
3109int config_parse_device_allow(
3110 const char *unit,
3111 const char *filename,
3112 unsigned line,
3113 const char *section,
71a61510 3114 unsigned section_line,
4ad49000
LP
3115 const char *lvalue,
3116 int ltype,
3117 const char *rvalue,
3118 void *data,
3119 void *userdata) {
3120
1116e14c 3121 _cleanup_free_ char *path = NULL, *t = NULL;
4ad49000
LP
3122 CGroupContext *c = data;
3123 CGroupDeviceAllow *a;
1116e14c 3124 const char *m = NULL;
4ad49000 3125 size_t n;
1116e14c 3126 int r;
4ad49000
LP
3127
3128 if (isempty(rvalue)) {
3129 while (c->device_allow)
3130 cgroup_context_free_device_allow(c, c->device_allow);
3131
3132 return 0;
3133 }
3134
1116e14c
NBS
3135 r = unit_full_printf(userdata, rvalue, &t);
3136 if(r < 0) {
3137 log_syntax(unit, LOG_WARNING, filename, line, r,
3138 "Failed to resolve specifiers in %s, ignoring: %m",
3139 rvalue);
3140 }
3141
3142 n = strcspn(t, WHITESPACE);
3143
3144 path = strndup(t, n);
4ad49000
LP
3145 if (!path)
3146 return log_oom();
3147
3ccb8862 3148 if (!is_deviceallow_pattern(path)) {
12ca818f 3149 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
3150 return 0;
3151 }
3152
1116e14c 3153 m = t + n + strspn(t + n, WHITESPACE);
4ad49000
LP
3154 if (isempty(m))
3155 m = "rwm";
3156
3157 if (!in_charset(m, "rwm")) {
12ca818f 3158 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
4ad49000
LP
3159 return 0;
3160 }
3161
3162 a = new0(CGroupDeviceAllow, 1);
3163 if (!a)
3164 return log_oom();
3165
3166 a->path = path;
3167 path = NULL;
3168 a->r = !!strchr(m, 'r');
3169 a->w = !!strchr(m, 'w');
3170 a->m = !!strchr(m, 'm');
3171
71fda00f 3172 LIST_PREPEND(device_allow, c->device_allow, a);
4ad49000
LP
3173 return 0;
3174}
3175
13c31542
TH
3176int config_parse_io_weight(
3177 const char *unit,
3178 const char *filename,
3179 unsigned line,
3180 const char *section,
3181 unsigned section_line,
3182 const char *lvalue,
3183 int ltype,
3184 const char *rvalue,
3185 void *data,
3186 void *userdata) {
3187
3188 uint64_t *weight = data;
3189 int r;
3190
3191 assert(filename);
3192 assert(lvalue);
3193 assert(rvalue);
3194
3195 r = cg_weight_parse(rvalue, weight);
3196 if (r < 0) {
3197 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue);
3198 return 0;
3199 }
3200
3201 return 0;
3202}
3203
3204int config_parse_io_device_weight(
3205 const char *unit,
3206 const char *filename,
3207 unsigned line,
3208 const char *section,
3209 unsigned section_line,
3210 const char *lvalue,
3211 int ltype,
3212 const char *rvalue,
3213 void *data,
3214 void *userdata) {
3215
3216 _cleanup_free_ char *path = NULL;
3217 CGroupIODeviceWeight *w;
3218 CGroupContext *c = data;
3219 const char *weight;
3220 uint64_t u;
3221 size_t n;
3222 int r;
3223
3224 assert(filename);
3225 assert(lvalue);
3226 assert(rvalue);
3227
3228 if (isempty(rvalue)) {
3229 while (c->io_device_weights)
3230 cgroup_context_free_io_device_weight(c, c->io_device_weights);
3231
3232 return 0;
3233 }
3234
3235 n = strcspn(rvalue, WHITESPACE);
3236 weight = rvalue + n;
3237 weight += strspn(weight, WHITESPACE);
3238
3239 if (isempty(weight)) {
3240 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
3241 return 0;
3242 }
3243
3244 path = strndup(rvalue, n);
3245 if (!path)
3246 return log_oom();
3247
3248 if (!path_startswith(path, "/dev")) {
3249 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3250 return 0;
3251 }
3252
3253 r = cg_weight_parse(weight, &u);
3254 if (r < 0) {
3255 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight);
3256 return 0;
3257 }
3258
3259 assert(u != CGROUP_WEIGHT_INVALID);
3260
3261 w = new0(CGroupIODeviceWeight, 1);
3262 if (!w)
3263 return log_oom();
3264
3265 w->path = path;
3266 path = NULL;
3267
3268 w->weight = u;
3269
3270 LIST_PREPEND(device_weights, c->io_device_weights, w);
3271 return 0;
3272}
3273
3274int config_parse_io_limit(
3275 const char *unit,
3276 const char *filename,
3277 unsigned line,
3278 const char *section,
3279 unsigned section_line,
3280 const char *lvalue,
3281 int ltype,
3282 const char *rvalue,
3283 void *data,
3284 void *userdata) {
3285
3286 _cleanup_free_ char *path = NULL;
3287 CGroupIODeviceLimit *l = NULL, *t;
3288 CGroupContext *c = data;
9be57249 3289 CGroupIOLimitType type;
13c31542
TH
3290 const char *limit;
3291 uint64_t num;
13c31542
TH
3292 size_t n;
3293 int r;
3294
3295 assert(filename);
3296 assert(lvalue);
3297 assert(rvalue);
3298
9be57249
TH
3299 type = cgroup_io_limit_type_from_string(lvalue);
3300 assert(type >= 0);
13c31542
TH
3301
3302 if (isempty(rvalue)) {
3303 LIST_FOREACH(device_limits, l, c->io_device_limits)
9be57249 3304 l->limits[type] = cgroup_io_limit_defaults[type];
13c31542
TH
3305 return 0;
3306 }
3307
3308 n = strcspn(rvalue, WHITESPACE);
3309 limit = rvalue + n;
3310 limit += strspn(limit, WHITESPACE);
3311
3312 if (!*limit) {
3313 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3314 return 0;
3315 }
3316
3317 path = strndup(rvalue, n);
3318 if (!path)
3319 return log_oom();
3320
3321 if (!path_startswith(path, "/dev")) {
3322 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3323 return 0;
3324 }
3325
e57c9ce1 3326 if (streq("infinity", limit)) {
13c31542
TH
3327 num = CGROUP_LIMIT_MAX;
3328 } else {
3329 r = parse_size(limit, 1000, &num);
3330 if (r < 0 || num <= 0) {
3331 log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue);
3332 return 0;
3333 }
3334 }
3335
3336 LIST_FOREACH(device_limits, t, c->io_device_limits) {
3337 if (path_equal(path, t->path)) {
3338 l = t;
3339 break;
3340 }
3341 }
3342
3343 if (!l) {
9be57249
TH
3344 CGroupIOLimitType ttype;
3345
13c31542
TH
3346 l = new0(CGroupIODeviceLimit, 1);
3347 if (!l)
3348 return log_oom();
3349
3350 l->path = path;
3351 path = NULL;
9be57249
TH
3352 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
3353 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
13c31542
TH
3354
3355 LIST_PREPEND(device_limits, c->io_device_limits, l);
3356 }
3357
9be57249 3358 l->limits[type] = num;
13c31542
TH
3359
3360 return 0;
3361}
3362
4ad49000
LP
3363int config_parse_blockio_weight(
3364 const char *unit,
3365 const char *filename,
3366 unsigned line,
3367 const char *section,
71a61510 3368 unsigned section_line,
4ad49000
LP
3369 const char *lvalue,
3370 int ltype,
3371 const char *rvalue,
3372 void *data,
3373 void *userdata) {
3374
d53d9474 3375 uint64_t *weight = data;
95ae05c0
WC
3376 int r;
3377
3378 assert(filename);
3379 assert(lvalue);
3380 assert(rvalue);
3381
d53d9474
LP
3382 r = cg_blkio_weight_parse(rvalue, weight);
3383 if (r < 0) {
3384 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
3385 return 0;
3386 }
3387
8e7076ca
LP
3388 return 0;
3389}
3390
3391int config_parse_blockio_device_weight(
3392 const char *unit,
3393 const char *filename,
3394 unsigned line,
3395 const char *section,
71a61510 3396 unsigned section_line,
8e7076ca
LP
3397 const char *lvalue,
3398 int ltype,
3399 const char *rvalue,
3400 void *data,
3401 void *userdata) {
3402
4ad49000 3403 _cleanup_free_ char *path = NULL;
8e7076ca 3404 CGroupBlockIODeviceWeight *w;
4ad49000 3405 CGroupContext *c = data;
4ad49000 3406 const char *weight;
d53d9474 3407 uint64_t u;
4ad49000
LP
3408 size_t n;
3409 int r;
3410
3411 assert(filename);
3412 assert(lvalue);
3413 assert(rvalue);
3414
3415 if (isempty(rvalue)) {
4ad49000
LP
3416 while (c->blockio_device_weights)
3417 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
3418
3419 return 0;
3420 }
3421
3422 n = strcspn(rvalue, WHITESPACE);
3423 weight = rvalue + n;
d53d9474
LP
3424 weight += strspn(weight, WHITESPACE);
3425
3426 if (isempty(weight)) {
12ca818f 3427 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
8e7076ca
LP
3428 return 0;
3429 }
4ad49000 3430
8e7076ca
LP
3431 path = strndup(rvalue, n);
3432 if (!path)
3433 return log_oom();
4ad49000 3434
8e7076ca 3435 if (!path_startswith(path, "/dev")) {
12ca818f 3436 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
8e7076ca
LP
3437 return 0;
3438 }
4ad49000 3439
d53d9474
LP
3440 r = cg_blkio_weight_parse(weight, &u);
3441 if (r < 0) {
3442 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
4ad49000
LP
3443 return 0;
3444 }
3445
d53d9474
LP
3446 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
3447
8e7076ca
LP
3448 w = new0(CGroupBlockIODeviceWeight, 1);
3449 if (!w)
3450 return log_oom();
4ad49000 3451
8e7076ca
LP
3452 w->path = path;
3453 path = NULL;
4ad49000 3454
d53d9474 3455 w->weight = u;
4ad49000 3456
71fda00f 3457 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
3458 return 0;
3459}
3460
3461int config_parse_blockio_bandwidth(
3462 const char *unit,
3463 const char *filename,
3464 unsigned line,
3465 const char *section,
71a61510 3466 unsigned section_line,
4ad49000
LP
3467 const char *lvalue,
3468 int ltype,
3469 const char *rvalue,
3470 void *data,
3471 void *userdata) {
3472
3473 _cleanup_free_ char *path = NULL;
979d0311 3474 CGroupBlockIODeviceBandwidth *b = NULL, *t;
4ad49000
LP
3475 CGroupContext *c = data;
3476 const char *bandwidth;
59f448cf 3477 uint64_t bytes;
47c0980d 3478 bool read;
4ad49000
LP
3479 size_t n;
3480 int r;
3481
3482 assert(filename);
3483 assert(lvalue);
3484 assert(rvalue);
3485
47c0980d
G
3486 read = streq("BlockIOReadBandwidth", lvalue);
3487
4ad49000 3488 if (isempty(rvalue)) {
979d0311
TH
3489 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
3490 b->rbps = CGROUP_LIMIT_MAX;
3491 b->wbps = CGROUP_LIMIT_MAX;
3492 }
4ad49000
LP
3493 return 0;
3494 }
3495
3496 n = strcspn(rvalue, WHITESPACE);
3497 bandwidth = rvalue + n;
3498 bandwidth += strspn(bandwidth, WHITESPACE);
3499
3500 if (!*bandwidth) {
12ca818f 3501 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
4ad49000
LP
3502 return 0;
3503 }
3504
3505 path = strndup(rvalue, n);
3506 if (!path)
3507 return log_oom();
3508
3509 if (!path_startswith(path, "/dev")) {
12ca818f 3510 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
3511 return 0;
3512 }
3513
5556b5fe 3514 r = parse_size(bandwidth, 1000, &bytes);
4ad49000 3515 if (r < 0 || bytes <= 0) {
12ca818f 3516 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
3517 return 0;
3518 }
3519
979d0311
TH
3520 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
3521 if (path_equal(path, t->path)) {
3522 b = t;
3523 break;
3524 }
3525 }
4ad49000 3526
979d0311
TH
3527 if (!t) {
3528 b = new0(CGroupBlockIODeviceBandwidth, 1);
3529 if (!b)
3530 return log_oom();
3531
3532 b->path = path;
3533 path = NULL;
3534 b->rbps = CGROUP_LIMIT_MAX;
3535 b->wbps = CGROUP_LIMIT_MAX;
3536
3537 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
3538 }
4ad49000 3539
979d0311
TH
3540 if (read)
3541 b->rbps = bytes;
3542 else
3543 b->wbps = bytes;
4ad49000
LP
3544
3545 return 0;
3546}
3547
d420282b
LP
3548DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
3549
3550int config_parse_job_mode_isolate(
3551 const char *unit,
3552 const char *filename,
3553 unsigned line,
3554 const char *section,
3555 unsigned section_line,
3556 const char *lvalue,
3557 int ltype,
3558 const char *rvalue,
3559 void *data,
3560 void *userdata) {
3561
3562 JobMode *m = data;
3563 int r;
3564
3565 assert(filename);
3566 assert(lvalue);
3567 assert(rvalue);
3568
3569 r = parse_boolean(rvalue);
3570 if (r < 0) {
12ca818f 3571 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
d420282b
LP
3572 return 0;
3573 }
3574
3575 *m = r ? JOB_ISOLATE : JOB_REPLACE;
3576 return 0;
3577}
3578
53f47dfc
YW
3579DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
3580
3536f49e 3581int config_parse_exec_directories(
e66cf1a3
LP
3582 const char *unit,
3583 const char *filename,
3584 unsigned line,
3585 const char *section,
3586 unsigned section_line,
3587 const char *lvalue,
3588 int ltype,
3589 const char *rvalue,
3590 void *data,
3591 void *userdata) {
3592
a2a5291b 3593 char***rt = data;
9b5864d9 3594 Unit *u = userdata;
035fe294 3595 const char *p;
e66cf1a3
LP
3596 int r;
3597
3598 assert(filename);
3599 assert(lvalue);
3600 assert(rvalue);
3601 assert(data);
3602
3603 if (isempty(rvalue)) {
3604 /* Empty assignment resets the list */
6796073e 3605 *rt = strv_free(*rt);
e66cf1a3
LP
3606 return 0;
3607 }
3608
035fe294
ZJS
3609 for (p = rvalue;;) {
3610 _cleanup_free_ char *word = NULL, *k = NULL;
e66cf1a3 3611
035fe294
ZJS
3612 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
3613 if (r == 0)
3614 return 0;
3615 if (r == -ENOMEM)
e66cf1a3 3616 return log_oom();
035fe294
ZJS
3617 if (r < 0) {
3618 log_syntax(unit, LOG_WARNING, filename, line, r,
3619 "Invalid syntax, ignoring: %s", rvalue);
3620 return 0;
3621 }
e66cf1a3 3622
18913df9 3623 r = unit_full_printf(u, word, &k);
9b5864d9 3624 if (r < 0) {
035fe294
ZJS
3625 log_syntax(unit, LOG_ERR, filename, line, r,
3626 "Failed to resolve specifiers in \"%s\", ignoring: %m", word);
9b5864d9
MG
3627 continue;
3628 }
3629
23a7448e 3630 if (!path_is_safe(k) || path_is_absolute(k)) {
035fe294 3631 log_syntax(unit, LOG_ERR, filename, line, 0,
3536f49e 3632 "%s is not valid, ignoring assignment: %s", lvalue, rvalue);
e66cf1a3
LP
3633 continue;
3634 }
3635
035fe294 3636 r = strv_push(rt, k);
e66cf1a3
LP
3637 if (r < 0)
3638 return log_oom();
035fe294 3639 k = NULL;
e66cf1a3 3640 }
e66cf1a3
LP
3641}
3642
3af00fb8
LP
3643int config_parse_set_status(
3644 const char *unit,
3645 const char *filename,
3646 unsigned line,
3647 const char *section,
3648 unsigned section_line,
3649 const char *lvalue,
3650 int ltype,
3651 const char *rvalue,
3652 void *data,
3653 void *userdata) {
3654
3af00fb8 3655 size_t l;
a2a5291b 3656 const char *word, *state;
3af00fb8
LP
3657 int r;
3658 ExitStatusSet *status_set = data;
3659
3660 assert(filename);
3661 assert(lvalue);
3662 assert(rvalue);
3663 assert(data);
3664
3e2d435b 3665 /* Empty assignment resets the list */
3af00fb8 3666 if (isempty(rvalue)) {
3e2d435b 3667 exit_status_set_free(status_set);
3af00fb8
LP
3668 return 0;
3669 }
3670
a2a5291b 3671 FOREACH_WORD(word, l, rvalue, state) {
3af00fb8
LP
3672 _cleanup_free_ char *temp;
3673 int val;
61593865 3674 Set **set;
3af00fb8 3675
a2a5291b 3676 temp = strndup(word, l);
3af00fb8
LP
3677 if (!temp)
3678 return log_oom();
3679
3680 r = safe_atoi(temp, &val);
3681 if (r < 0) {
3682 val = signal_from_string_try_harder(temp);
3683
1e2fd62d 3684 if (val <= 0) {
12ca818f 3685 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
61593865 3686 continue;
3af00fb8 3687 }
61593865 3688 set = &status_set->signal;
3af00fb8 3689 } else {
1e2fd62d 3690 if (val < 0 || val > 255) {
12ca818f 3691 log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
1e2fd62d 3692 continue;
3af00fb8 3693 }
61593865 3694 set = &status_set->status;
3af00fb8 3695 }
1e2fd62d 3696
61593865 3697 r = set_ensure_allocated(set, NULL);
1e2fd62d
ZJS
3698 if (r < 0)
3699 return log_oom();
3700
61593865 3701 r = set_put(*set, INT_TO_PTR(val));
1e2fd62d 3702 if (r < 0) {
12ca818f 3703 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
1e2fd62d
ZJS
3704 return r;
3705 }
3af00fb8 3706 }
b2fadec6 3707 if (!isempty(state))
12ca818f 3708 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3af00fb8
LP
3709
3710 return 0;
3711}
3712
94828d2d
LP
3713int config_parse_namespace_path_strv(
3714 const char *unit,
3715 const char *filename,
3716 unsigned line,
3717 const char *section,
3718 unsigned section_line,
3719 const char *lvalue,
3720 int ltype,
3721 const char *rvalue,
3722 void *data,
3723 void *userdata) {
3724
7b07e993 3725 Unit *u = userdata;
a2a5291b 3726 char*** sv = data;
727f76d7 3727 const char *cur;
94828d2d
LP
3728 int r;
3729
3730 assert(filename);
3731 assert(lvalue);
3732 assert(rvalue);
3733 assert(data);
3734
3735 if (isempty(rvalue)) {
3736 /* Empty assignment resets the list */
6796073e 3737 *sv = strv_free(*sv);
94828d2d
LP
3738 return 0;
3739 }
3740
7b07e993 3741 cur = rvalue;
727f76d7 3742 for (;;) {
7b07e993 3743 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
20b7a007
LP
3744 const char *w;
3745 bool ignore_enoent = false, shall_prefix = false;
94828d2d 3746
727f76d7 3747 r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
0293a7a8
EV
3748 if (r == 0)
3749 break;
3750 if (r == -ENOMEM)
3751 return log_oom();
727f76d7 3752 if (r < 0) {
7b07e993 3753 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
727f76d7
EV
3754 return 0;
3755 }
94828d2d 3756
727f76d7
EV
3757 if (!utf8_is_valid(word)) {
3758 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
94828d2d
LP
3759 continue;
3760 }
3761
20b7a007
LP
3762 w = word;
3763 if (startswith(w, "-")) {
3764 ignore_enoent = true;
3765 w++;
3766 }
3767 if (startswith(w, "+")) {
3768 shall_prefix = true;
3769 w++;
3770 }
7b07e993 3771
20b7a007 3772 r = unit_full_printf(u, w, &resolved);
7b07e993
LP
3773 if (r < 0) {
3774 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word);
94828d2d
LP
3775 continue;
3776 }
3777
7b07e993
LP
3778 if (!path_is_absolute(resolved)) {
3779 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", resolved);
3780 continue;
3781 }
3782
3783 path_kill_slashes(resolved);
94828d2d 3784
20b7a007
LP
3785 joined = strjoin(ignore_enoent ? "-" : "",
3786 shall_prefix ? "+" : "",
3787 resolved);
7b07e993
LP
3788
3789 r = strv_push(sv, joined);
94828d2d
LP
3790 if (r < 0)
3791 return log_oom();
3792
7b07e993 3793 joined = NULL;
94828d2d
LP
3794 }
3795
3796 return 0;
3797}
3798
d2d6c096
LP
3799int config_parse_bind_paths(
3800 const char *unit,
3801 const char *filename,
3802 unsigned line,
3803 const char *section,
3804 unsigned section_line,
3805 const char *lvalue,
3806 int ltype,
3807 const char *rvalue,
3808 void *data,
3809 void *userdata) {
3810
3811 ExecContext *c = data;
42d43f21 3812 Unit *u = userdata;
d2d6c096
LP
3813 const char *p;
3814 int r;
3815
3816 assert(filename);
3817 assert(lvalue);
3818 assert(rvalue);
3819 assert(data);
3820
3821 if (isempty(rvalue)) {
3822 /* Empty assignment resets the list */
3823 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
3824 c->bind_mounts = NULL;
3825 c->n_bind_mounts = 0;
3826 return 0;
3827 }
3828
3829 p = rvalue;
3830 for (;;) {
3831 _cleanup_free_ char *source = NULL, *destination = NULL;
42d43f21 3832 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
d2d6c096
LP
3833 char *s = NULL, *d = NULL;
3834 bool rbind = true, ignore_enoent = false;
3835
3836 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
3837 if (r == 0)
3838 break;
3839 if (r == -ENOMEM)
3840 return log_oom();
3841 if (r < 0) {
3842 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
3843 return 0;
3844 }
3845
42d43f21
DC
3846 r = unit_full_printf(u, source, &sresolved);
3847 if (r < 0) {
3848 log_syntax(unit, LOG_ERR, filename, line, r,
3849 "Failed to resolved specifiers in \"%s\", ignoring: %m", source);
3850 return 0;
3851 }
3852
3853 s = sresolved;
d2d6c096
LP
3854 if (s[0] == '-') {
3855 ignore_enoent = true;
3856 s++;
3857 }
3858
3859 if (!utf8_is_valid(s)) {
3860 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, s);
3861 return 0;
3862 }
3863 if (!path_is_absolute(s)) {
3864 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute source path, ignoring: %s", s);
3865 return 0;
3866 }
3867
3868 path_kill_slashes(s);
3869
3870 /* Optionally, the destination is specified. */
3871 if (p && p[-1] == ':') {
3872 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
3873 if (r == -ENOMEM)
3874 return log_oom();
3875 if (r < 0) {
3876 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
3877 return 0;
3878 }
3879 if (r == 0) {
3880 log_syntax(unit, LOG_ERR, filename, line, 0, "Missing argument after ':': %s", rvalue);
3881 return 0;
3882 }
3883
42d43f21
DC
3884 r = unit_full_printf(u, destination, &dresolved);
3885 if (r < 0) {
3886 log_syntax(unit, LOG_ERR, filename, line, r,
3887 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination);
3888 return 0;
3889 }
3890
3891 if (!utf8_is_valid(dresolved)) {
3892 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, dresolved);
d2d6c096
LP
3893 return 0;
3894 }
42d43f21
DC
3895 if (!path_is_absolute(dresolved)) {
3896 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute destination path, ignoring: %s", dresolved);
d2d6c096
LP
3897 return 0;
3898 }
3899
42d43f21 3900 d = path_kill_slashes(dresolved);
d2d6c096
LP
3901
3902 /* Optionally, there's also a short option string specified */
3903 if (p && p[-1] == ':') {
3904 _cleanup_free_ char *options = NULL;
3905
3906 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
3907 if (r == -ENOMEM)
3908 return log_oom();
3909 if (r < 0) {
3910 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
3911 return 0;
3912 }
3913
3914 if (isempty(options) || streq(options, "rbind"))
3915 rbind = true;
3916 else if (streq(options, "norbind"))
3917 rbind = false;
3918 else {
3919 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
3920 return 0;
3921 }
3922 }
3923 } else
3924 d = s;
3925
3926 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
3927 &(BindMount) {
3928 .source = s,
3929 .destination = d,
3930 .read_only = !!strstr(lvalue, "ReadOnly"),
3931 .recursive = rbind,
3932 .ignore_enoent = ignore_enoent,
3933 });
3934 if (r < 0)
3935 return log_oom();
3936 }
3937
3938 return 0;
3939}
3940
f1721625 3941int config_parse_no_new_privileges(
760b9d7c
LP
3942 const char* unit,
3943 const char *filename,
3944 unsigned line,
3945 const char *section,
3946 unsigned section_line,
3947 const char *lvalue,
3948 int ltype,
3949 const char *rvalue,
3950 void *data,
3951 void *userdata) {
3952
3953 ExecContext *c = data;
3954 int k;
3955
3956 assert(filename);
3957 assert(lvalue);
3958 assert(rvalue);
3959 assert(data);
3960
3961 k = parse_boolean(rvalue);
3962 if (k < 0) {
12ca818f 3963 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
760b9d7c
LP
3964 return 0;
3965 }
3966
9b232d32 3967 c->no_new_privileges = k;
760b9d7c
LP
3968
3969 return 0;
3970}
3971
1b8689f9 3972int config_parse_protect_home(
417116f2
LP
3973 const char* unit,
3974 const char *filename,
3975 unsigned line,
3976 const char *section,
3977 unsigned section_line,
3978 const char *lvalue,
3979 int ltype,
3980 const char *rvalue,
3981 void *data,
3982 void *userdata) {
3983
3984 ExecContext *c = data;
3985 int k;
3986
3987 assert(filename);
3988 assert(lvalue);
3989 assert(rvalue);
3990 assert(data);
3991
3992 /* Our enum shall be a superset of booleans, hence first try
61233823 3993 * to parse as boolean, and then as enum */
417116f2
LP
3994
3995 k = parse_boolean(rvalue);
3996 if (k > 0)
1b8689f9 3997 c->protect_home = PROTECT_HOME_YES;
417116f2 3998 else if (k == 0)
1b8689f9 3999 c->protect_home = PROTECT_HOME_NO;
417116f2 4000 else {
1b8689f9 4001 ProtectHome h;
417116f2 4002
1b8689f9 4003 h = protect_home_from_string(rvalue);
9ed794a3 4004 if (h < 0) {
12ca818f 4005 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
417116f2
LP
4006 return 0;
4007 }
4008
1b8689f9
LP
4009 c->protect_home = h;
4010 }
4011
4012 return 0;
4013}
4014
4015int config_parse_protect_system(
4016 const char* unit,
4017 const char *filename,
4018 unsigned line,
4019 const char *section,
4020 unsigned section_line,
4021 const char *lvalue,
4022 int ltype,
4023 const char *rvalue,
4024 void *data,
4025 void *userdata) {
4026
4027 ExecContext *c = data;
4028 int k;
4029
4030 assert(filename);
4031 assert(lvalue);
4032 assert(rvalue);
4033 assert(data);
4034
4035 /* Our enum shall be a superset of booleans, hence first try
61233823 4036 * to parse as boolean, and then as enum */
1b8689f9
LP
4037
4038 k = parse_boolean(rvalue);
4039 if (k > 0)
4040 c->protect_system = PROTECT_SYSTEM_YES;
4041 else if (k == 0)
4042 c->protect_system = PROTECT_SYSTEM_NO;
4043 else {
4044 ProtectSystem s;
4045
4046 s = protect_system_from_string(rvalue);
9ed794a3 4047 if (s < 0) {
12ca818f 4048 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
1b8689f9
LP
4049 return 0;
4050 }
4051
4052 c->protect_system = s;
417116f2
LP
4053 }
4054
4055 return 0;
4056}
4057
071830ff 4058#define FOLLOW_MAX 8
87f0e418 4059
9e2f7c11 4060static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
a837f088 4061 char *id = NULL;
0301abf4 4062 unsigned c = 0;
87f0e418
LP
4063 int fd, r;
4064 FILE *f;
87f0e418
LP
4065
4066 assert(filename);
4067 assert(*filename);
4068 assert(_f);
4069 assert(names);
4070
0301abf4
LP
4071 /* This will update the filename pointer if the loaded file is
4072 * reached by a symlink. The old string will be freed. */
87f0e418 4073
0301abf4 4074 for (;;) {
2c7108c4 4075 char *target, *name;
87f0e418 4076
0301abf4
LP
4077 if (c++ >= FOLLOW_MAX)
4078 return -ELOOP;
4079
b08d03ff
LP
4080 path_kill_slashes(*filename);
4081
87f0e418 4082 /* Add the file name we are currently looking at to
8f05424d
LP
4083 * the names of this unit, but only if it is a valid
4084 * unit name. */
2b6bf07d 4085 name = basename(*filename);
7410616c 4086 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
8f05424d 4087
15e11d81
LP
4088 id = set_get(names, name);
4089 if (!id) {
4090 id = strdup(name);
4091 if (!id)
8f05424d 4092 return -ENOMEM;
87f0e418 4093
ef42202a
ZJS
4094 r = set_consume(names, id);
4095 if (r < 0)
8f05424d 4096 return r;
87f0e418 4097 }
87f0e418
LP
4098 }
4099
0301abf4 4100 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
4101 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
4102 if (fd >= 0)
87f0e418
LP
4103 break;
4104
0301abf4
LP
4105 if (errno != ELOOP)
4106 return -errno;
4107
87f0e418 4108 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
4109 r = readlink_and_make_absolute(*filename, &target);
4110 if (r < 0)
0301abf4 4111 return r;
87f0e418 4112
0301abf4 4113 free(*filename);
2c7108c4 4114 *filename = target;
87f0e418
LP
4115 }
4116
9946996c
LP
4117 f = fdopen(fd, "re");
4118 if (!f) {
03e334a1 4119 safe_close(fd);
d4ad27a1 4120 return -errno;
87f0e418
LP
4121 }
4122
4123 *_f = f;
9e2f7c11 4124 *_final = id;
a837f088 4125
0301abf4 4126 return 0;
87f0e418
LP
4127}
4128
23a177ef
LP
4129static int merge_by_names(Unit **u, Set *names, const char *id) {
4130 char *k;
4131 int r;
4132
4133 assert(u);
4134 assert(*u);
4135 assert(names);
4136
4137 /* Let's try to add in all symlink names we found */
4138 while ((k = set_steal_first(names))) {
4139
4140 /* First try to merge in the other name into our
4141 * unit */
9946996c
LP
4142 r = unit_merge_by_name(*u, k);
4143 if (r < 0) {
23a177ef
LP
4144 Unit *other;
4145
4146 /* Hmm, we couldn't merge the other unit into
4147 * ours? Then let's try it the other way
4148 * round */
4149
7aad67e7
MS
4150 /* If the symlink name we are looking at is unit template, then
4151 we must search for instance of this template */
9d3e3406 4152 if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) {
7aad67e7
MS
4153 _cleanup_free_ char *instance = NULL;
4154
4155 r = unit_name_replace_instance(k, (*u)->instance, &instance);
4156 if (r < 0)
4157 return r;
4158
4159 other = manager_get_unit((*u)->manager, instance);
4160 } else
4161 other = manager_get_unit((*u)->manager, k);
4162
23a177ef
LP
4163 free(k);
4164
9946996c
LP
4165 if (other) {
4166 r = unit_merge(other, *u);
4167 if (r >= 0) {
23a177ef
LP
4168 *u = other;
4169 return merge_by_names(u, names, NULL);
4170 }
9946996c 4171 }
23a177ef
LP
4172
4173 return r;
4174 }
4175
4176 if (id == k)
4177 unit_choose_id(*u, id);
4178
4179 free(k);
4180 }
4181
4182 return 0;
4183}
4184
e537352b 4185static int load_from_path(Unit *u, const char *path) {
e48614c4
ZJS
4186 _cleanup_set_free_free_ Set *symlink_names = NULL;
4187 _cleanup_fclose_ FILE *f = NULL;
4188 _cleanup_free_ char *filename = NULL;
4189 char *id = NULL;
23a177ef 4190 Unit *merged;
45fb0699 4191 struct stat st;
a837f088 4192 int r;
23a177ef
LP
4193
4194 assert(u);
e537352b 4195 assert(path);
3efd4195 4196
d5099efc 4197 symlink_names = set_new(&string_hash_ops);
f975e971 4198 if (!symlink_names)
87f0e418 4199 return -ENOMEM;
3efd4195 4200
036643a2
LP
4201 if (path_is_absolute(path)) {
4202
9946996c 4203 filename = strdup(path);
e48614c4
ZJS
4204 if (!filename)
4205 return -ENOMEM;
036643a2 4206
9946996c
LP
4207 r = open_follow(&filename, &f, symlink_names, &id);
4208 if (r < 0) {
97b11eed 4209 filename = mfree(filename);
036643a2 4210 if (r != -ENOENT)
e48614c4 4211 return r;
036643a2
LP
4212 }
4213
4214 } else {
4215 char **p;
4216
a3c4eb07 4217 STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
036643a2
LP
4218
4219 /* Instead of opening the path right away, we manually
4220 * follow all symlinks and add their name to our unit
4221 * name set while doing so */
9946996c 4222 filename = path_make_absolute(path, *p);
e48614c4
ZJS
4223 if (!filename)
4224 return -ENOMEM;
036643a2 4225
ac155bb8
MS
4226 if (u->manager->unit_path_cache &&
4227 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
4228 r = -ENOENT;
4229 else
4230 r = open_follow(&filename, &f, symlink_names, &id);
a837f088
LP
4231 if (r >= 0)
4232 break;
4233 filename = mfree(filename);
a1feacf7
ZJS
4234
4235 /* ENOENT means that the file is missing or is a dangling symlink.
4236 * ENOTDIR means that one of paths we expect to be is a directory
4237 * is not a directory, we should just ignore that.
4238 * EACCES means that the directory or file permissions are wrong.
4239 */
4240 if (r == -EACCES)
4241 log_debug_errno(r, "Cannot access \"%s\": %m", filename);
4242 else if (!IN_SET(r, -ENOENT, -ENOTDIR))
a837f088 4243 return r;
fe51822e 4244
a837f088
LP
4245 /* Empty the symlink names for the next run */
4246 set_clear_free(symlink_names);
036643a2
LP
4247 }
4248 }
034c6ed7 4249
e48614c4 4250 if (!filename)
8f05424d 4251 /* Hmm, no suitable file found? */
e48614c4 4252 return 0;
87f0e418 4253
8a993b61 4254 if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
a837f088
LP
4255 log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
4256 return -ELOOP;
4257 }
4258
23a177ef 4259 merged = u;
9946996c
LP
4260 r = merge_by_names(&merged, symlink_names, id);
4261 if (r < 0)
e48614c4 4262 return r;
87f0e418 4263
23a177ef 4264 if (merged != u) {
ac155bb8 4265 u->load_state = UNIT_MERGED;
e48614c4 4266 return 0;
034c6ed7
LP
4267 }
4268
e48614c4
ZJS
4269 if (fstat(fileno(f), &st) < 0)
4270 return -errno;
45fb0699 4271
3a8db9fe 4272 if (null_or_empty(&st)) {
ac155bb8 4273 u->load_state = UNIT_MASKED;
3a8db9fe
ZJS
4274 u->fragment_mtime = 0;
4275 } else {
c2756a68 4276 u->load_state = UNIT_LOADED;
3a8db9fe 4277 u->fragment_mtime = timespec_load(&st.st_mtim);
c2756a68 4278
00dc5d76 4279 /* Now, parse the file contents */
36f822c4
ZJS
4280 r = config_parse(u->id, filename, f,
4281 UNIT_VTABLE(u)->sections,
4282 config_item_perf_lookup, load_fragment_gperf_lookup,
4283 false, true, false, u);
f975e971 4284 if (r < 0)
e48614c4 4285 return r;
00dc5d76 4286 }
b08d03ff 4287
ac155bb8
MS
4288 free(u->fragment_path);
4289 u->fragment_path = filename;
0301abf4 4290 filename = NULL;
87f0e418 4291
1b64d026
LP
4292 if (u->source_path) {
4293 if (stat(u->source_path, &st) >= 0)
4294 u->source_mtime = timespec_load(&st.st_mtim);
4295 else
4296 u->source_mtime = 0;
4297 }
4298
e48614c4 4299 return 0;
0301abf4
LP
4300}
4301
e537352b 4302int unit_load_fragment(Unit *u) {
23a177ef 4303 int r;
294d81f1
LP
4304 Iterator i;
4305 const char *t;
0301abf4
LP
4306
4307 assert(u);
ac155bb8
MS
4308 assert(u->load_state == UNIT_STUB);
4309 assert(u->id);
23a177ef 4310
3f5e8115
LP
4311 if (u->transient) {
4312 u->load_state = UNIT_LOADED;
4313 return 0;
4314 }
4315
294d81f1
LP
4316 /* First, try to find the unit under its id. We always look
4317 * for unit files in the default directories, to make it easy
4318 * to override things by placing things in /etc/systemd/system */
9946996c
LP
4319 r = load_from_path(u, u->id);
4320 if (r < 0)
294d81f1
LP
4321 return r;
4322
4323 /* Try to find an alias we can load this with */
abc08d4d 4324 if (u->load_state == UNIT_STUB) {
ac155bb8 4325 SET_FOREACH(t, u->names, i) {
294d81f1 4326
ac155bb8 4327 if (t == u->id)
294d81f1
LP
4328 continue;
4329
9946996c
LP
4330 r = load_from_path(u, t);
4331 if (r < 0)
294d81f1
LP
4332 return r;
4333
ac155bb8 4334 if (u->load_state != UNIT_STUB)
294d81f1
LP
4335 break;
4336 }
abc08d4d 4337 }
23a177ef 4338
294d81f1 4339 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 4340 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 4341
9946996c
LP
4342 r = load_from_path(u, u->fragment_path);
4343 if (r < 0)
23a177ef 4344 return r;
0301abf4 4345
ece174c5 4346 if (u->load_state == UNIT_STUB)
6ccb1b44
LP
4347 /* Hmm, this didn't work? Then let's get rid
4348 * of the fragment path stored for us, so that
4349 * we don't point to an invalid location. */
a1e58e8e 4350 u->fragment_path = mfree(u->fragment_path);
6ccb1b44
LP
4351 }
4352
294d81f1 4353 /* Look for a template */
ac155bb8 4354 if (u->load_state == UNIT_STUB && u->instance) {
7410616c 4355 _cleanup_free_ char *k = NULL;
294d81f1 4356
7410616c
LP
4357 r = unit_name_template(u->id, &k);
4358 if (r < 0)
4359 return r;
294d81f1
LP
4360
4361 r = load_from_path(u, k);
bb28e684
ZJS
4362 if (r < 0) {
4363 if (r == -ENOEXEC)
4364 log_unit_notice(u, "Unit configuration has fatal error, unit will not be started.");
9e2f7c11 4365 return r;
bb28e684 4366 }
890f434c 4367
abc08d4d 4368 if (u->load_state == UNIT_STUB) {
ac155bb8 4369 SET_FOREACH(t, u->names, i) {
bc9fd78c 4370 _cleanup_free_ char *z = NULL;
87f0e418 4371
ac155bb8 4372 if (t == u->id)
23a177ef 4373 continue;
071830ff 4374
7410616c
LP
4375 r = unit_name_template(t, &z);
4376 if (r < 0)
4377 return r;
294d81f1 4378
bc9fd78c 4379 r = load_from_path(u, z);
294d81f1 4380 if (r < 0)
23a177ef 4381 return r;
890f434c 4382
ac155bb8 4383 if (u->load_state != UNIT_STUB)
23a177ef
LP
4384 break;
4385 }
abc08d4d 4386 }
071830ff
LP
4387 }
4388
23a177ef 4389 return 0;
3efd4195 4390}
e537352b
LP
4391
4392void unit_dump_config_items(FILE *f) {
f975e971
LP
4393 static const struct {
4394 const ConfigParserCallback callback;
4395 const char *rvalue;
4396 } table[] = {
7f8aa671 4397#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
17df7223
LP
4398 { config_parse_warn_compat, "NOTSUPPORTED" },
4399#endif
f975e971
LP
4400 { config_parse_int, "INTEGER" },
4401 { config_parse_unsigned, "UNSIGNED" },
5556b5fe 4402 { config_parse_iec_size, "SIZE" },
59f448cf 4403 { config_parse_iec_uint64, "SIZE" },
5556b5fe 4404 { config_parse_si_size, "SIZE" },
f975e971
LP
4405 { config_parse_bool, "BOOLEAN" },
4406 { config_parse_string, "STRING" },
4407 { config_parse_path, "PATH" },
4408 { config_parse_unit_path_printf, "PATH" },
4409 { config_parse_strv, "STRING [...]" },
4410 { config_parse_exec_nice, "NICE" },
4411 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
4412 { config_parse_exec_io_class, "IOCLASS" },
4413 { config_parse_exec_io_priority, "IOPRIORITY" },
4414 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
4415 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
4416 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
4417 { config_parse_mode, "MODE" },
4418 { config_parse_unit_env_file, "FILE" },
52c239d7
LB
4419 { config_parse_exec_output, "OUTPUT" },
4420 { config_parse_exec_input, "INPUT" },
ca37242e
LP
4421 { config_parse_log_facility, "FACILITY" },
4422 { config_parse_log_level, "LEVEL" },
f975e971 4423 { config_parse_exec_secure_bits, "SECUREBITS" },
a103496c 4424 { config_parse_capability_set, "BOUNDINGSET" },
f975e971 4425 { config_parse_limit, "LIMIT" },
f975e971 4426 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
4427 { config_parse_exec, "PATH [ARGUMENT [...]]" },
4428 { config_parse_service_type, "SERVICETYPE" },
4429 { config_parse_service_restart, "SERVICERESTART" },
4430#ifdef HAVE_SYSV_COMPAT
4431 { config_parse_sysv_priority, "SYSVPRIORITY" },
f975e971
LP
4432#endif
4433 { config_parse_kill_mode, "KILLMODE" },
f757855e 4434 { config_parse_signal, "SIGNAL" },
f975e971
LP
4435 { config_parse_socket_listen, "SOCKET [...]" },
4436 { config_parse_socket_bind, "SOCKETBIND" },
4437 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 4438 { config_parse_sec, "SECONDS" },
d88a251b 4439 { config_parse_nsec, "NANOSECONDS" },
94828d2d 4440 { config_parse_namespace_path_strv, "PATH [...]" },
d2d6c096 4441 { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
7c8fa05c 4442 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
4443 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
4444 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 4445 { config_parse_trigger_unit, "UNIT" },
f975e971 4446 { config_parse_timer, "TIMER" },
f975e971 4447 { config_parse_path_spec, "PATH" },
f975e971
LP
4448 { config_parse_notify_access, "ACCESS" },
4449 { config_parse_ip_tos, "TOS" },
4450 { config_parse_unit_condition_path, "CONDITION" },
4451 { config_parse_unit_condition_string, "CONDITION" },
4452 { config_parse_unit_condition_null, "CONDITION" },
a016b922 4453 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
4454 { config_parse_documentation, "URL" },
4455 { config_parse_service_timeout, "SECONDS" },
87a47f99 4456 { config_parse_emergency_action, "ACTION" },
7f0386f6
LP
4457 { config_parse_set_status, "STATUS" },
4458 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 4459 { config_parse_environ, "ENVIRON" },
c0467cf3 4460#ifdef HAVE_SECCOMP
17df7223 4461 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 4462 { config_parse_syscall_archs, "ARCHS" },
17df7223 4463 { config_parse_syscall_errno, "ERRNO" },
4298d0b5 4464 { config_parse_address_families, "FAMILIES" },
add00535 4465 { config_parse_restrict_namespaces, "NAMESPACES" },
c0467cf3 4466#endif
7f0386f6 4467 { config_parse_cpu_shares, "SHARES" },
66ebf6c0 4468 { config_parse_cpu_weight, "WEIGHT" },
7f0386f6
LP
4469 { config_parse_memory_limit, "LIMIT" },
4470 { config_parse_device_allow, "DEVICE" },
4471 { config_parse_device_policy, "POLICY" },
13c31542
TH
4472 { config_parse_io_limit, "LIMIT" },
4473 { config_parse_io_weight, "WEIGHT" },
4474 { config_parse_io_device_weight, "DEVICEWEIGHT" },
7f0386f6
LP
4475 { config_parse_blockio_bandwidth, "BANDWIDTH" },
4476 { config_parse_blockio_weight, "WEIGHT" },
4477 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
4478 { config_parse_long, "LONG" },
4479 { config_parse_socket_service, "SERVICE" },
6a6751fe
LP
4480#ifdef HAVE_SELINUX
4481 { config_parse_exec_selinux_context, "LABEL" },
4482#endif
4483 { config_parse_job_mode, "MODE" },
4484 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 4485 { config_parse_personality, "PERSONALITY" },
f975e971
LP
4486 };
4487
4488 const char *prev = NULL;
4489 const char *i;
4490
4491 assert(f);
e537352b 4492
f975e971
LP
4493 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
4494 const char *rvalue = "OTHER", *lvalue;
4495 unsigned j;
4496 size_t prefix_len;
4497 const char *dot;
4498 const ConfigPerfItem *p;
4499
4500 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
4501
4502 dot = strchr(i, '.');
4503 lvalue = dot ? dot + 1 : i;
4504 prefix_len = dot-i;
4505
4506 if (dot)
641906e9 4507 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
4508 if (prev)
4509 fputc('\n', f);
4510
4511 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
4512 }
4513
4514 for (j = 0; j < ELEMENTSOF(table); j++)
4515 if (p->parse == table[j].callback) {
4516 rvalue = table[j].rvalue;
4517 break;
4518 }
4519
4520 fprintf(f, "%s=%s\n", lvalue, rvalue);
4521 prev = i;
4522 }
e537352b 4523}