]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
Merge pull request #3329 from htejun/dbus-cgroup-fixes
[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
17df7223
LP
2399int config_parse_syscall_filter(
2400 const char *unit,
2401 const char *filename,
2402 unsigned line,
2403 const char *section,
2404 unsigned section_line,
2405 const char *lvalue,
2406 int ltype,
2407 const char *rvalue,
2408 void *data,
2409 void *userdata) {
2410
2411 static const char default_syscalls[] =
2412 "execve\0"
2413 "exit\0"
2414 "exit_group\0"
2415 "rt_sigreturn\0"
2416 "sigreturn\0";
2417
8351ceae
LP
2418 ExecContext *c = data;
2419 Unit *u = userdata;
b5fb3789 2420 bool invert = false;
a2a5291b 2421 const char *word, *state;
8351ceae 2422 size_t l;
17df7223 2423 int r;
8351ceae
LP
2424
2425 assert(filename);
2426 assert(lvalue);
2427 assert(rvalue);
2428 assert(u);
2429
74051b9b
LP
2430 if (isempty(rvalue)) {
2431 /* Empty assignment resets the list */
525d3cc7 2432 c->syscall_filter = set_free(c->syscall_filter);
17df7223 2433 c->syscall_whitelist = false;
74051b9b
LP
2434 return 0;
2435 }
2436
8351ceae
LP
2437 if (rvalue[0] == '~') {
2438 invert = true;
2439 rvalue++;
2440 }
2441
17df7223 2442 if (!c->syscall_filter) {
d5099efc 2443 c->syscall_filter = set_new(NULL);
17df7223
LP
2444 if (!c->syscall_filter)
2445 return log_oom();
2446
c0467cf3 2447 if (invert)
17df7223
LP
2448 /* Allow everything but the ones listed */
2449 c->syscall_whitelist = false;
c0467cf3 2450 else {
17df7223
LP
2451 const char *i;
2452
2453 /* Allow nothing but the ones listed */
2454 c->syscall_whitelist = true;
8351ceae 2455
17df7223
LP
2456 /* Accept default syscalls if we are on a whitelist */
2457 NULSTR_FOREACH(i, default_syscalls) {
2458 int id;
8351ceae 2459
17df7223 2460 id = seccomp_syscall_resolve_name(i);
c0467cf3
RC
2461 if (id < 0)
2462 continue;
8351ceae 2463
17df7223 2464 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
756c09e6 2465 if (r == 0)
17df7223
LP
2466 continue;
2467 if (r < 0)
2468 return log_oom();
c0467cf3
RC
2469 }
2470 }
8351ceae
LP
2471 }
2472
a2a5291b 2473 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
7fd1b19b 2474 _cleanup_free_ char *t = NULL;
17df7223 2475 int id;
8351ceae 2476
a2a5291b 2477 t = strndup(word, l);
8351ceae 2478 if (!t)
74051b9b 2479 return log_oom();
8351ceae 2480
c0467cf3 2481 id = seccomp_syscall_resolve_name(t);
8351ceae 2482 if (id < 0) {
12ca818f 2483 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
8351ceae
LP
2484 continue;
2485 }
2486
17df7223
LP
2487 /* If we previously wanted to forbid a syscall and now
2488 * we want to allow it, then remove it from the list
c0467cf3 2489 */
17df7223
LP
2490 if (!invert == c->syscall_whitelist) {
2491 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
756c09e6 2492 if (r == 0)
17df7223
LP
2493 continue;
2494 if (r < 0)
2495 return log_oom();
2496 } else
2497 set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
c0467cf3 2498 }
b2fadec6 2499 if (!isempty(state))
12ca818f 2500 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
c0467cf3 2501
760b9d7c
LP
2502 /* Turn on NNP, but only if it wasn't configured explicitly
2503 * before, and only if we are in user mode. */
463d0d15 2504 if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager))
760b9d7c 2505 c->no_new_privileges = true;
17df7223
LP
2506
2507 return 0;
2508}
2509
57183d11
LP
2510int config_parse_syscall_archs(
2511 const char *unit,
2512 const char *filename,
2513 unsigned line,
2514 const char *section,
2515 unsigned section_line,
2516 const char *lvalue,
2517 int ltype,
2518 const char *rvalue,
2519 void *data,
2520 void *userdata) {
2521
d3b1c508 2522 Set **archs = data;
a2a5291b 2523 const char *word, *state;
57183d11
LP
2524 size_t l;
2525 int r;
2526
2527 if (isempty(rvalue)) {
525d3cc7 2528 *archs = set_free(*archs);
57183d11
LP
2529 return 0;
2530 }
2531
d5099efc 2532 r = set_ensure_allocated(archs, NULL);
57183d11
LP
2533 if (r < 0)
2534 return log_oom();
2535
a2a5291b 2536 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
57183d11
LP
2537 _cleanup_free_ char *t = NULL;
2538 uint32_t a;
2539
a2a5291b 2540 t = strndup(word, l);
57183d11
LP
2541 if (!t)
2542 return log_oom();
2543
2544 r = seccomp_arch_from_string(t, &a);
2545 if (r < 0) {
12ca818f 2546 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call architecture, ignoring: %s", t);
57183d11
LP
2547 continue;
2548 }
2549
d3b1c508 2550 r = set_put(*archs, UINT32_TO_PTR(a + 1));
756c09e6 2551 if (r == 0)
57183d11
LP
2552 continue;
2553 if (r < 0)
2554 return log_oom();
2555 }
b2fadec6 2556 if (!isempty(state))
12ca818f 2557 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
57183d11
LP
2558
2559 return 0;
2560}
2561
17df7223
LP
2562int config_parse_syscall_errno(
2563 const char *unit,
2564 const char *filename,
2565 unsigned line,
2566 const char *section,
2567 unsigned section_line,
2568 const char *lvalue,
2569 int ltype,
2570 const char *rvalue,
2571 void *data,
2572 void *userdata) {
2573
2574 ExecContext *c = data;
2575 int e;
2576
2577 assert(filename);
2578 assert(lvalue);
2579 assert(rvalue);
2580
2581 if (isempty(rvalue)) {
2582 /* Empty assignment resets to KILL */
2583 c->syscall_errno = 0;
2584 return 0;
8351ceae
LP
2585 }
2586
17df7223
LP
2587 e = errno_from_name(rvalue);
2588 if (e < 0) {
12ca818f 2589 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
17df7223
LP
2590 return 0;
2591 }
8351ceae 2592
17df7223 2593 c->syscall_errno = e;
8351ceae
LP
2594 return 0;
2595}
4298d0b5
LP
2596
2597int config_parse_address_families(
2598 const char *unit,
2599 const char *filename,
2600 unsigned line,
2601 const char *section,
2602 unsigned section_line,
2603 const char *lvalue,
2604 int ltype,
2605 const char *rvalue,
2606 void *data,
2607 void *userdata) {
2608
2609 ExecContext *c = data;
4298d0b5 2610 bool invert = false;
a2a5291b 2611 const char *word, *state;
4298d0b5
LP
2612 size_t l;
2613 int r;
2614
2615 assert(filename);
2616 assert(lvalue);
2617 assert(rvalue);
4298d0b5
LP
2618
2619 if (isempty(rvalue)) {
2620 /* Empty assignment resets the list */
525d3cc7 2621 c->address_families = set_free(c->address_families);
4298d0b5
LP
2622 c->address_families_whitelist = false;
2623 return 0;
2624 }
2625
2626 if (rvalue[0] == '~') {
2627 invert = true;
2628 rvalue++;
2629 }
2630
2631 if (!c->address_families) {
d5099efc 2632 c->address_families = set_new(NULL);
4298d0b5
LP
2633 if (!c->address_families)
2634 return log_oom();
2635
2636 c->address_families_whitelist = !invert;
2637 }
2638
a2a5291b 2639 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
4298d0b5
LP
2640 _cleanup_free_ char *t = NULL;
2641 int af;
2642
a2a5291b 2643 t = strndup(word, l);
4298d0b5
LP
2644 if (!t)
2645 return log_oom();
2646
2647 af = af_from_name(t);
2648 if (af <= 0) {
12ca818f 2649 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse address family, ignoring: %s", t);
4298d0b5
LP
2650 continue;
2651 }
2652
2653 /* If we previously wanted to forbid an address family and now
2654 * we want to allow it, then remove it from the list
2655 */
2656 if (!invert == c->address_families_whitelist) {
2657 r = set_put(c->address_families, INT_TO_PTR(af));
756c09e6 2658 if (r == 0)
4298d0b5
LP
2659 continue;
2660 if (r < 0)
2661 return log_oom();
2662 } else
2663 set_remove(c->address_families, INT_TO_PTR(af));
2664 }
b2fadec6 2665 if (!isempty(state))
12ca818f 2666 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
4298d0b5
LP
2667
2668 return 0;
2669}
c0467cf3 2670#endif
8351ceae 2671
a016b922
LP
2672int config_parse_unit_slice(
2673 const char *unit,
2674 const char *filename,
2675 unsigned line,
2676 const char *section,
71a61510 2677 unsigned section_line,
a016b922
LP
2678 const char *lvalue,
2679 int ltype,
2680 const char *rvalue,
2681 void *data,
2682 void *userdata) {
2683
2684 _cleanup_free_ char *k = NULL;
d79200e2 2685 Unit *u = userdata, *slice = NULL;
a016b922
LP
2686 int r;
2687
2688 assert(filename);
2689 assert(lvalue);
2690 assert(rvalue);
2691 assert(u);
2692
19f6d710 2693 r = unit_name_printf(u, rvalue, &k);
d79200e2
LP
2694 if (r < 0) {
2695 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
2696 return 0;
19f6d710 2697 }
a016b922 2698
19f6d710 2699 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
a016b922 2700 if (r < 0) {
d79200e2 2701 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k);
a016b922
LP
2702 return 0;
2703 }
2704
d79200e2
LP
2705 r = unit_set_slice(u, slice);
2706 if (r < 0) {
2707 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id);
a016b922
LP
2708 return 0;
2709 }
2710
a016b922
LP
2711 return 0;
2712}
2713
4ad49000
LP
2714DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2715
2716int config_parse_cpu_shares(
2717 const char *unit,
2718 const char *filename,
2719 unsigned line,
2720 const char *section,
71a61510 2721 unsigned section_line,
4ad49000
LP
2722 const char *lvalue,
2723 int ltype,
2724 const char *rvalue,
2725 void *data,
2726 void *userdata) {
2727
d53d9474 2728 uint64_t *shares = data;
95ae05c0
WC
2729 int r;
2730
2731 assert(filename);
2732 assert(lvalue);
2733 assert(rvalue);
2734
d53d9474
LP
2735 r = cg_cpu_shares_parse(rvalue, shares);
2736 if (r < 0) {
2737 log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
2738 return 0;
2739 }
2740
4ad49000
LP
2741 return 0;
2742}
2743
b2f8b02e
LP
2744int config_parse_cpu_quota(
2745 const char *unit,
2746 const char *filename,
2747 unsigned line,
2748 const char *section,
2749 unsigned section_line,
2750 const char *lvalue,
2751 int ltype,
2752 const char *rvalue,
2753 void *data,
2754 void *userdata) {
2755
2756 CGroupContext *c = data;
9a054909 2757 double percent;
b2f8b02e
LP
2758
2759 assert(filename);
2760 assert(lvalue);
2761 assert(rvalue);
2762
2763 if (isempty(rvalue)) {
3a43da28 2764 c->cpu_quota_per_sec_usec = USEC_INFINITY;
b2f8b02e
LP
2765 return 0;
2766 }
2767
9a054909 2768 if (!endswith(rvalue, "%")) {
12ca818f 2769 log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue);
9a054909
LP
2770 return 0;
2771 }
b2f8b02e 2772
9a054909 2773 if (sscanf(rvalue, "%lf%%", &percent) != 1 || percent <= 0) {
12ca818f 2774 log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' invalid. Ignoring.", rvalue);
9a054909 2775 return 0;
b2f8b02e
LP
2776 }
2777
9a054909
LP
2778 c->cpu_quota_per_sec_usec = (usec_t) (percent * USEC_PER_SEC / 100);
2779
b2f8b02e
LP
2780 return 0;
2781}
2782
4ad49000
LP
2783int config_parse_memory_limit(
2784 const char *unit,
2785 const char *filename,
2786 unsigned line,
2787 const char *section,
71a61510 2788 unsigned section_line,
4ad49000
LP
2789 const char *lvalue,
2790 int ltype,
2791 const char *rvalue,
2792 void *data,
2793 void *userdata) {
2794
2795 CGroupContext *c = data;
59f448cf 2796 uint64_t bytes;
4ad49000
LP
2797 int r;
2798
03a7b521 2799 if (isempty(rvalue) || streq(rvalue, "infinity")) {
ddca82ac 2800 c->memory_limit = (uint64_t) -1;
4ad49000
LP
2801 return 0;
2802 }
2803
5556b5fe 2804 r = parse_size(rvalue, 1024, &bytes);
03a7b521 2805 if (r < 0 || bytes < 1) {
12ca818f 2806 log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
2807 return 0;
2808 }
2809
59f448cf 2810 c->memory_limit = bytes;
4ad49000
LP
2811 return 0;
2812}
2813
03a7b521
LP
2814int config_parse_tasks_max(
2815 const char *unit,
2816 const char *filename,
2817 unsigned line,
2818 const char *section,
2819 unsigned section_line,
2820 const char *lvalue,
2821 int ltype,
2822 const char *rvalue,
2823 void *data,
2824 void *userdata) {
2825
0af20ea2 2826 uint64_t *tasks_max = data, u;
03a7b521
LP
2827 int r;
2828
2829 if (isempty(rvalue) || streq(rvalue, "infinity")) {
0af20ea2 2830 *tasks_max = (uint64_t) -1;
03a7b521
LP
2831 return 0;
2832 }
2833
2834 r = safe_atou64(rvalue, &u);
2835 if (r < 0 || u < 1) {
12ca818f 2836 log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
03a7b521
LP
2837 return 0;
2838 }
2839
0af20ea2 2840 *tasks_max = u;
03a7b521
LP
2841 return 0;
2842}
2843
4ad49000
LP
2844int config_parse_device_allow(
2845 const char *unit,
2846 const char *filename,
2847 unsigned line,
2848 const char *section,
71a61510 2849 unsigned section_line,
4ad49000
LP
2850 const char *lvalue,
2851 int ltype,
2852 const char *rvalue,
2853 void *data,
2854 void *userdata) {
2855
1116e14c 2856 _cleanup_free_ char *path = NULL, *t = NULL;
4ad49000
LP
2857 CGroupContext *c = data;
2858 CGroupDeviceAllow *a;
1116e14c 2859 const char *m = NULL;
4ad49000 2860 size_t n;
1116e14c 2861 int r;
4ad49000
LP
2862
2863 if (isempty(rvalue)) {
2864 while (c->device_allow)
2865 cgroup_context_free_device_allow(c, c->device_allow);
2866
2867 return 0;
2868 }
2869
1116e14c
NBS
2870 r = unit_full_printf(userdata, rvalue, &t);
2871 if(r < 0) {
2872 log_syntax(unit, LOG_WARNING, filename, line, r,
2873 "Failed to resolve specifiers in %s, ignoring: %m",
2874 rvalue);
2875 }
2876
2877 n = strcspn(t, WHITESPACE);
2878
2879 path = strndup(t, n);
4ad49000
LP
2880 if (!path)
2881 return log_oom();
2882
90060676
LP
2883 if (!startswith(path, "/dev/") &&
2884 !startswith(path, "block-") &&
2885 !startswith(path, "char-")) {
12ca818f 2886 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
2887 return 0;
2888 }
2889
1116e14c 2890 m = t + n + strspn(t + n, WHITESPACE);
4ad49000
LP
2891 if (isempty(m))
2892 m = "rwm";
2893
2894 if (!in_charset(m, "rwm")) {
12ca818f 2895 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
4ad49000
LP
2896 return 0;
2897 }
2898
2899 a = new0(CGroupDeviceAllow, 1);
2900 if (!a)
2901 return log_oom();
2902
2903 a->path = path;
2904 path = NULL;
2905 a->r = !!strchr(m, 'r');
2906 a->w = !!strchr(m, 'w');
2907 a->m = !!strchr(m, 'm');
2908
71fda00f 2909 LIST_PREPEND(device_allow, c->device_allow, a);
4ad49000
LP
2910 return 0;
2911}
2912
13c31542
TH
2913int config_parse_io_weight(
2914 const char *unit,
2915 const char *filename,
2916 unsigned line,
2917 const char *section,
2918 unsigned section_line,
2919 const char *lvalue,
2920 int ltype,
2921 const char *rvalue,
2922 void *data,
2923 void *userdata) {
2924
2925 uint64_t *weight = data;
2926 int r;
2927
2928 assert(filename);
2929 assert(lvalue);
2930 assert(rvalue);
2931
2932 r = cg_weight_parse(rvalue, weight);
2933 if (r < 0) {
2934 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue);
2935 return 0;
2936 }
2937
2938 return 0;
2939}
2940
2941int config_parse_io_device_weight(
2942 const char *unit,
2943 const char *filename,
2944 unsigned line,
2945 const char *section,
2946 unsigned section_line,
2947 const char *lvalue,
2948 int ltype,
2949 const char *rvalue,
2950 void *data,
2951 void *userdata) {
2952
2953 _cleanup_free_ char *path = NULL;
2954 CGroupIODeviceWeight *w;
2955 CGroupContext *c = data;
2956 const char *weight;
2957 uint64_t u;
2958 size_t n;
2959 int r;
2960
2961 assert(filename);
2962 assert(lvalue);
2963 assert(rvalue);
2964
2965 if (isempty(rvalue)) {
2966 while (c->io_device_weights)
2967 cgroup_context_free_io_device_weight(c, c->io_device_weights);
2968
2969 return 0;
2970 }
2971
2972 n = strcspn(rvalue, WHITESPACE);
2973 weight = rvalue + n;
2974 weight += strspn(weight, WHITESPACE);
2975
2976 if (isempty(weight)) {
2977 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
2978 return 0;
2979 }
2980
2981 path = strndup(rvalue, n);
2982 if (!path)
2983 return log_oom();
2984
2985 if (!path_startswith(path, "/dev")) {
2986 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
2987 return 0;
2988 }
2989
2990 r = cg_weight_parse(weight, &u);
2991 if (r < 0) {
2992 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight);
2993 return 0;
2994 }
2995
2996 assert(u != CGROUP_WEIGHT_INVALID);
2997
2998 w = new0(CGroupIODeviceWeight, 1);
2999 if (!w)
3000 return log_oom();
3001
3002 w->path = path;
3003 path = NULL;
3004
3005 w->weight = u;
3006
3007 LIST_PREPEND(device_weights, c->io_device_weights, w);
3008 return 0;
3009}
3010
3011int config_parse_io_limit(
3012 const char *unit,
3013 const char *filename,
3014 unsigned line,
3015 const char *section,
3016 unsigned section_line,
3017 const char *lvalue,
3018 int ltype,
3019 const char *rvalue,
3020 void *data,
3021 void *userdata) {
3022
3023 _cleanup_free_ char *path = NULL;
3024 CGroupIODeviceLimit *l = NULL, *t;
3025 CGroupContext *c = data;
9be57249 3026 CGroupIOLimitType type;
13c31542
TH
3027 const char *limit;
3028 uint64_t num;
13c31542
TH
3029 size_t n;
3030 int r;
3031
3032 assert(filename);
3033 assert(lvalue);
3034 assert(rvalue);
3035
9be57249
TH
3036 type = cgroup_io_limit_type_from_string(lvalue);
3037 assert(type >= 0);
13c31542
TH
3038
3039 if (isempty(rvalue)) {
3040 LIST_FOREACH(device_limits, l, c->io_device_limits)
9be57249 3041 l->limits[type] = cgroup_io_limit_defaults[type];
13c31542
TH
3042 return 0;
3043 }
3044
3045 n = strcspn(rvalue, WHITESPACE);
3046 limit = rvalue + n;
3047 limit += strspn(limit, WHITESPACE);
3048
3049 if (!*limit) {
3050 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3051 return 0;
3052 }
3053
3054 path = strndup(rvalue, n);
3055 if (!path)
3056 return log_oom();
3057
3058 if (!path_startswith(path, "/dev")) {
3059 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3060 return 0;
3061 }
3062
3063 if (streq("max", limit)) {
3064 num = CGROUP_LIMIT_MAX;
3065 } else {
3066 r = parse_size(limit, 1000, &num);
3067 if (r < 0 || num <= 0) {
3068 log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue);
3069 return 0;
3070 }
3071 }
3072
3073 LIST_FOREACH(device_limits, t, c->io_device_limits) {
3074 if (path_equal(path, t->path)) {
3075 l = t;
3076 break;
3077 }
3078 }
3079
3080 if (!l) {
9be57249
TH
3081 CGroupIOLimitType ttype;
3082
13c31542
TH
3083 l = new0(CGroupIODeviceLimit, 1);
3084 if (!l)
3085 return log_oom();
3086
3087 l->path = path;
3088 path = NULL;
9be57249
TH
3089 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
3090 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
13c31542
TH
3091
3092 LIST_PREPEND(device_limits, c->io_device_limits, l);
3093 }
3094
9be57249 3095 l->limits[type] = num;
13c31542
TH
3096
3097 return 0;
3098}
3099
4ad49000
LP
3100int config_parse_blockio_weight(
3101 const char *unit,
3102 const char *filename,
3103 unsigned line,
3104 const char *section,
71a61510 3105 unsigned section_line,
4ad49000
LP
3106 const char *lvalue,
3107 int ltype,
3108 const char *rvalue,
3109 void *data,
3110 void *userdata) {
3111
d53d9474 3112 uint64_t *weight = data;
95ae05c0
WC
3113 int r;
3114
3115 assert(filename);
3116 assert(lvalue);
3117 assert(rvalue);
3118
d53d9474
LP
3119 r = cg_blkio_weight_parse(rvalue, weight);
3120 if (r < 0) {
3121 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
3122 return 0;
3123 }
3124
8e7076ca
LP
3125 return 0;
3126}
3127
3128int config_parse_blockio_device_weight(
3129 const char *unit,
3130 const char *filename,
3131 unsigned line,
3132 const char *section,
71a61510 3133 unsigned section_line,
8e7076ca
LP
3134 const char *lvalue,
3135 int ltype,
3136 const char *rvalue,
3137 void *data,
3138 void *userdata) {
3139
4ad49000 3140 _cleanup_free_ char *path = NULL;
8e7076ca 3141 CGroupBlockIODeviceWeight *w;
4ad49000 3142 CGroupContext *c = data;
4ad49000 3143 const char *weight;
d53d9474 3144 uint64_t u;
4ad49000
LP
3145 size_t n;
3146 int r;
3147
3148 assert(filename);
3149 assert(lvalue);
3150 assert(rvalue);
3151
3152 if (isempty(rvalue)) {
4ad49000
LP
3153 while (c->blockio_device_weights)
3154 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
3155
3156 return 0;
3157 }
3158
3159 n = strcspn(rvalue, WHITESPACE);
3160 weight = rvalue + n;
d53d9474
LP
3161 weight += strspn(weight, WHITESPACE);
3162
3163 if (isempty(weight)) {
12ca818f 3164 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
8e7076ca
LP
3165 return 0;
3166 }
4ad49000 3167
8e7076ca
LP
3168 path = strndup(rvalue, n);
3169 if (!path)
3170 return log_oom();
4ad49000 3171
8e7076ca 3172 if (!path_startswith(path, "/dev")) {
12ca818f 3173 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
8e7076ca
LP
3174 return 0;
3175 }
4ad49000 3176
d53d9474
LP
3177 r = cg_blkio_weight_parse(weight, &u);
3178 if (r < 0) {
3179 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
4ad49000
LP
3180 return 0;
3181 }
3182
d53d9474
LP
3183 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
3184
8e7076ca
LP
3185 w = new0(CGroupBlockIODeviceWeight, 1);
3186 if (!w)
3187 return log_oom();
4ad49000 3188
8e7076ca
LP
3189 w->path = path;
3190 path = NULL;
4ad49000 3191
d53d9474 3192 w->weight = u;
4ad49000 3193
71fda00f 3194 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
3195 return 0;
3196}
3197
3198int config_parse_blockio_bandwidth(
3199 const char *unit,
3200 const char *filename,
3201 unsigned line,
3202 const char *section,
71a61510 3203 unsigned section_line,
4ad49000
LP
3204 const char *lvalue,
3205 int ltype,
3206 const char *rvalue,
3207 void *data,
3208 void *userdata) {
3209
3210 _cleanup_free_ char *path = NULL;
979d0311 3211 CGroupBlockIODeviceBandwidth *b = NULL, *t;
4ad49000
LP
3212 CGroupContext *c = data;
3213 const char *bandwidth;
59f448cf 3214 uint64_t bytes;
47c0980d 3215 bool read;
4ad49000
LP
3216 size_t n;
3217 int r;
3218
3219 assert(filename);
3220 assert(lvalue);
3221 assert(rvalue);
3222
47c0980d
G
3223 read = streq("BlockIOReadBandwidth", lvalue);
3224
4ad49000 3225 if (isempty(rvalue)) {
979d0311
TH
3226 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
3227 b->rbps = CGROUP_LIMIT_MAX;
3228 b->wbps = CGROUP_LIMIT_MAX;
3229 }
4ad49000
LP
3230 return 0;
3231 }
3232
3233 n = strcspn(rvalue, WHITESPACE);
3234 bandwidth = rvalue + n;
3235 bandwidth += strspn(bandwidth, WHITESPACE);
3236
3237 if (!*bandwidth) {
12ca818f 3238 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
4ad49000
LP
3239 return 0;
3240 }
3241
3242 path = strndup(rvalue, n);
3243 if (!path)
3244 return log_oom();
3245
3246 if (!path_startswith(path, "/dev")) {
12ca818f 3247 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
3248 return 0;
3249 }
3250
5556b5fe 3251 r = parse_size(bandwidth, 1000, &bytes);
4ad49000 3252 if (r < 0 || bytes <= 0) {
12ca818f 3253 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
3254 return 0;
3255 }
3256
979d0311
TH
3257 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
3258 if (path_equal(path, t->path)) {
3259 b = t;
3260 break;
3261 }
3262 }
4ad49000 3263
979d0311
TH
3264 if (!t) {
3265 b = new0(CGroupBlockIODeviceBandwidth, 1);
3266 if (!b)
3267 return log_oom();
3268
3269 b->path = path;
3270 path = NULL;
3271 b->rbps = CGROUP_LIMIT_MAX;
3272 b->wbps = CGROUP_LIMIT_MAX;
3273
3274 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
3275 }
4ad49000 3276
979d0311
TH
3277 if (read)
3278 b->rbps = bytes;
3279 else
3280 b->wbps = bytes;
4ad49000
LP
3281
3282 return 0;
3283}
3284
d420282b
LP
3285DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
3286
3287int config_parse_job_mode_isolate(
3288 const char *unit,
3289 const char *filename,
3290 unsigned line,
3291 const char *section,
3292 unsigned section_line,
3293 const char *lvalue,
3294 int ltype,
3295 const char *rvalue,
3296 void *data,
3297 void *userdata) {
3298
3299 JobMode *m = data;
3300 int r;
3301
3302 assert(filename);
3303 assert(lvalue);
3304 assert(rvalue);
3305
3306 r = parse_boolean(rvalue);
3307 if (r < 0) {
12ca818f 3308 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
d420282b
LP
3309 return 0;
3310 }
3311
3312 *m = r ? JOB_ISOLATE : JOB_REPLACE;
3313 return 0;
3314}
3315
e66cf1a3
LP
3316int config_parse_runtime_directory(
3317 const char *unit,
3318 const char *filename,
3319 unsigned line,
3320 const char *section,
3321 unsigned section_line,
3322 const char *lvalue,
3323 int ltype,
3324 const char *rvalue,
3325 void *data,
3326 void *userdata) {
3327
a2a5291b 3328 char***rt = data;
9b5864d9 3329 Unit *u = userdata;
a2a5291b 3330 const char *word, *state;
e66cf1a3
LP
3331 size_t l;
3332 int r;
3333
3334 assert(filename);
3335 assert(lvalue);
3336 assert(rvalue);
3337 assert(data);
3338
3339 if (isempty(rvalue)) {
3340 /* Empty assignment resets the list */
6796073e 3341 *rt = strv_free(*rt);
e66cf1a3
LP
3342 return 0;
3343 }
3344
a2a5291b 3345 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
9b5864d9 3346 _cleanup_free_ char *t = NULL, *n = NULL;
e66cf1a3 3347
9b5864d9
MG
3348 t = strndup(word, l);
3349 if (!t)
e66cf1a3
LP
3350 return log_oom();
3351
9b5864d9
MG
3352 r = unit_name_printf(u, t, &n);
3353 if (r < 0) {
12ca818f 3354 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
9b5864d9
MG
3355 continue;
3356 }
3357
ae6c3cc0 3358 if (!filename_is_valid(n)) {
12ca818f 3359 log_syntax(unit, LOG_ERR, filename, line, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
e66cf1a3
LP
3360 continue;
3361 }
3362
3363 r = strv_push(rt, n);
3364 if (r < 0)
3365 return log_oom();
3366
3367 n = NULL;
3368 }
b2fadec6 3369 if (!isempty(state))
12ca818f 3370 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
e66cf1a3
LP
3371
3372 return 0;
3373}
3374
3af00fb8
LP
3375int config_parse_set_status(
3376 const char *unit,
3377 const char *filename,
3378 unsigned line,
3379 const char *section,
3380 unsigned section_line,
3381 const char *lvalue,
3382 int ltype,
3383 const char *rvalue,
3384 void *data,
3385 void *userdata) {
3386
3af00fb8 3387 size_t l;
a2a5291b 3388 const char *word, *state;
3af00fb8
LP
3389 int r;
3390 ExitStatusSet *status_set = data;
3391
3392 assert(filename);
3393 assert(lvalue);
3394 assert(rvalue);
3395 assert(data);
3396
3e2d435b 3397 /* Empty assignment resets the list */
3af00fb8 3398 if (isempty(rvalue)) {
3e2d435b 3399 exit_status_set_free(status_set);
3af00fb8
LP
3400 return 0;
3401 }
3402
a2a5291b 3403 FOREACH_WORD(word, l, rvalue, state) {
3af00fb8
LP
3404 _cleanup_free_ char *temp;
3405 int val;
61593865 3406 Set **set;
3af00fb8 3407
a2a5291b 3408 temp = strndup(word, l);
3af00fb8
LP
3409 if (!temp)
3410 return log_oom();
3411
3412 r = safe_atoi(temp, &val);
3413 if (r < 0) {
3414 val = signal_from_string_try_harder(temp);
3415
1e2fd62d 3416 if (val <= 0) {
12ca818f 3417 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
61593865 3418 continue;
3af00fb8 3419 }
61593865 3420 set = &status_set->signal;
3af00fb8 3421 } else {
1e2fd62d 3422 if (val < 0 || val > 255) {
12ca818f 3423 log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
1e2fd62d 3424 continue;
3af00fb8 3425 }
61593865 3426 set = &status_set->status;
3af00fb8 3427 }
1e2fd62d 3428
61593865 3429 r = set_ensure_allocated(set, NULL);
1e2fd62d
ZJS
3430 if (r < 0)
3431 return log_oom();
3432
61593865 3433 r = set_put(*set, INT_TO_PTR(val));
1e2fd62d 3434 if (r < 0) {
12ca818f 3435 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
1e2fd62d
ZJS
3436 return r;
3437 }
3af00fb8 3438 }
b2fadec6 3439 if (!isempty(state))
12ca818f 3440 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3af00fb8
LP
3441
3442 return 0;
3443}
3444
94828d2d
LP
3445int config_parse_namespace_path_strv(
3446 const char *unit,
3447 const char *filename,
3448 unsigned line,
3449 const char *section,
3450 unsigned section_line,
3451 const char *lvalue,
3452 int ltype,
3453 const char *rvalue,
3454 void *data,
3455 void *userdata) {
3456
a2a5291b 3457 char*** sv = data;
727f76d7
EV
3458 const char *prev;
3459 const char *cur;
94828d2d
LP
3460 int r;
3461
3462 assert(filename);
3463 assert(lvalue);
3464 assert(rvalue);
3465 assert(data);
3466
3467 if (isempty(rvalue)) {
3468 /* Empty assignment resets the list */
6796073e 3469 *sv = strv_free(*sv);
94828d2d
LP
3470 return 0;
3471 }
3472
727f76d7
EV
3473 prev = cur = rvalue;
3474 for (;;) {
3475 _cleanup_free_ char *word = NULL;
94828d2d
LP
3476 int offset;
3477
727f76d7 3478 r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
0293a7a8
EV
3479 if (r == 0)
3480 break;
3481 if (r == -ENOMEM)
3482 return log_oom();
727f76d7 3483 if (r < 0) {
0293a7a8 3484 log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage, ignoring: %s", prev);
727f76d7
EV
3485 return 0;
3486 }
94828d2d 3487
727f76d7
EV
3488 if (!utf8_is_valid(word)) {
3489 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
3490 prev = cur;
94828d2d
LP
3491 continue;
3492 }
3493
727f76d7
EV
3494 offset = word[0] == '-';
3495 if (!path_is_absolute(word + offset)) {
3496 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", word);
3497 prev = cur;
94828d2d
LP
3498 continue;
3499 }
3500
727f76d7 3501 path_kill_slashes(word + offset);
94828d2d 3502
727f76d7 3503 r = strv_push(sv, word);
94828d2d
LP
3504 if (r < 0)
3505 return log_oom();
3506
727f76d7
EV
3507 prev = cur;
3508 word = NULL;
94828d2d
LP
3509 }
3510
3511 return 0;
3512}
3513
f1721625 3514int config_parse_no_new_privileges(
760b9d7c
LP
3515 const char* unit,
3516 const char *filename,
3517 unsigned line,
3518 const char *section,
3519 unsigned section_line,
3520 const char *lvalue,
3521 int ltype,
3522 const char *rvalue,
3523 void *data,
3524 void *userdata) {
3525
3526 ExecContext *c = data;
3527 int k;
3528
3529 assert(filename);
3530 assert(lvalue);
3531 assert(rvalue);
3532 assert(data);
3533
3534 k = parse_boolean(rvalue);
3535 if (k < 0) {
12ca818f 3536 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
760b9d7c
LP
3537 return 0;
3538 }
3539
3540 c->no_new_privileges = !!k;
3541 c->no_new_privileges_set = true;
3542
3543 return 0;
3544}
3545
1b8689f9 3546int config_parse_protect_home(
417116f2
LP
3547 const char* unit,
3548 const char *filename,
3549 unsigned line,
3550 const char *section,
3551 unsigned section_line,
3552 const char *lvalue,
3553 int ltype,
3554 const char *rvalue,
3555 void *data,
3556 void *userdata) {
3557
3558 ExecContext *c = data;
3559 int k;
3560
3561 assert(filename);
3562 assert(lvalue);
3563 assert(rvalue);
3564 assert(data);
3565
3566 /* Our enum shall be a superset of booleans, hence first try
3567 * to parse as as boolean, and then as enum */
3568
3569 k = parse_boolean(rvalue);
3570 if (k > 0)
1b8689f9 3571 c->protect_home = PROTECT_HOME_YES;
417116f2 3572 else if (k == 0)
1b8689f9 3573 c->protect_home = PROTECT_HOME_NO;
417116f2 3574 else {
1b8689f9 3575 ProtectHome h;
417116f2 3576
1b8689f9 3577 h = protect_home_from_string(rvalue);
9ed794a3 3578 if (h < 0) {
12ca818f 3579 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
417116f2
LP
3580 return 0;
3581 }
3582
1b8689f9
LP
3583 c->protect_home = h;
3584 }
3585
3586 return 0;
3587}
3588
3589int config_parse_protect_system(
3590 const char* unit,
3591 const char *filename,
3592 unsigned line,
3593 const char *section,
3594 unsigned section_line,
3595 const char *lvalue,
3596 int ltype,
3597 const char *rvalue,
3598 void *data,
3599 void *userdata) {
3600
3601 ExecContext *c = data;
3602 int k;
3603
3604 assert(filename);
3605 assert(lvalue);
3606 assert(rvalue);
3607 assert(data);
3608
3609 /* Our enum shall be a superset of booleans, hence first try
3610 * to parse as as boolean, and then as enum */
3611
3612 k = parse_boolean(rvalue);
3613 if (k > 0)
3614 c->protect_system = PROTECT_SYSTEM_YES;
3615 else if (k == 0)
3616 c->protect_system = PROTECT_SYSTEM_NO;
3617 else {
3618 ProtectSystem s;
3619
3620 s = protect_system_from_string(rvalue);
9ed794a3 3621 if (s < 0) {
12ca818f 3622 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
1b8689f9
LP
3623 return 0;
3624 }
3625
3626 c->protect_system = s;
417116f2
LP
3627 }
3628
3629 return 0;
3630}
3631
071830ff 3632#define FOLLOW_MAX 8
87f0e418 3633
9e2f7c11 3634static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
a837f088 3635 char *id = NULL;
0301abf4 3636 unsigned c = 0;
87f0e418
LP
3637 int fd, r;
3638 FILE *f;
87f0e418
LP
3639
3640 assert(filename);
3641 assert(*filename);
3642 assert(_f);
3643 assert(names);
3644
0301abf4
LP
3645 /* This will update the filename pointer if the loaded file is
3646 * reached by a symlink. The old string will be freed. */
87f0e418 3647
0301abf4 3648 for (;;) {
2c7108c4 3649 char *target, *name;
87f0e418 3650
0301abf4
LP
3651 if (c++ >= FOLLOW_MAX)
3652 return -ELOOP;
3653
b08d03ff
LP
3654 path_kill_slashes(*filename);
3655
87f0e418 3656 /* Add the file name we are currently looking at to
8f05424d
LP
3657 * the names of this unit, but only if it is a valid
3658 * unit name. */
2b6bf07d 3659 name = basename(*filename);
7410616c 3660 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
8f05424d 3661
15e11d81
LP
3662 id = set_get(names, name);
3663 if (!id) {
3664 id = strdup(name);
3665 if (!id)
8f05424d 3666 return -ENOMEM;
87f0e418 3667
ef42202a
ZJS
3668 r = set_consume(names, id);
3669 if (r < 0)
8f05424d 3670 return r;
87f0e418 3671 }
87f0e418
LP
3672 }
3673
0301abf4 3674 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
3675 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
3676 if (fd >= 0)
87f0e418
LP
3677 break;
3678
0301abf4
LP
3679 if (errno != ELOOP)
3680 return -errno;
3681
87f0e418 3682 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
3683 r = readlink_and_make_absolute(*filename, &target);
3684 if (r < 0)
0301abf4 3685 return r;
87f0e418 3686
0301abf4 3687 free(*filename);
2c7108c4 3688 *filename = target;
87f0e418
LP
3689 }
3690
9946996c
LP
3691 f = fdopen(fd, "re");
3692 if (!f) {
03e334a1 3693 safe_close(fd);
d4ad27a1 3694 return -errno;
87f0e418
LP
3695 }
3696
3697 *_f = f;
9e2f7c11 3698 *_final = id;
a837f088 3699
0301abf4 3700 return 0;
87f0e418
LP
3701}
3702
23a177ef
LP
3703static int merge_by_names(Unit **u, Set *names, const char *id) {
3704 char *k;
3705 int r;
3706
3707 assert(u);
3708 assert(*u);
3709 assert(names);
3710
3711 /* Let's try to add in all symlink names we found */
3712 while ((k = set_steal_first(names))) {
3713
3714 /* First try to merge in the other name into our
3715 * unit */
9946996c
LP
3716 r = unit_merge_by_name(*u, k);
3717 if (r < 0) {
23a177ef
LP
3718 Unit *other;
3719
3720 /* Hmm, we couldn't merge the other unit into
3721 * ours? Then let's try it the other way
3722 * round */
3723
7aad67e7
MS
3724 /* If the symlink name we are looking at is unit template, then
3725 we must search for instance of this template */
3726 if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE)) {
3727 _cleanup_free_ char *instance = NULL;
3728
3729 r = unit_name_replace_instance(k, (*u)->instance, &instance);
3730 if (r < 0)
3731 return r;
3732
3733 other = manager_get_unit((*u)->manager, instance);
3734 } else
3735 other = manager_get_unit((*u)->manager, k);
3736
23a177ef
LP
3737 free(k);
3738
9946996c
LP
3739 if (other) {
3740 r = unit_merge(other, *u);
3741 if (r >= 0) {
23a177ef
LP
3742 *u = other;
3743 return merge_by_names(u, names, NULL);
3744 }
9946996c 3745 }
23a177ef
LP
3746
3747 return r;
3748 }
3749
3750 if (id == k)
3751 unit_choose_id(*u, id);
3752
3753 free(k);
3754 }
3755
3756 return 0;
3757}
3758
e537352b 3759static int load_from_path(Unit *u, const char *path) {
e48614c4
ZJS
3760 _cleanup_set_free_free_ Set *symlink_names = NULL;
3761 _cleanup_fclose_ FILE *f = NULL;
3762 _cleanup_free_ char *filename = NULL;
3763 char *id = NULL;
23a177ef 3764 Unit *merged;
45fb0699 3765 struct stat st;
a837f088 3766 int r;
23a177ef
LP
3767
3768 assert(u);
e537352b 3769 assert(path);
3efd4195 3770
d5099efc 3771 symlink_names = set_new(&string_hash_ops);
f975e971 3772 if (!symlink_names)
87f0e418 3773 return -ENOMEM;
3efd4195 3774
036643a2
LP
3775 if (path_is_absolute(path)) {
3776
9946996c 3777 filename = strdup(path);
e48614c4
ZJS
3778 if (!filename)
3779 return -ENOMEM;
036643a2 3780
9946996c
LP
3781 r = open_follow(&filename, &f, symlink_names, &id);
3782 if (r < 0) {
97b11eed 3783 filename = mfree(filename);
036643a2 3784 if (r != -ENOENT)
e48614c4 3785 return r;
036643a2
LP
3786 }
3787
3788 } else {
3789 char **p;
3790
a3c4eb07 3791 STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
036643a2
LP
3792
3793 /* Instead of opening the path right away, we manually
3794 * follow all symlinks and add their name to our unit
3795 * name set while doing so */
9946996c 3796 filename = path_make_absolute(path, *p);
e48614c4
ZJS
3797 if (!filename)
3798 return -ENOMEM;
036643a2 3799
ac155bb8
MS
3800 if (u->manager->unit_path_cache &&
3801 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
3802 r = -ENOENT;
3803 else
3804 r = open_follow(&filename, &f, symlink_names, &id);
a837f088
LP
3805 if (r >= 0)
3806 break;
3807 filename = mfree(filename);
3808 if (r != -ENOENT)
3809 return r;
fe51822e 3810
a837f088
LP
3811 /* Empty the symlink names for the next run */
3812 set_clear_free(symlink_names);
036643a2
LP
3813 }
3814 }
034c6ed7 3815
e48614c4 3816 if (!filename)
8f05424d 3817 /* Hmm, no suitable file found? */
e48614c4 3818 return 0;
87f0e418 3819
8a993b61 3820 if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
a837f088
LP
3821 log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
3822 return -ELOOP;
3823 }
3824
23a177ef 3825 merged = u;
9946996c
LP
3826 r = merge_by_names(&merged, symlink_names, id);
3827 if (r < 0)
e48614c4 3828 return r;
87f0e418 3829
23a177ef 3830 if (merged != u) {
ac155bb8 3831 u->load_state = UNIT_MERGED;
e48614c4 3832 return 0;
034c6ed7
LP
3833 }
3834
e48614c4
ZJS
3835 if (fstat(fileno(f), &st) < 0)
3836 return -errno;
45fb0699 3837
3a8db9fe 3838 if (null_or_empty(&st)) {
ac155bb8 3839 u->load_state = UNIT_MASKED;
3a8db9fe
ZJS
3840 u->fragment_mtime = 0;
3841 } else {
c2756a68 3842 u->load_state = UNIT_LOADED;
3a8db9fe 3843 u->fragment_mtime = timespec_load(&st.st_mtim);
c2756a68 3844
00dc5d76 3845 /* Now, parse the file contents */
36f822c4
ZJS
3846 r = config_parse(u->id, filename, f,
3847 UNIT_VTABLE(u)->sections,
3848 config_item_perf_lookup, load_fragment_gperf_lookup,
3849 false, true, false, u);
f975e971 3850 if (r < 0)
e48614c4 3851 return r;
00dc5d76 3852 }
b08d03ff 3853
ac155bb8
MS
3854 free(u->fragment_path);
3855 u->fragment_path = filename;
0301abf4 3856 filename = NULL;
87f0e418 3857
1b64d026
LP
3858 if (u->source_path) {
3859 if (stat(u->source_path, &st) >= 0)
3860 u->source_mtime = timespec_load(&st.st_mtim);
3861 else
3862 u->source_mtime = 0;
3863 }
3864
e48614c4 3865 return 0;
0301abf4
LP
3866}
3867
e537352b 3868int unit_load_fragment(Unit *u) {
23a177ef 3869 int r;
294d81f1
LP
3870 Iterator i;
3871 const char *t;
0301abf4
LP
3872
3873 assert(u);
ac155bb8
MS
3874 assert(u->load_state == UNIT_STUB);
3875 assert(u->id);
23a177ef 3876
3f5e8115
LP
3877 if (u->transient) {
3878 u->load_state = UNIT_LOADED;
3879 return 0;
3880 }
3881
294d81f1
LP
3882 /* First, try to find the unit under its id. We always look
3883 * for unit files in the default directories, to make it easy
3884 * to override things by placing things in /etc/systemd/system */
9946996c
LP
3885 r = load_from_path(u, u->id);
3886 if (r < 0)
294d81f1
LP
3887 return r;
3888
3889 /* Try to find an alias we can load this with */
abc08d4d 3890 if (u->load_state == UNIT_STUB) {
ac155bb8 3891 SET_FOREACH(t, u->names, i) {
294d81f1 3892
ac155bb8 3893 if (t == u->id)
294d81f1
LP
3894 continue;
3895
9946996c
LP
3896 r = load_from_path(u, t);
3897 if (r < 0)
294d81f1
LP
3898 return r;
3899
ac155bb8 3900 if (u->load_state != UNIT_STUB)
294d81f1
LP
3901 break;
3902 }
abc08d4d 3903 }
23a177ef 3904
294d81f1 3905 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 3906 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 3907
9946996c
LP
3908 r = load_from_path(u, u->fragment_path);
3909 if (r < 0)
23a177ef 3910 return r;
0301abf4 3911
ece174c5 3912 if (u->load_state == UNIT_STUB)
6ccb1b44
LP
3913 /* Hmm, this didn't work? Then let's get rid
3914 * of the fragment path stored for us, so that
3915 * we don't point to an invalid location. */
a1e58e8e 3916 u->fragment_path = mfree(u->fragment_path);
6ccb1b44
LP
3917 }
3918
294d81f1 3919 /* Look for a template */
ac155bb8 3920 if (u->load_state == UNIT_STUB && u->instance) {
7410616c 3921 _cleanup_free_ char *k = NULL;
294d81f1 3922
7410616c
LP
3923 r = unit_name_template(u->id, &k);
3924 if (r < 0)
3925 return r;
294d81f1
LP
3926
3927 r = load_from_path(u, k);
294d81f1 3928 if (r < 0)
9e2f7c11 3929 return r;
890f434c 3930
abc08d4d 3931 if (u->load_state == UNIT_STUB) {
ac155bb8 3932 SET_FOREACH(t, u->names, i) {
bc9fd78c 3933 _cleanup_free_ char *z = NULL;
87f0e418 3934
ac155bb8 3935 if (t == u->id)
23a177ef 3936 continue;
071830ff 3937
7410616c
LP
3938 r = unit_name_template(t, &z);
3939 if (r < 0)
3940 return r;
294d81f1 3941
bc9fd78c 3942 r = load_from_path(u, z);
294d81f1 3943 if (r < 0)
23a177ef 3944 return r;
890f434c 3945
ac155bb8 3946 if (u->load_state != UNIT_STUB)
23a177ef
LP
3947 break;
3948 }
abc08d4d 3949 }
071830ff
LP
3950 }
3951
23a177ef 3952 return 0;
3efd4195 3953}
e537352b
LP
3954
3955void unit_dump_config_items(FILE *f) {
f975e971
LP
3956 static const struct {
3957 const ConfigParserCallback callback;
3958 const char *rvalue;
3959 } table[] = {
7f8aa671 3960#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
17df7223
LP
3961 { config_parse_warn_compat, "NOTSUPPORTED" },
3962#endif
f975e971
LP
3963 { config_parse_int, "INTEGER" },
3964 { config_parse_unsigned, "UNSIGNED" },
5556b5fe 3965 { config_parse_iec_size, "SIZE" },
59f448cf 3966 { config_parse_iec_uint64, "SIZE" },
5556b5fe 3967 { config_parse_si_size, "SIZE" },
f975e971
LP
3968 { config_parse_bool, "BOOLEAN" },
3969 { config_parse_string, "STRING" },
3970 { config_parse_path, "PATH" },
3971 { config_parse_unit_path_printf, "PATH" },
3972 { config_parse_strv, "STRING [...]" },
3973 { config_parse_exec_nice, "NICE" },
3974 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
3975 { config_parse_exec_io_class, "IOCLASS" },
3976 { config_parse_exec_io_priority, "IOPRIORITY" },
3977 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
3978 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
3979 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
3980 { config_parse_mode, "MODE" },
3981 { config_parse_unit_env_file, "FILE" },
3982 { config_parse_output, "OUTPUT" },
3983 { config_parse_input, "INPUT" },
ca37242e
LP
3984 { config_parse_log_facility, "FACILITY" },
3985 { config_parse_log_level, "LEVEL" },
f975e971 3986 { config_parse_exec_secure_bits, "SECUREBITS" },
a103496c 3987 { config_parse_capability_set, "BOUNDINGSET" },
f975e971 3988 { config_parse_limit, "LIMIT" },
f975e971 3989 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
3990 { config_parse_exec, "PATH [ARGUMENT [...]]" },
3991 { config_parse_service_type, "SERVICETYPE" },
3992 { config_parse_service_restart, "SERVICERESTART" },
3993#ifdef HAVE_SYSV_COMPAT
3994 { config_parse_sysv_priority, "SYSVPRIORITY" },
f975e971
LP
3995#endif
3996 { config_parse_kill_mode, "KILLMODE" },
f757855e 3997 { config_parse_signal, "SIGNAL" },
f975e971
LP
3998 { config_parse_socket_listen, "SOCKET [...]" },
3999 { config_parse_socket_bind, "SOCKETBIND" },
4000 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 4001 { config_parse_sec, "SECONDS" },
d88a251b 4002 { config_parse_nsec, "NANOSECONDS" },
94828d2d 4003 { config_parse_namespace_path_strv, "PATH [...]" },
7c8fa05c 4004 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
4005 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
4006 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 4007 { config_parse_trigger_unit, "UNIT" },
f975e971 4008 { config_parse_timer, "TIMER" },
f975e971 4009 { config_parse_path_spec, "PATH" },
f975e971
LP
4010 { config_parse_notify_access, "ACCESS" },
4011 { config_parse_ip_tos, "TOS" },
4012 { config_parse_unit_condition_path, "CONDITION" },
4013 { config_parse_unit_condition_string, "CONDITION" },
4014 { config_parse_unit_condition_null, "CONDITION" },
a016b922 4015 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
4016 { config_parse_documentation, "URL" },
4017 { config_parse_service_timeout, "SECONDS" },
bf500566 4018 { config_parse_failure_action, "ACTION" },
7f0386f6
LP
4019 { config_parse_set_status, "STATUS" },
4020 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 4021 { config_parse_environ, "ENVIRON" },
c0467cf3 4022#ifdef HAVE_SECCOMP
17df7223 4023 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 4024 { config_parse_syscall_archs, "ARCHS" },
17df7223 4025 { config_parse_syscall_errno, "ERRNO" },
4298d0b5 4026 { config_parse_address_families, "FAMILIES" },
c0467cf3 4027#endif
7f0386f6
LP
4028 { config_parse_cpu_shares, "SHARES" },
4029 { config_parse_memory_limit, "LIMIT" },
4030 { config_parse_device_allow, "DEVICE" },
4031 { config_parse_device_policy, "POLICY" },
13c31542
TH
4032 { config_parse_io_limit, "LIMIT" },
4033 { config_parse_io_weight, "WEIGHT" },
4034 { config_parse_io_device_weight, "DEVICEWEIGHT" },
7f0386f6
LP
4035 { config_parse_blockio_bandwidth, "BANDWIDTH" },
4036 { config_parse_blockio_weight, "WEIGHT" },
4037 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
4038 { config_parse_long, "LONG" },
4039 { config_parse_socket_service, "SERVICE" },
6a6751fe
LP
4040#ifdef HAVE_SELINUX
4041 { config_parse_exec_selinux_context, "LABEL" },
4042#endif
4043 { config_parse_job_mode, "MODE" },
4044 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 4045 { config_parse_personality, "PERSONALITY" },
f975e971
LP
4046 };
4047
4048 const char *prev = NULL;
4049 const char *i;
4050
4051 assert(f);
e537352b 4052
f975e971
LP
4053 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
4054 const char *rvalue = "OTHER", *lvalue;
4055 unsigned j;
4056 size_t prefix_len;
4057 const char *dot;
4058 const ConfigPerfItem *p;
4059
4060 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
4061
4062 dot = strchr(i, '.');
4063 lvalue = dot ? dot + 1 : i;
4064 prefix_len = dot-i;
4065
4066 if (dot)
641906e9 4067 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
4068 if (prev)
4069 fputc('\n', f);
4070
4071 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
4072 }
4073
4074 for (j = 0; j < ELEMENTSOF(table); j++)
4075 if (p->parse == table[j].callback) {
4076 rvalue = table[j].rvalue;
4077 break;
4078 }
4079
4080 fprintf(f, "%s=%s\n", lvalue, rvalue);
4081 prev = i;
4082 }
e537352b 4083}