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