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