]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
core: add support for IOReadIOPSMax and IOWriteIOPSMax
[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;
3211 CGroupBlockIODeviceBandwidth *b;
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)) {
47c0980d
G
3226 CGroupBlockIODeviceBandwidth *next;
3227
3228 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
3229 if (b->read == read)
3230 cgroup_context_free_blockio_device_bandwidth(c, b);
4ad49000
LP
3231
3232 return 0;
3233 }
3234
3235 n = strcspn(rvalue, WHITESPACE);
3236 bandwidth = rvalue + n;
3237 bandwidth += strspn(bandwidth, WHITESPACE);
3238
3239 if (!*bandwidth) {
12ca818f 3240 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
4ad49000
LP
3241 return 0;
3242 }
3243
3244 path = strndup(rvalue, n);
3245 if (!path)
3246 return log_oom();
3247
3248 if (!path_startswith(path, "/dev")) {
12ca818f 3249 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
3250 return 0;
3251 }
3252
5556b5fe 3253 r = parse_size(bandwidth, 1000, &bytes);
4ad49000 3254 if (r < 0 || bytes <= 0) {
12ca818f 3255 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
3256 return 0;
3257 }
3258
3259 b = new0(CGroupBlockIODeviceBandwidth, 1);
3260 if (!b)
3261 return log_oom();
3262
3263 b->path = path;
3264 path = NULL;
59f448cf 3265 b->bandwidth = bytes;
47c0980d 3266 b->read = read;
4ad49000 3267
71fda00f 3268 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
4ad49000
LP
3269
3270 return 0;
3271}
3272
d420282b
LP
3273DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
3274
3275int config_parse_job_mode_isolate(
3276 const char *unit,
3277 const char *filename,
3278 unsigned line,
3279 const char *section,
3280 unsigned section_line,
3281 const char *lvalue,
3282 int ltype,
3283 const char *rvalue,
3284 void *data,
3285 void *userdata) {
3286
3287 JobMode *m = data;
3288 int r;
3289
3290 assert(filename);
3291 assert(lvalue);
3292 assert(rvalue);
3293
3294 r = parse_boolean(rvalue);
3295 if (r < 0) {
12ca818f 3296 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
d420282b
LP
3297 return 0;
3298 }
3299
3300 *m = r ? JOB_ISOLATE : JOB_REPLACE;
3301 return 0;
3302}
3303
e66cf1a3
LP
3304int config_parse_runtime_directory(
3305 const char *unit,
3306 const char *filename,
3307 unsigned line,
3308 const char *section,
3309 unsigned section_line,
3310 const char *lvalue,
3311 int ltype,
3312 const char *rvalue,
3313 void *data,
3314 void *userdata) {
3315
a2a5291b 3316 char***rt = data;
9b5864d9 3317 Unit *u = userdata;
a2a5291b 3318 const char *word, *state;
e66cf1a3
LP
3319 size_t l;
3320 int r;
3321
3322 assert(filename);
3323 assert(lvalue);
3324 assert(rvalue);
3325 assert(data);
3326
3327 if (isempty(rvalue)) {
3328 /* Empty assignment resets the list */
6796073e 3329 *rt = strv_free(*rt);
e66cf1a3
LP
3330 return 0;
3331 }
3332
a2a5291b 3333 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
9b5864d9 3334 _cleanup_free_ char *t = NULL, *n = NULL;
e66cf1a3 3335
9b5864d9
MG
3336 t = strndup(word, l);
3337 if (!t)
e66cf1a3
LP
3338 return log_oom();
3339
9b5864d9
MG
3340 r = unit_name_printf(u, t, &n);
3341 if (r < 0) {
12ca818f 3342 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
9b5864d9
MG
3343 continue;
3344 }
3345
ae6c3cc0 3346 if (!filename_is_valid(n)) {
12ca818f 3347 log_syntax(unit, LOG_ERR, filename, line, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
e66cf1a3
LP
3348 continue;
3349 }
3350
3351 r = strv_push(rt, n);
3352 if (r < 0)
3353 return log_oom();
3354
3355 n = NULL;
3356 }
b2fadec6 3357 if (!isempty(state))
12ca818f 3358 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
e66cf1a3
LP
3359
3360 return 0;
3361}
3362
3af00fb8
LP
3363int config_parse_set_status(
3364 const char *unit,
3365 const char *filename,
3366 unsigned line,
3367 const char *section,
3368 unsigned section_line,
3369 const char *lvalue,
3370 int ltype,
3371 const char *rvalue,
3372 void *data,
3373 void *userdata) {
3374
3af00fb8 3375 size_t l;
a2a5291b 3376 const char *word, *state;
3af00fb8
LP
3377 int r;
3378 ExitStatusSet *status_set = data;
3379
3380 assert(filename);
3381 assert(lvalue);
3382 assert(rvalue);
3383 assert(data);
3384
3e2d435b 3385 /* Empty assignment resets the list */
3af00fb8 3386 if (isempty(rvalue)) {
3e2d435b 3387 exit_status_set_free(status_set);
3af00fb8
LP
3388 return 0;
3389 }
3390
a2a5291b 3391 FOREACH_WORD(word, l, rvalue, state) {
3af00fb8
LP
3392 _cleanup_free_ char *temp;
3393 int val;
61593865 3394 Set **set;
3af00fb8 3395
a2a5291b 3396 temp = strndup(word, l);
3af00fb8
LP
3397 if (!temp)
3398 return log_oom();
3399
3400 r = safe_atoi(temp, &val);
3401 if (r < 0) {
3402 val = signal_from_string_try_harder(temp);
3403
1e2fd62d 3404 if (val <= 0) {
12ca818f 3405 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
61593865 3406 continue;
3af00fb8 3407 }
61593865 3408 set = &status_set->signal;
3af00fb8 3409 } else {
1e2fd62d 3410 if (val < 0 || val > 255) {
12ca818f 3411 log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
1e2fd62d 3412 continue;
3af00fb8 3413 }
61593865 3414 set = &status_set->status;
3af00fb8 3415 }
1e2fd62d 3416
61593865 3417 r = set_ensure_allocated(set, NULL);
1e2fd62d
ZJS
3418 if (r < 0)
3419 return log_oom();
3420
61593865 3421 r = set_put(*set, INT_TO_PTR(val));
1e2fd62d 3422 if (r < 0) {
12ca818f 3423 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
1e2fd62d
ZJS
3424 return r;
3425 }
3af00fb8 3426 }
b2fadec6 3427 if (!isempty(state))
12ca818f 3428 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3af00fb8
LP
3429
3430 return 0;
3431}
3432
94828d2d
LP
3433int config_parse_namespace_path_strv(
3434 const char *unit,
3435 const char *filename,
3436 unsigned line,
3437 const char *section,
3438 unsigned section_line,
3439 const char *lvalue,
3440 int ltype,
3441 const char *rvalue,
3442 void *data,
3443 void *userdata) {
3444
a2a5291b 3445 char*** sv = data;
727f76d7
EV
3446 const char *prev;
3447 const char *cur;
94828d2d
LP
3448 int r;
3449
3450 assert(filename);
3451 assert(lvalue);
3452 assert(rvalue);
3453 assert(data);
3454
3455 if (isempty(rvalue)) {
3456 /* Empty assignment resets the list */
6796073e 3457 *sv = strv_free(*sv);
94828d2d
LP
3458 return 0;
3459 }
3460
727f76d7
EV
3461 prev = cur = rvalue;
3462 for (;;) {
3463 _cleanup_free_ char *word = NULL;
94828d2d
LP
3464 int offset;
3465
727f76d7 3466 r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
0293a7a8
EV
3467 if (r == 0)
3468 break;
3469 if (r == -ENOMEM)
3470 return log_oom();
727f76d7 3471 if (r < 0) {
0293a7a8 3472 log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage, ignoring: %s", prev);
727f76d7
EV
3473 return 0;
3474 }
94828d2d 3475
727f76d7
EV
3476 if (!utf8_is_valid(word)) {
3477 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
3478 prev = cur;
94828d2d
LP
3479 continue;
3480 }
3481
727f76d7
EV
3482 offset = word[0] == '-';
3483 if (!path_is_absolute(word + offset)) {
3484 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", word);
3485 prev = cur;
94828d2d
LP
3486 continue;
3487 }
3488
727f76d7 3489 path_kill_slashes(word + offset);
94828d2d 3490
727f76d7 3491 r = strv_push(sv, word);
94828d2d
LP
3492 if (r < 0)
3493 return log_oom();
3494
727f76d7
EV
3495 prev = cur;
3496 word = NULL;
94828d2d
LP
3497 }
3498
3499 return 0;
3500}
3501
f1721625 3502int config_parse_no_new_privileges(
760b9d7c
LP
3503 const char* unit,
3504 const char *filename,
3505 unsigned line,
3506 const char *section,
3507 unsigned section_line,
3508 const char *lvalue,
3509 int ltype,
3510 const char *rvalue,
3511 void *data,
3512 void *userdata) {
3513
3514 ExecContext *c = data;
3515 int k;
3516
3517 assert(filename);
3518 assert(lvalue);
3519 assert(rvalue);
3520 assert(data);
3521
3522 k = parse_boolean(rvalue);
3523 if (k < 0) {
12ca818f 3524 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
760b9d7c
LP
3525 return 0;
3526 }
3527
3528 c->no_new_privileges = !!k;
3529 c->no_new_privileges_set = true;
3530
3531 return 0;
3532}
3533
1b8689f9 3534int config_parse_protect_home(
417116f2
LP
3535 const char* unit,
3536 const char *filename,
3537 unsigned line,
3538 const char *section,
3539 unsigned section_line,
3540 const char *lvalue,
3541 int ltype,
3542 const char *rvalue,
3543 void *data,
3544 void *userdata) {
3545
3546 ExecContext *c = data;
3547 int k;
3548
3549 assert(filename);
3550 assert(lvalue);
3551 assert(rvalue);
3552 assert(data);
3553
3554 /* Our enum shall be a superset of booleans, hence first try
3555 * to parse as as boolean, and then as enum */
3556
3557 k = parse_boolean(rvalue);
3558 if (k > 0)
1b8689f9 3559 c->protect_home = PROTECT_HOME_YES;
417116f2 3560 else if (k == 0)
1b8689f9 3561 c->protect_home = PROTECT_HOME_NO;
417116f2 3562 else {
1b8689f9 3563 ProtectHome h;
417116f2 3564
1b8689f9 3565 h = protect_home_from_string(rvalue);
9ed794a3 3566 if (h < 0) {
12ca818f 3567 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
417116f2
LP
3568 return 0;
3569 }
3570
1b8689f9
LP
3571 c->protect_home = h;
3572 }
3573
3574 return 0;
3575}
3576
3577int config_parse_protect_system(
3578 const char* unit,
3579 const char *filename,
3580 unsigned line,
3581 const char *section,
3582 unsigned section_line,
3583 const char *lvalue,
3584 int ltype,
3585 const char *rvalue,
3586 void *data,
3587 void *userdata) {
3588
3589 ExecContext *c = data;
3590 int k;
3591
3592 assert(filename);
3593 assert(lvalue);
3594 assert(rvalue);
3595 assert(data);
3596
3597 /* Our enum shall be a superset of booleans, hence first try
3598 * to parse as as boolean, and then as enum */
3599
3600 k = parse_boolean(rvalue);
3601 if (k > 0)
3602 c->protect_system = PROTECT_SYSTEM_YES;
3603 else if (k == 0)
3604 c->protect_system = PROTECT_SYSTEM_NO;
3605 else {
3606 ProtectSystem s;
3607
3608 s = protect_system_from_string(rvalue);
9ed794a3 3609 if (s < 0) {
12ca818f 3610 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
1b8689f9
LP
3611 return 0;
3612 }
3613
3614 c->protect_system = s;
417116f2
LP
3615 }
3616
3617 return 0;
3618}
3619
071830ff 3620#define FOLLOW_MAX 8
87f0e418 3621
9e2f7c11 3622static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
a837f088 3623 char *id = NULL;
0301abf4 3624 unsigned c = 0;
87f0e418
LP
3625 int fd, r;
3626 FILE *f;
87f0e418
LP
3627
3628 assert(filename);
3629 assert(*filename);
3630 assert(_f);
3631 assert(names);
3632
0301abf4
LP
3633 /* This will update the filename pointer if the loaded file is
3634 * reached by a symlink. The old string will be freed. */
87f0e418 3635
0301abf4 3636 for (;;) {
2c7108c4 3637 char *target, *name;
87f0e418 3638
0301abf4
LP
3639 if (c++ >= FOLLOW_MAX)
3640 return -ELOOP;
3641
b08d03ff
LP
3642 path_kill_slashes(*filename);
3643
87f0e418 3644 /* Add the file name we are currently looking at to
8f05424d
LP
3645 * the names of this unit, but only if it is a valid
3646 * unit name. */
2b6bf07d 3647 name = basename(*filename);
7410616c 3648 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
8f05424d 3649
15e11d81
LP
3650 id = set_get(names, name);
3651 if (!id) {
3652 id = strdup(name);
3653 if (!id)
8f05424d 3654 return -ENOMEM;
87f0e418 3655
ef42202a
ZJS
3656 r = set_consume(names, id);
3657 if (r < 0)
8f05424d 3658 return r;
87f0e418 3659 }
87f0e418
LP
3660 }
3661
0301abf4 3662 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
3663 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
3664 if (fd >= 0)
87f0e418
LP
3665 break;
3666
0301abf4
LP
3667 if (errno != ELOOP)
3668 return -errno;
3669
87f0e418 3670 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
3671 r = readlink_and_make_absolute(*filename, &target);
3672 if (r < 0)
0301abf4 3673 return r;
87f0e418 3674
0301abf4 3675 free(*filename);
2c7108c4 3676 *filename = target;
87f0e418
LP
3677 }
3678
9946996c
LP
3679 f = fdopen(fd, "re");
3680 if (!f) {
03e334a1 3681 safe_close(fd);
d4ad27a1 3682 return -errno;
87f0e418
LP
3683 }
3684
3685 *_f = f;
9e2f7c11 3686 *_final = id;
a837f088 3687
0301abf4 3688 return 0;
87f0e418
LP
3689}
3690
23a177ef
LP
3691static int merge_by_names(Unit **u, Set *names, const char *id) {
3692 char *k;
3693 int r;
3694
3695 assert(u);
3696 assert(*u);
3697 assert(names);
3698
3699 /* Let's try to add in all symlink names we found */
3700 while ((k = set_steal_first(names))) {
3701
3702 /* First try to merge in the other name into our
3703 * unit */
9946996c
LP
3704 r = unit_merge_by_name(*u, k);
3705 if (r < 0) {
23a177ef
LP
3706 Unit *other;
3707
3708 /* Hmm, we couldn't merge the other unit into
3709 * ours? Then let's try it the other way
3710 * round */
3711
7aad67e7
MS
3712 /* If the symlink name we are looking at is unit template, then
3713 we must search for instance of this template */
3714 if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE)) {
3715 _cleanup_free_ char *instance = NULL;
3716
3717 r = unit_name_replace_instance(k, (*u)->instance, &instance);
3718 if (r < 0)
3719 return r;
3720
3721 other = manager_get_unit((*u)->manager, instance);
3722 } else
3723 other = manager_get_unit((*u)->manager, k);
3724
23a177ef
LP
3725 free(k);
3726
9946996c
LP
3727 if (other) {
3728 r = unit_merge(other, *u);
3729 if (r >= 0) {
23a177ef
LP
3730 *u = other;
3731 return merge_by_names(u, names, NULL);
3732 }
9946996c 3733 }
23a177ef
LP
3734
3735 return r;
3736 }
3737
3738 if (id == k)
3739 unit_choose_id(*u, id);
3740
3741 free(k);
3742 }
3743
3744 return 0;
3745}
3746
e537352b 3747static int load_from_path(Unit *u, const char *path) {
e48614c4
ZJS
3748 _cleanup_set_free_free_ Set *symlink_names = NULL;
3749 _cleanup_fclose_ FILE *f = NULL;
3750 _cleanup_free_ char *filename = NULL;
3751 char *id = NULL;
23a177ef 3752 Unit *merged;
45fb0699 3753 struct stat st;
a837f088 3754 int r;
23a177ef
LP
3755
3756 assert(u);
e537352b 3757 assert(path);
3efd4195 3758
d5099efc 3759 symlink_names = set_new(&string_hash_ops);
f975e971 3760 if (!symlink_names)
87f0e418 3761 return -ENOMEM;
3efd4195 3762
036643a2
LP
3763 if (path_is_absolute(path)) {
3764
9946996c 3765 filename = strdup(path);
e48614c4
ZJS
3766 if (!filename)
3767 return -ENOMEM;
036643a2 3768
9946996c
LP
3769 r = open_follow(&filename, &f, symlink_names, &id);
3770 if (r < 0) {
97b11eed 3771 filename = mfree(filename);
036643a2 3772 if (r != -ENOENT)
e48614c4 3773 return r;
036643a2
LP
3774 }
3775
3776 } else {
3777 char **p;
3778
a3c4eb07 3779 STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
036643a2
LP
3780
3781 /* Instead of opening the path right away, we manually
3782 * follow all symlinks and add their name to our unit
3783 * name set while doing so */
9946996c 3784 filename = path_make_absolute(path, *p);
e48614c4
ZJS
3785 if (!filename)
3786 return -ENOMEM;
036643a2 3787
ac155bb8
MS
3788 if (u->manager->unit_path_cache &&
3789 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
3790 r = -ENOENT;
3791 else
3792 r = open_follow(&filename, &f, symlink_names, &id);
a837f088
LP
3793 if (r >= 0)
3794 break;
3795 filename = mfree(filename);
3796 if (r != -ENOENT)
3797 return r;
fe51822e 3798
a837f088
LP
3799 /* Empty the symlink names for the next run */
3800 set_clear_free(symlink_names);
036643a2
LP
3801 }
3802 }
034c6ed7 3803
e48614c4 3804 if (!filename)
8f05424d 3805 /* Hmm, no suitable file found? */
e48614c4 3806 return 0;
87f0e418 3807
8a993b61 3808 if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
a837f088
LP
3809 log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
3810 return -ELOOP;
3811 }
3812
23a177ef 3813 merged = u;
9946996c
LP
3814 r = merge_by_names(&merged, symlink_names, id);
3815 if (r < 0)
e48614c4 3816 return r;
87f0e418 3817
23a177ef 3818 if (merged != u) {
ac155bb8 3819 u->load_state = UNIT_MERGED;
e48614c4 3820 return 0;
034c6ed7
LP
3821 }
3822
e48614c4
ZJS
3823 if (fstat(fileno(f), &st) < 0)
3824 return -errno;
45fb0699 3825
3a8db9fe 3826 if (null_or_empty(&st)) {
ac155bb8 3827 u->load_state = UNIT_MASKED;
3a8db9fe
ZJS
3828 u->fragment_mtime = 0;
3829 } else {
c2756a68 3830 u->load_state = UNIT_LOADED;
3a8db9fe 3831 u->fragment_mtime = timespec_load(&st.st_mtim);
c2756a68 3832
00dc5d76 3833 /* Now, parse the file contents */
36f822c4
ZJS
3834 r = config_parse(u->id, filename, f,
3835 UNIT_VTABLE(u)->sections,
3836 config_item_perf_lookup, load_fragment_gperf_lookup,
3837 false, true, false, u);
f975e971 3838 if (r < 0)
e48614c4 3839 return r;
00dc5d76 3840 }
b08d03ff 3841
ac155bb8
MS
3842 free(u->fragment_path);
3843 u->fragment_path = filename;
0301abf4 3844 filename = NULL;
87f0e418 3845
1b64d026
LP
3846 if (u->source_path) {
3847 if (stat(u->source_path, &st) >= 0)
3848 u->source_mtime = timespec_load(&st.st_mtim);
3849 else
3850 u->source_mtime = 0;
3851 }
3852
e48614c4 3853 return 0;
0301abf4
LP
3854}
3855
e537352b 3856int unit_load_fragment(Unit *u) {
23a177ef 3857 int r;
294d81f1
LP
3858 Iterator i;
3859 const char *t;
0301abf4
LP
3860
3861 assert(u);
ac155bb8
MS
3862 assert(u->load_state == UNIT_STUB);
3863 assert(u->id);
23a177ef 3864
3f5e8115
LP
3865 if (u->transient) {
3866 u->load_state = UNIT_LOADED;
3867 return 0;
3868 }
3869
294d81f1
LP
3870 /* First, try to find the unit under its id. We always look
3871 * for unit files in the default directories, to make it easy
3872 * to override things by placing things in /etc/systemd/system */
9946996c
LP
3873 r = load_from_path(u, u->id);
3874 if (r < 0)
294d81f1
LP
3875 return r;
3876
3877 /* Try to find an alias we can load this with */
abc08d4d 3878 if (u->load_state == UNIT_STUB) {
ac155bb8 3879 SET_FOREACH(t, u->names, i) {
294d81f1 3880
ac155bb8 3881 if (t == u->id)
294d81f1
LP
3882 continue;
3883
9946996c
LP
3884 r = load_from_path(u, t);
3885 if (r < 0)
294d81f1
LP
3886 return r;
3887
ac155bb8 3888 if (u->load_state != UNIT_STUB)
294d81f1
LP
3889 break;
3890 }
abc08d4d 3891 }
23a177ef 3892
294d81f1 3893 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 3894 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 3895
9946996c
LP
3896 r = load_from_path(u, u->fragment_path);
3897 if (r < 0)
23a177ef 3898 return r;
0301abf4 3899
ece174c5 3900 if (u->load_state == UNIT_STUB)
6ccb1b44
LP
3901 /* Hmm, this didn't work? Then let's get rid
3902 * of the fragment path stored for us, so that
3903 * we don't point to an invalid location. */
a1e58e8e 3904 u->fragment_path = mfree(u->fragment_path);
6ccb1b44
LP
3905 }
3906
294d81f1 3907 /* Look for a template */
ac155bb8 3908 if (u->load_state == UNIT_STUB && u->instance) {
7410616c 3909 _cleanup_free_ char *k = NULL;
294d81f1 3910
7410616c
LP
3911 r = unit_name_template(u->id, &k);
3912 if (r < 0)
3913 return r;
294d81f1
LP
3914
3915 r = load_from_path(u, k);
294d81f1 3916 if (r < 0)
9e2f7c11 3917 return r;
890f434c 3918
abc08d4d 3919 if (u->load_state == UNIT_STUB) {
ac155bb8 3920 SET_FOREACH(t, u->names, i) {
bc9fd78c 3921 _cleanup_free_ char *z = NULL;
87f0e418 3922
ac155bb8 3923 if (t == u->id)
23a177ef 3924 continue;
071830ff 3925
7410616c
LP
3926 r = unit_name_template(t, &z);
3927 if (r < 0)
3928 return r;
294d81f1 3929
bc9fd78c 3930 r = load_from_path(u, z);
294d81f1 3931 if (r < 0)
23a177ef 3932 return r;
890f434c 3933
ac155bb8 3934 if (u->load_state != UNIT_STUB)
23a177ef
LP
3935 break;
3936 }
abc08d4d 3937 }
071830ff
LP
3938 }
3939
23a177ef 3940 return 0;
3efd4195 3941}
e537352b
LP
3942
3943void unit_dump_config_items(FILE *f) {
f975e971
LP
3944 static const struct {
3945 const ConfigParserCallback callback;
3946 const char *rvalue;
3947 } table[] = {
7f8aa671 3948#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
17df7223
LP
3949 { config_parse_warn_compat, "NOTSUPPORTED" },
3950#endif
f975e971
LP
3951 { config_parse_int, "INTEGER" },
3952 { config_parse_unsigned, "UNSIGNED" },
5556b5fe 3953 { config_parse_iec_size, "SIZE" },
59f448cf 3954 { config_parse_iec_uint64, "SIZE" },
5556b5fe 3955 { config_parse_si_size, "SIZE" },
f975e971
LP
3956 { config_parse_bool, "BOOLEAN" },
3957 { config_parse_string, "STRING" },
3958 { config_parse_path, "PATH" },
3959 { config_parse_unit_path_printf, "PATH" },
3960 { config_parse_strv, "STRING [...]" },
3961 { config_parse_exec_nice, "NICE" },
3962 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
3963 { config_parse_exec_io_class, "IOCLASS" },
3964 { config_parse_exec_io_priority, "IOPRIORITY" },
3965 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
3966 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
3967 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
3968 { config_parse_mode, "MODE" },
3969 { config_parse_unit_env_file, "FILE" },
3970 { config_parse_output, "OUTPUT" },
3971 { config_parse_input, "INPUT" },
ca37242e
LP
3972 { config_parse_log_facility, "FACILITY" },
3973 { config_parse_log_level, "LEVEL" },
f975e971 3974 { config_parse_exec_secure_bits, "SECUREBITS" },
a103496c 3975 { config_parse_capability_set, "BOUNDINGSET" },
f975e971 3976 { config_parse_limit, "LIMIT" },
f975e971 3977 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
3978 { config_parse_exec, "PATH [ARGUMENT [...]]" },
3979 { config_parse_service_type, "SERVICETYPE" },
3980 { config_parse_service_restart, "SERVICERESTART" },
3981#ifdef HAVE_SYSV_COMPAT
3982 { config_parse_sysv_priority, "SYSVPRIORITY" },
f975e971
LP
3983#endif
3984 { config_parse_kill_mode, "KILLMODE" },
f757855e 3985 { config_parse_signal, "SIGNAL" },
f975e971
LP
3986 { config_parse_socket_listen, "SOCKET [...]" },
3987 { config_parse_socket_bind, "SOCKETBIND" },
3988 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 3989 { config_parse_sec, "SECONDS" },
d88a251b 3990 { config_parse_nsec, "NANOSECONDS" },
94828d2d 3991 { config_parse_namespace_path_strv, "PATH [...]" },
7c8fa05c 3992 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
3993 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
3994 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 3995 { config_parse_trigger_unit, "UNIT" },
f975e971 3996 { config_parse_timer, "TIMER" },
f975e971 3997 { config_parse_path_spec, "PATH" },
f975e971
LP
3998 { config_parse_notify_access, "ACCESS" },
3999 { config_parse_ip_tos, "TOS" },
4000 { config_parse_unit_condition_path, "CONDITION" },
4001 { config_parse_unit_condition_string, "CONDITION" },
4002 { config_parse_unit_condition_null, "CONDITION" },
a016b922 4003 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
4004 { config_parse_documentation, "URL" },
4005 { config_parse_service_timeout, "SECONDS" },
bf500566 4006 { config_parse_failure_action, "ACTION" },
7f0386f6
LP
4007 { config_parse_set_status, "STATUS" },
4008 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 4009 { config_parse_environ, "ENVIRON" },
c0467cf3 4010#ifdef HAVE_SECCOMP
17df7223 4011 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 4012 { config_parse_syscall_archs, "ARCHS" },
17df7223 4013 { config_parse_syscall_errno, "ERRNO" },
4298d0b5 4014 { config_parse_address_families, "FAMILIES" },
c0467cf3 4015#endif
7f0386f6
LP
4016 { config_parse_cpu_shares, "SHARES" },
4017 { config_parse_memory_limit, "LIMIT" },
4018 { config_parse_device_allow, "DEVICE" },
4019 { config_parse_device_policy, "POLICY" },
13c31542
TH
4020 { config_parse_io_limit, "LIMIT" },
4021 { config_parse_io_weight, "WEIGHT" },
4022 { config_parse_io_device_weight, "DEVICEWEIGHT" },
7f0386f6
LP
4023 { config_parse_blockio_bandwidth, "BANDWIDTH" },
4024 { config_parse_blockio_weight, "WEIGHT" },
4025 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
4026 { config_parse_long, "LONG" },
4027 { config_parse_socket_service, "SERVICE" },
6a6751fe
LP
4028#ifdef HAVE_SELINUX
4029 { config_parse_exec_selinux_context, "LABEL" },
4030#endif
4031 { config_parse_job_mode, "MODE" },
4032 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 4033 { config_parse_personality, "PERSONALITY" },
f975e971
LP
4034 };
4035
4036 const char *prev = NULL;
4037 const char *i;
4038
4039 assert(f);
e537352b 4040
f975e971
LP
4041 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
4042 const char *rvalue = "OTHER", *lvalue;
4043 unsigned j;
4044 size_t prefix_len;
4045 const char *dot;
4046 const ConfigPerfItem *p;
4047
4048 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
4049
4050 dot = strchr(i, '.');
4051 lvalue = dot ? dot + 1 : i;
4052 prefix_len = dot-i;
4053
4054 if (dot)
641906e9 4055 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
4056 if (prev)
4057 fputc('\n', f);
4058
4059 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
4060 }
4061
4062 for (j = 0; j < ELEMENTSOF(table); j++)
4063 if (p->parse == table[j].callback) {
4064 rvalue = table[j].rvalue;
4065 break;
4066 }
4067
4068 fprintf(f, "%s=%s\n", lvalue, rvalue);
4069 prev = i;
4070 }
e537352b 4071}