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