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