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