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