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