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