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