]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
conf-parser: distinguish between multiple sections with the same name
[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>
3efd4195 36
84f6181c 37#include "sd-messages.h"
87f0e418 38#include "unit.h"
3efd4195
LP
39#include "strv.h"
40#include "conf-parser.h"
41#include "load-fragment.h"
16354eff 42#include "log.h"
9eba9da4 43#include "ioprio.h"
94f04347
LP
44#include "securebits.h"
45#include "missing.h"
9e2f7c11 46#include "unit-name.h"
41f9172f 47#include "unit-printf.h"
7f110ff9 48#include "utf8.h"
9eb977db 49#include "path-util.h"
8351ceae 50#include "syscall-list.h"
853b8397 51#include "env-util.h"
4ad49000 52#include "cgroup.h"
718db961
LP
53#include "bus-util.h"
54#include "bus-error.h"
3efd4195 55
07459bb6 56#ifndef HAVE_SYSV_COMPAT
e8e581bf
ZJS
57int config_parse_warn_compat(const char *unit,
58 const char *filename,
59 unsigned line,
60 const char *section,
71a61510 61 unsigned section_line,
e8e581bf
ZJS
62 const char *lvalue,
63 int ltype,
64 const char *rvalue,
65 void *data,
66 void *userdata) {
67
68 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
69 "Support for option %s= has been disabled at compile time and is ignored",
70 lvalue);
07459bb6
FF
71 return 0;
72}
73#endif
74
e8e581bf
ZJS
75int config_parse_unit_deps(const char* unit,
76 const char *filename,
77 unsigned line,
78 const char *section,
71a61510 79 unsigned section_line,
e8e581bf
ZJS
80 const char *lvalue,
81 int ltype,
82 const char *rvalue,
83 void *data,
84 void *userdata) {
3efd4195 85
f975e971 86 UnitDependency d = ltype;
87f0e418 87 Unit *u = userdata;
3efd4195
LP
88 char *w;
89 size_t l;
90 char *state;
91
92 assert(filename);
93 assert(lvalue);
94 assert(rvalue);
3efd4195 95
f60f22df 96 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 97 _cleanup_free_ char *t = NULL, *k = NULL;
3efd4195 98 int r;
3efd4195 99
57020a3a
LP
100 t = strndup(w, l);
101 if (!t)
74051b9b 102 return log_oom();
3efd4195 103
19f6d710
LP
104 r = unit_name_printf(u, t, &k);
105 if (r < 0) {
106 log_syntax(unit, LOG_ERR, filename, line, -r,
107 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
108 continue;
109 }
9e2f7c11 110
701cc384 111 r = unit_add_dependency_by_name(u, d, k, NULL, true);
57020a3a 112 if (r < 0)
e8e581bf
ZJS
113 log_syntax(unit, LOG_ERR, filename, line, -r,
114 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
3efd4195
LP
115 }
116
117 return 0;
118}
119
e8e581bf
ZJS
120int config_parse_unit_string_printf(const char *unit,
121 const char *filename,
122 unsigned line,
123 const char *section,
71a61510 124 unsigned section_line,
e8e581bf
ZJS
125 const char *lvalue,
126 int ltype,
127 const char *rvalue,
128 void *data,
129 void *userdata) {
932921b5
LP
130
131 Unit *u = userdata;
74051b9b 132 _cleanup_free_ char *k = NULL;
19f6d710 133 int r;
932921b5
LP
134
135 assert(filename);
136 assert(lvalue);
137 assert(rvalue);
f2d3769a 138 assert(u);
932921b5 139
19f6d710
LP
140 r = unit_full_printf(u, rvalue, &k);
141 if (r < 0)
142 log_syntax(unit, LOG_ERR, filename, line, -r,
143 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
932921b5 144
71a61510 145 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 146 k ? k : rvalue, data, userdata);
932921b5
LP
147}
148
e8e581bf
ZJS
149int config_parse_unit_strv_printf(const char *unit,
150 const char *filename,
151 unsigned line,
152 const char *section,
71a61510 153 unsigned section_line,
e8e581bf
ZJS
154 const char *lvalue,
155 int ltype,
156 const char *rvalue,
157 void *data,
158 void *userdata) {
8fef7659
LP
159
160 Unit *u = userdata;
74051b9b 161 _cleanup_free_ char *k = NULL;
19f6d710 162 int r;
8fef7659
LP
163
164 assert(filename);
165 assert(lvalue);
166 assert(rvalue);
167 assert(u);
168
19f6d710
LP
169 r = unit_full_printf(u, rvalue, &k);
170 if (r < 0)
171 log_syntax(unit, LOG_ERR, filename, line, -r,
172 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
8fef7659 173
71a61510 174 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 175 k ? k : rvalue, data, userdata);
8fef7659
LP
176}
177
e8e581bf
ZJS
178int config_parse_unit_path_printf(const char *unit,
179 const char *filename,
180 unsigned line,
181 const char *section,
71a61510 182 unsigned section_line,
e8e581bf
ZJS
183 const char *lvalue,
184 int ltype,
185 const char *rvalue,
186 void *data,
187 void *userdata) {
6ea832a2
LP
188
189 Unit *u = userdata;
74051b9b 190 _cleanup_free_ char *k = NULL;
19f6d710 191 int r;
6ea832a2
LP
192
193 assert(filename);
194 assert(lvalue);
195 assert(rvalue);
6ea832a2
LP
196 assert(u);
197
19f6d710
LP
198 r = unit_full_printf(u, rvalue, &k);
199 if (r < 0)
200 log_syntax(unit, LOG_ERR, filename, line, -r,
201 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
6ea832a2 202
71a61510 203 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 204 k ? k : rvalue, data, userdata);
6ea832a2
LP
205}
206
e8e581bf
ZJS
207int config_parse_socket_listen(const char *unit,
208 const char *filename,
209 unsigned line,
210 const char *section,
71a61510 211 unsigned section_line,
e8e581bf
ZJS
212 const char *lvalue,
213 int ltype,
214 const char *rvalue,
215 void *data,
216 void *userdata) {
42f4e3c4 217
49f91047 218 SocketPort *p, *tail;
542563ba 219 Socket *s;
19f6d710 220 int r;
16354eff 221
42f4e3c4
LP
222 assert(filename);
223 assert(lvalue);
224 assert(rvalue);
225 assert(data);
226
595ed347 227 s = SOCKET(data);
542563ba 228
74051b9b
LP
229 if (isempty(rvalue)) {
230 /* An empty assignment removes all ports */
231 socket_free_ports(s);
232 return 0;
233 }
234
7f110ff9
LP
235 p = new0(SocketPort, 1);
236 if (!p)
74051b9b 237 return log_oom();
916abb21 238
74051b9b 239 if (ltype != SOCKET_SOCKET) {
916abb21 240
74051b9b 241 p->type = ltype;
19f6d710
LP
242 r = unit_full_printf(UNIT(s), rvalue, &p->path);
243 if (r < 0) {
487060c2
LP
244 p->path = strdup(rvalue);
245 if (!p->path) {
246 free(p);
247 return log_oom();
248 } else
19f6d710
LP
249 log_syntax(unit, LOG_ERR, filename, line, -r,
250 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
916abb21
LP
251 }
252
253 path_kill_slashes(p->path);
254
7a22745a 255 } else if (streq(lvalue, "ListenNetlink")) {
74051b9b 256 _cleanup_free_ char *k = NULL;
1fd45a90 257
7a22745a 258 p->type = SOCKET_SOCKET;
19f6d710
LP
259 r = unit_full_printf(UNIT(s), rvalue, &k);
260 if (r < 0)
261 log_syntax(unit, LOG_ERR, filename, line, -r,
262 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
7a22745a 263
487060c2 264 r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
1fd45a90 265 if (r < 0) {
19f6d710 266 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf 267 "Failed to parse address value, ignoring: %s", rvalue);
7a22745a
LP
268 free(p);
269 return 0;
270 }
271
542563ba 272 } else {
74051b9b 273 _cleanup_free_ char *k = NULL;
1fd45a90 274
542563ba 275 p->type = SOCKET_SOCKET;
19f6d710
LP
276 r = unit_full_printf(UNIT(s), rvalue, &k);
277 if (r < 0)
278 log_syntax(unit, LOG_ERR, filename, line, -r,
279 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
542563ba 280
487060c2 281 r = socket_address_parse(&p->address, k ? k : rvalue);
1fd45a90 282 if (r < 0) {
19f6d710 283 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf 284 "Failed to parse address value, ignoring: %s", rvalue);
542563ba 285 free(p);
c0b34696 286 return 0;
542563ba
LP
287 }
288
289 if (streq(lvalue, "ListenStream"))
290 p->address.type = SOCK_STREAM;
291 else if (streq(lvalue, "ListenDatagram"))
292 p->address.type = SOCK_DGRAM;
293 else {
294 assert(streq(lvalue, "ListenSequentialPacket"));
295 p->address.type = SOCK_SEQPACKET;
296 }
297
298 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
e8e581bf
ZJS
299 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
300 "Address family not supported, ignoring: %s", rvalue);
542563ba 301 free(p);
c0b34696 302 return 0;
542563ba 303 }
16354eff
LP
304 }
305
542563ba 306 p->fd = -1;
2e41a51e 307 p->socket = s;
49f91047
LP
308
309 if (s->ports) {
71fda00f
LP
310 LIST_FIND_TAIL(port, s->ports, tail);
311 LIST_INSERT_AFTER(port, s->ports, tail, p);
49f91047 312 } else
71fda00f 313 LIST_PREPEND(port, s->ports, p);
542563ba 314
16354eff 315 return 0;
42f4e3c4
LP
316}
317
e8e581bf
ZJS
318int config_parse_socket_bind(const char *unit,
319 const char *filename,
320 unsigned line,
321 const char *section,
71a61510 322 unsigned section_line,
e8e581bf
ZJS
323 const char *lvalue,
324 int ltype,
325 const char *rvalue,
326 void *data,
327 void *userdata) {
42f4e3c4 328
542563ba 329 Socket *s;
c0120d99 330 SocketAddressBindIPv6Only b;
42f4e3c4
LP
331
332 assert(filename);
333 assert(lvalue);
334 assert(rvalue);
335 assert(data);
336
595ed347 337 s = SOCKET(data);
542563ba 338
5198dabc
LP
339 b = socket_address_bind_ipv6_only_from_string(rvalue);
340 if (b < 0) {
c0120d99
LP
341 int r;
342
5198dabc
LP
343 r = parse_boolean(rvalue);
344 if (r < 0) {
e8e581bf
ZJS
345 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
346 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
c0b34696 347 return 0;
c0120d99 348 }
42f4e3c4 349
c0120d99
LP
350 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
351 } else
352 s->bind_ipv6_only = b;
542563ba 353
42f4e3c4
LP
354 return 0;
355}
356
e8e581bf
ZJS
357int config_parse_exec_nice(const char *unit,
358 const char *filename,
359 unsigned line,
360 const char *section,
71a61510 361 unsigned section_line,
e8e581bf
ZJS
362 const char *lvalue,
363 int ltype,
364 const char *rvalue,
365 void *data,
366 void *userdata) {
034c6ed7 367
fb33a393 368 ExecContext *c = data;
e8e581bf 369 int priority, r;
034c6ed7
LP
370
371 assert(filename);
372 assert(lvalue);
373 assert(rvalue);
374 assert(data);
375
e8e581bf
ZJS
376 r = safe_atoi(rvalue, &priority);
377 if (r < 0) {
378 log_syntax(unit, LOG_ERR, filename, line, -r,
379 "Failed to parse nice priority, ignoring: %s. ", rvalue);
c0b34696 380 return 0;
034c6ed7
LP
381 }
382
383 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
e8e581bf
ZJS
384 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
385 "Nice priority out of range, ignoring: %s", rvalue);
c0b34696 386 return 0;
034c6ed7
LP
387 }
388
fb33a393 389 c->nice = priority;
71155933 390 c->nice_set = true;
fb33a393 391
034c6ed7
LP
392 return 0;
393}
394
e8e581bf
ZJS
395int config_parse_exec_oom_score_adjust(const char* unit,
396 const char *filename,
397 unsigned line,
398 const char *section,
71a61510 399 unsigned section_line,
e8e581bf
ZJS
400 const char *lvalue,
401 int ltype,
402 const char *rvalue,
403 void *data,
404 void *userdata) {
034c6ed7 405
fb33a393 406 ExecContext *c = data;
e8e581bf 407 int oa, r;
034c6ed7
LP
408
409 assert(filename);
410 assert(lvalue);
411 assert(rvalue);
412 assert(data);
413
e8e581bf
ZJS
414 r = safe_atoi(rvalue, &oa);
415 if (r < 0) {
416 log_syntax(unit, LOG_ERR, filename, line, -r,
417 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
c0b34696 418 return 0;
034c6ed7
LP
419 }
420
dd6c17b1 421 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
e8e581bf
ZJS
422 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
423 "OOM score adjust value out of range, ignoring: %s", rvalue);
c0b34696 424 return 0;
034c6ed7
LP
425 }
426
dd6c17b1
LP
427 c->oom_score_adjust = oa;
428 c->oom_score_adjust_set = true;
fb33a393 429
034c6ed7
LP
430 return 0;
431}
432
e8e581bf
ZJS
433int config_parse_exec(const char *unit,
434 const char *filename,
435 unsigned line,
436 const char *section,
71a61510 437 unsigned section_line,
e8e581bf
ZJS
438 const char *lvalue,
439 int ltype,
440 const char *rvalue,
441 void *data,
442 void *userdata) {
034c6ed7 443
61e5d8ed
LP
444 ExecCommand **e = data, *nce;
445 char *path, **n;
034c6ed7 446 unsigned k;
7f110ff9 447 int r;
034c6ed7
LP
448
449 assert(filename);
450 assert(lvalue);
451 assert(rvalue);
61e5d8ed 452 assert(e);
034c6ed7 453
74051b9b
LP
454 e += ltype;
455
456 if (isempty(rvalue)) {
457 /* An empty assignment resets the list */
458 exec_command_free_list(*e);
459 *e = NULL;
460 return 0;
461 }
462
6c666e26
LP
463 /* We accept an absolute path as first argument, or
464 * alternatively an absolute prefixed with @ to allow
465 * overriding of argv[0]. */
61e5d8ed 466 for (;;) {
0f67f1ef 467 int i;
61e5d8ed
LP
468 char *w;
469 size_t l;
470 char *state;
b708e7ce 471 bool honour_argv0 = false, ignore = false;
6c666e26 472
61e5d8ed
LP
473 path = NULL;
474 nce = NULL;
475 n = NULL;
6c666e26 476
61e5d8ed 477 rvalue += strspn(rvalue, WHITESPACE);
034c6ed7 478
61e5d8ed
LP
479 if (rvalue[0] == 0)
480 break;
034c6ed7 481
0f67f1ef
ZJS
482 for (i = 0; i < 2; i++) {
483 if (rvalue[0] == '-' && !ignore) {
484 ignore = true;
485 rvalue ++;
486 }
b708e7ce 487
0f67f1ef
ZJS
488 if (rvalue[0] == '@' && !honour_argv0) {
489 honour_argv0 = true;
490 rvalue ++;
491 }
b708e7ce 492 }
61e5d8ed 493
b708e7ce 494 if (*rvalue != '/') {
e8e581bf
ZJS
495 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
496 "Executable path is not absolute, ignoring: %s", rvalue);
c0b34696 497 return 0;
6c666e26 498 }
034c6ed7 499
61e5d8ed
LP
500 k = 0;
501 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
641906e9 502 if (strneq(w, ";", MAX(l, 1U)))
61e5d8ed 503 break;
034c6ed7 504
61e5d8ed
LP
505 k++;
506 }
034c6ed7 507
7f110ff9
LP
508 n = new(char*, k + !honour_argv0);
509 if (!n)
74051b9b 510 return log_oom();
61e5d8ed
LP
511
512 k = 0;
61e5d8ed 513 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
641906e9 514 if (strneq(w, ";", MAX(l, 1U)))
61e5d8ed 515 break;
641906e9 516 else if (strneq(w, "\\;", MAX(l, 1U)))
7e1a84f5 517 w ++;
61e5d8ed 518
b708e7ce
LP
519 if (honour_argv0 && w == rvalue) {
520 assert(!path);
7f110ff9
LP
521
522 path = strndup(w, l);
523 if (!path) {
74051b9b 524 r = log_oom();
7f110ff9
LP
525 goto fail;
526 }
527
528 if (!utf8_is_valid(path)) {
e8e581bf
ZJS
529 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
530 "Path is not UTF-8 clean, ignoring assignment: %s",
531 rvalue);
7f110ff9 532 r = 0;
61e5d8ed 533 goto fail;
7f110ff9
LP
534 }
535
61e5d8ed 536 } else {
7f110ff9
LP
537 char *c;
538
539 c = n[k++] = cunescape_length(w, l);
540 if (!c) {
74051b9b 541 r = log_oom();
61e5d8ed 542 goto fail;
7f110ff9
LP
543 }
544
545 if (!utf8_is_valid(c)) {
e8e581bf
ZJS
546 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
547 "Path is not UTF-8 clean, ignoring assignment: %s",
548 rvalue);
7f110ff9
LP
549 r = 0;
550 goto fail;
551 }
61e5d8ed
LP
552 }
553 }
554
555 n[k] = NULL;
556
557 if (!n[0]) {
e8e581bf
ZJS
558 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
559 "Invalid command line, ignoring: %s", rvalue);
7f110ff9
LP
560 r = 0;
561 goto fail;
61e5d8ed
LP
562 }
563
7f110ff9
LP
564 if (!path) {
565 path = strdup(n[0]);
566 if (!path) {
74051b9b 567 r = log_oom();
61e5d8ed 568 goto fail;
7f110ff9
LP
569 }
570 }
6c666e26 571
61e5d8ed 572 assert(path_is_absolute(path));
6c666e26 573
7f110ff9
LP
574 nce = new0(ExecCommand, 1);
575 if (!nce) {
74051b9b 576 r = log_oom();
61e5d8ed 577 goto fail;
7f110ff9 578 }
61e5d8ed
LP
579
580 nce->argv = n;
581 nce->path = path;
b708e7ce 582 nce->ignore = ignore;
034c6ed7 583
61e5d8ed 584 path_kill_slashes(nce->path);
034c6ed7 585
61e5d8ed 586 exec_command_append_list(e, nce);
01f78473 587
61e5d8ed
LP
588 rvalue = state;
589 }
034c6ed7
LP
590
591 return 0;
592
593fail:
6c666e26
LP
594 n[k] = NULL;
595 strv_free(n);
596 free(path);
034c6ed7
LP
597 free(nce);
598
7f110ff9 599 return r;
034c6ed7
LP
600}
601
f975e971
LP
602DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
603DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
034c6ed7 604
e8e581bf
ZJS
605int config_parse_socket_bindtodevice(const char* unit,
606 const char *filename,
607 unsigned line,
608 const char *section,
71a61510 609 unsigned section_line,
e8e581bf
ZJS
610 const char *lvalue,
611 int ltype,
612 const char *rvalue,
613 void *data,
614 void *userdata) {
acbb0225
LP
615
616 Socket *s = data;
617 char *n;
618
619 assert(filename);
620 assert(lvalue);
621 assert(rvalue);
622 assert(data);
623
624 if (rvalue[0] && !streq(rvalue, "*")) {
74051b9b
LP
625 n = strdup(rvalue);
626 if (!n)
627 return log_oom();
acbb0225
LP
628 } else
629 n = NULL;
630
631 free(s->bind_to_device);
632 s->bind_to_device = n;
633
634 return 0;
635}
636
f975e971
LP
637DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
638DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
87f0e418 639
e8e581bf
ZJS
640int config_parse_exec_io_class(const char *unit,
641 const char *filename,
642 unsigned line,
643 const char *section,
71a61510 644 unsigned section_line,
e8e581bf
ZJS
645 const char *lvalue,
646 int ltype,
647 const char *rvalue,
648 void *data,
649 void *userdata) {
94f04347
LP
650
651 ExecContext *c = data;
652 int x;
653
654 assert(filename);
655 assert(lvalue);
656 assert(rvalue);
657 assert(data);
658
f8b69d1d
MS
659 x = ioprio_class_from_string(rvalue);
660 if (x < 0) {
e8e581bf
ZJS
661 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
662 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
c0b34696 663 return 0;
0d87eb42 664 }
94f04347
LP
665
666 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
667 c->ioprio_set = true;
668
669 return 0;
670}
671
e8e581bf
ZJS
672int config_parse_exec_io_priority(const char *unit,
673 const char *filename,
674 unsigned line,
675 const char *section,
71a61510 676 unsigned section_line,
e8e581bf
ZJS
677 const char *lvalue,
678 int ltype,
679 const char *rvalue,
680 void *data,
681 void *userdata) {
94f04347
LP
682
683 ExecContext *c = data;
e8e581bf 684 int i, r;
94f04347
LP
685
686 assert(filename);
687 assert(lvalue);
688 assert(rvalue);
689 assert(data);
690
e8e581bf
ZJS
691 r = safe_atoi(rvalue, &i);
692 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
693 log_syntax(unit, LOG_ERR, filename, line, -r,
694 "Failed to parse IO priority, ignoring: %s", rvalue);
c0b34696 695 return 0;
071830ff
LP
696 }
697
94f04347
LP
698 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
699 c->ioprio_set = true;
700
071830ff
LP
701 return 0;
702}
703
e8e581bf
ZJS
704int config_parse_exec_cpu_sched_policy(const char *unit,
705 const char *filename,
706 unsigned line,
707 const char *section,
71a61510 708 unsigned section_line,
e8e581bf
ZJS
709 const char *lvalue,
710 int ltype,
711 const char *rvalue,
712 void *data,
713 void *userdata) {
9eba9da4 714
94f04347
LP
715
716 ExecContext *c = data;
717 int x;
718
719 assert(filename);
720 assert(lvalue);
721 assert(rvalue);
722 assert(data);
723
f8b69d1d
MS
724 x = sched_policy_from_string(rvalue);
725 if (x < 0) {
e8e581bf
ZJS
726 log_syntax(unit, LOG_ERR, filename, line, -x,
727 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 728 return 0;
0d87eb42 729 }
94f04347
LP
730
731 c->cpu_sched_policy = x;
bb112710
HHPF
732 /* Moving to or from real-time policy? We need to adjust the priority */
733 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
94f04347
LP
734 c->cpu_sched_set = true;
735
736 return 0;
737}
738
e8e581bf
ZJS
739int config_parse_exec_cpu_sched_prio(const char *unit,
740 const char *filename,
741 unsigned line,
742 const char *section,
71a61510 743 unsigned section_line,
e8e581bf
ZJS
744 const char *lvalue,
745 int ltype,
746 const char *rvalue,
747 void *data,
748 void *userdata) {
9eba9da4
LP
749
750 ExecContext *c = data;
e8e581bf 751 int i, min, max, r;
9eba9da4
LP
752
753 assert(filename);
754 assert(lvalue);
755 assert(rvalue);
756 assert(data);
757
e8e581bf
ZJS
758 r = safe_atoi(rvalue, &i);
759 if (r < 0) {
760 log_syntax(unit, LOG_ERR, filename, line, -r,
761 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 762 return 0;
94f04347 763 }
9eba9da4 764
bb112710
HHPF
765 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
766 min = sched_get_priority_min(c->cpu_sched_policy);
767 max = sched_get_priority_max(c->cpu_sched_policy);
768
769 if (i < min || i > max) {
e8e581bf
ZJS
770 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
771 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
bb112710
HHPF
772 return 0;
773 }
774
94f04347
LP
775 c->cpu_sched_priority = i;
776 c->cpu_sched_set = true;
777
778 return 0;
779}
780
e8e581bf
ZJS
781int config_parse_exec_cpu_affinity(const char *unit,
782 const char *filename,
783 unsigned line,
784 const char *section,
71a61510 785 unsigned section_line,
e8e581bf
ZJS
786 const char *lvalue,
787 int ltype,
788 const char *rvalue,
789 void *data,
790 void *userdata) {
94f04347
LP
791
792 ExecContext *c = data;
793 char *w;
794 size_t l;
795 char *state;
796
797 assert(filename);
798 assert(lvalue);
799 assert(rvalue);
800 assert(data);
801
74051b9b
LP
802 if (isempty(rvalue)) {
803 /* An empty assignment resets the CPU list */
804 if (c->cpuset)
805 CPU_FREE(c->cpuset);
806 c->cpuset = NULL;
807 return 0;
808 }
809
f60f22df 810 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 811 _cleanup_free_ char *t = NULL;
94f04347
LP
812 int r;
813 unsigned cpu;
814
c040936b
ZJS
815 t = strndup(w, l);
816 if (!t)
74051b9b 817 return log_oom();
94f04347 818
487393e9 819 r = safe_atou(t, &cpu);
487393e9 820
c040936b
ZJS
821 if (!c->cpuset) {
822 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
823 if (!c->cpuset)
74051b9b 824 return log_oom();
c040936b 825 }
82c121a4 826
82c121a4 827 if (r < 0 || cpu >= c->cpuset_ncpus) {
e8e581bf
ZJS
828 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
829 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
c0b34696 830 return 0;
9eba9da4 831 }
94f04347 832
82c121a4 833 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
9eba9da4
LP
834 }
835
94f04347
LP
836 return 0;
837}
838
e8e581bf
ZJS
839int config_parse_exec_capabilities(const char *unit,
840 const char *filename,
841 unsigned line,
842 const char *section,
71a61510 843 unsigned section_line,
e8e581bf
ZJS
844 const char *lvalue,
845 int ltype,
846 const char *rvalue,
847 void *data,
848 void *userdata) {
94f04347
LP
849
850 ExecContext *c = data;
851 cap_t cap;
852
853 assert(filename);
854 assert(lvalue);
855 assert(rvalue);
856 assert(data);
857
74051b9b
LP
858 cap = cap_from_text(rvalue);
859 if (!cap) {
e8e581bf
ZJS
860 log_syntax(unit, LOG_ERR, filename, line, errno,
861 "Failed to parse capabilities, ignoring: %s", rvalue);
c0b34696 862 return 0;
94f04347
LP
863 }
864
865 if (c->capabilities)
866 cap_free(c->capabilities);
867 c->capabilities = cap;
868
869 return 0;
870}
871
e8e581bf
ZJS
872int config_parse_exec_secure_bits(const char *unit,
873 const char *filename,
874 unsigned line,
875 const char *section,
71a61510 876 unsigned section_line,
e8e581bf
ZJS
877 const char *lvalue,
878 int ltype,
879 const char *rvalue,
880 void *data,
881 void *userdata) {
94f04347
LP
882
883 ExecContext *c = data;
884 char *w;
885 size_t l;
886 char *state;
887
888 assert(filename);
889 assert(lvalue);
890 assert(rvalue);
891 assert(data);
892
74051b9b
LP
893 if (isempty(rvalue)) {
894 /* An empty assignment resets the field */
895 c->secure_bits = 0;
896 return 0;
897 }
898
f60f22df 899 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
94f04347 900 if (first_word(w, "keep-caps"))
cbb21cca 901 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
94f04347 902 else if (first_word(w, "keep-caps-locked"))
cbb21cca 903 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
94f04347 904 else if (first_word(w, "no-setuid-fixup"))
cbb21cca 905 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
94f04347 906 else if (first_word(w, "no-setuid-fixup-locked"))
cbb21cca 907 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
94f04347 908 else if (first_word(w, "noroot"))
cbb21cca 909 c->secure_bits |= 1<<SECURE_NOROOT;
94f04347 910 else if (first_word(w, "noroot-locked"))
cbb21cca 911 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
9eba9da4 912 else {
e8e581bf
ZJS
913 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
914 "Failed to parse secure bits, ignoring: %s", rvalue);
c0b34696 915 return 0;
9eba9da4
LP
916 }
917 }
918
94f04347
LP
919 return 0;
920}
921
e8e581bf
ZJS
922int config_parse_bounding_set(const char *unit,
923 const char *filename,
924 unsigned line,
925 const char *section,
71a61510 926 unsigned section_line,
e8e581bf
ZJS
927 const char *lvalue,
928 int ltype,
929 const char *rvalue,
930 void *data,
931 void *userdata) {
94f04347 932
ec8927ca 933 uint64_t *capability_bounding_set_drop = data;
94f04347
LP
934 char *w;
935 size_t l;
936 char *state;
260abb78
LP
937 bool invert = false;
938 uint64_t sum = 0;
94f04347
LP
939
940 assert(filename);
941 assert(lvalue);
942 assert(rvalue);
943 assert(data);
944
260abb78
LP
945 if (rvalue[0] == '~') {
946 invert = true;
947 rvalue++;
948 }
949
950 /* Note that we store this inverted internally, since the
951 * kernel wants it like this. But we actually expose it
952 * non-inverted everywhere to have a fully normalized
953 * interface. */
954
f60f22df 955 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 956 _cleanup_free_ char *t = NULL;
94f04347
LP
957 int r;
958 cap_value_t cap;
959
ec8927ca
LP
960 t = strndup(w, l);
961 if (!t)
74051b9b 962 return log_oom();
94f04347
LP
963
964 r = cap_from_name(t, &cap);
94f04347 965 if (r < 0) {
e8e581bf
ZJS
966 log_syntax(unit, LOG_ERR, filename, line, errno,
967 "Failed to parse capability in bounding set, ignoring: %s", t);
8351ceae 968 continue;
94f04347
LP
969 }
970
260abb78 971 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
94f04347 972 }
9eba9da4 973
260abb78 974 if (invert)
ec8927ca 975 *capability_bounding_set_drop |= sum;
260abb78 976 else
ec8927ca 977 *capability_bounding_set_drop |= ~sum;
260abb78 978
9eba9da4
LP
979 return 0;
980}
981
e8e581bf
ZJS
982int config_parse_limit(const char *unit,
983 const char *filename,
984 unsigned line,
985 const char *section,
71a61510 986 unsigned section_line,
e8e581bf
ZJS
987 const char *lvalue,
988 int ltype,
989 const char *rvalue,
990 void *data,
991 void *userdata) {
94f04347
LP
992
993 struct rlimit **rl = data;
994 unsigned long long u;
94f04347
LP
995
996 assert(filename);
997 assert(lvalue);
998 assert(rvalue);
999 assert(data);
1000
f975e971
LP
1001 rl += ltype;
1002
3d57c6ab
LP
1003 if (streq(rvalue, "infinity"))
1004 u = (unsigned long long) RLIM_INFINITY;
e8e581bf
ZJS
1005 else {
1006 int r;
1007
1008 r = safe_atollu(rvalue, &u);
1009 if (r < 0) {
1010 log_syntax(unit, LOG_ERR, filename, line, -r,
1011 "Failed to parse resource value, ignoring: %s", rvalue);
1012 return 0;
1013 }
94f04347
LP
1014 }
1015
74051b9b
LP
1016 if (!*rl) {
1017 *rl = new(struct rlimit, 1);
1018 if (!*rl)
1019 return log_oom();
1020 }
9eba9da4 1021
94f04347 1022 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
9eba9da4
LP
1023 return 0;
1024}
1025
07459bb6 1026#ifdef HAVE_SYSV_COMPAT
e8e581bf
ZJS
1027int config_parse_sysv_priority(const char *unit,
1028 const char *filename,
1029 unsigned line,
1030 const char *section,
71a61510 1031 unsigned section_line,
e8e581bf
ZJS
1032 const char *lvalue,
1033 int ltype,
1034 const char *rvalue,
1035 void *data,
1036 void *userdata) {
a9a1e00a
LP
1037
1038 int *priority = data;
e8e581bf 1039 int i, r;
a9a1e00a
LP
1040
1041 assert(filename);
1042 assert(lvalue);
1043 assert(rvalue);
1044 assert(data);
1045
e8e581bf
ZJS
1046 r = safe_atoi(rvalue, &i);
1047 if (r < 0 || i < 0) {
1048 log_syntax(unit, LOG_ERR, filename, line, -r,
1049 "Failed to parse SysV start priority, ignoring: %s", rvalue);
c0b34696 1050 return 0;
a9a1e00a
LP
1051 }
1052
1053 *priority = (int) i;
1054 return 0;
1055}
07459bb6 1056#endif
a9a1e00a 1057
f975e971 1058DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
50159e6a 1059
e8e581bf
ZJS
1060int config_parse_kill_signal(const char *unit,
1061 const char *filename,
1062 unsigned line,
1063 const char *section,
71a61510 1064 unsigned section_line,
e8e581bf
ZJS
1065 const char *lvalue,
1066 int ltype,
1067 const char *rvalue,
1068 void *data,
1069 void *userdata) {
2e22afe9
LP
1070
1071 int *sig = data;
1072 int r;
1073
1074 assert(filename);
1075 assert(lvalue);
1076 assert(rvalue);
1077 assert(sig);
1078
74051b9b
LP
1079 r = signal_from_string_try_harder(rvalue);
1080 if (r <= 0) {
e8e581bf
ZJS
1081 log_syntax(unit, LOG_ERR, filename, line, -r,
1082 "Failed to parse kill signal, ignoring: %s", rvalue);
c0b34696 1083 return 0;
2e22afe9
LP
1084 }
1085
1086 *sig = r;
1087 return 0;
1088}
1089
e8e581bf
ZJS
1090int config_parse_exec_mount_flags(const char *unit,
1091 const char *filename,
1092 unsigned line,
1093 const char *section,
71a61510 1094 unsigned section_line,
e8e581bf
ZJS
1095 const char *lvalue,
1096 int ltype,
1097 const char *rvalue,
1098 void *data,
1099 void *userdata) {
15ae422b
LP
1100
1101 ExecContext *c = data;
1102 char *w;
1103 size_t l;
1104 char *state;
1105 unsigned long flags = 0;
1106
1107 assert(filename);
1108 assert(lvalue);
1109 assert(rvalue);
1110 assert(data);
1111
ac97e2c5 1112 FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
7fd1b19b 1113 _cleanup_free_ char *t;
ac97e2c5
ZJS
1114
1115 t = strndup(w, l);
1116 if (!t)
74051b9b 1117 return log_oom();
ac97e2c5
ZJS
1118
1119 if (streq(t, "shared"))
15ae422b 1120 flags |= MS_SHARED;
ac97e2c5 1121 else if (streq(t, "slave"))
15ae422b 1122 flags |= MS_SLAVE;
ac97e2c5 1123 else if (streq(w, "private"))
15ae422b
LP
1124 flags |= MS_PRIVATE;
1125 else {
e8e581bf
ZJS
1126 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1127 "Failed to parse mount flag %s, ignoring: %s",
1128 t, rvalue);
c0b34696 1129 return 0;
15ae422b
LP
1130 }
1131 }
1132
1133 c->mount_flags = flags;
1134 return 0;
1135}
1136
e8e581bf
ZJS
1137int config_parse_timer(const char *unit,
1138 const char *filename,
1139 unsigned line,
1140 const char *section,
71a61510 1141 unsigned section_line,
e8e581bf
ZJS
1142 const char *lvalue,
1143 int ltype,
1144 const char *rvalue,
1145 void *data,
1146 void *userdata) {
871d7de4
LP
1147
1148 Timer *t = data;
36697dc0 1149 usec_t u = 0;
871d7de4
LP
1150 TimerValue *v;
1151 TimerBase b;
36697dc0
LP
1152 CalendarSpec *c = NULL;
1153 clockid_t id;
871d7de4
LP
1154
1155 assert(filename);
1156 assert(lvalue);
1157 assert(rvalue);
1158 assert(data);
1159
74051b9b
LP
1160 if (isempty(rvalue)) {
1161 /* Empty assignment resets list */
1162 timer_free_values(t);
1163 return 0;
1164 }
1165
36697dc0
LP
1166 b = timer_base_from_string(lvalue);
1167 if (b < 0) {
e8e581bf
ZJS
1168 log_syntax(unit, LOG_ERR, filename, line, -b,
1169 "Failed to parse timer base, ignoring: %s", lvalue);
c0b34696 1170 return 0;
871d7de4
LP
1171 }
1172
36697dc0
LP
1173 if (b == TIMER_CALENDAR) {
1174 if (calendar_spec_from_string(rvalue, &c) < 0) {
e8e581bf
ZJS
1175 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1176 "Failed to parse calendar specification, ignoring: %s",
1177 rvalue);
36697dc0
LP
1178 return 0;
1179 }
1180
1181 id = CLOCK_REALTIME;
1182 } else {
7f602784 1183 if (parse_sec(rvalue, &u) < 0) {
e8e581bf
ZJS
1184 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1185 "Failed to parse timer value, ignoring: %s",
1186 rvalue);
36697dc0
LP
1187 return 0;
1188 }
1189
1190 id = CLOCK_MONOTONIC;
871d7de4
LP
1191 }
1192
36697dc0
LP
1193 v = new0(TimerValue, 1);
1194 if (!v)
74051b9b 1195 return log_oom();
871d7de4
LP
1196
1197 v->base = b;
36697dc0 1198 v->clock_id = id;
871d7de4 1199 v->value = u;
36697dc0 1200 v->calendar_spec = c;
871d7de4 1201
71fda00f 1202 LIST_PREPEND(value, t->values, v);
871d7de4
LP
1203
1204 return 0;
1205}
1206
3ecaa09b
LP
1207int config_parse_trigger_unit(
1208 const char *unit,
1209 const char *filename,
1210 unsigned line,
1211 const char *section,
71a61510 1212 unsigned section_line,
3ecaa09b
LP
1213 const char *lvalue,
1214 int ltype,
1215 const char *rvalue,
1216 void *data,
1217 void *userdata) {
871d7de4 1218
74051b9b 1219 _cleanup_free_ char *p = NULL;
3ecaa09b
LP
1220 Unit *u = data;
1221 UnitType type;
1222 int r;
398ef8ba
LP
1223
1224 assert(filename);
1225 assert(lvalue);
1226 assert(rvalue);
1227 assert(data);
1228
3ecaa09b
LP
1229 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1230 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1231 "Multiple units to trigger specified, ignoring: %s", rvalue);
1232 return 0;
1233 }
871d7de4 1234
19f6d710
LP
1235 r = unit_name_printf(u, rvalue, &p);
1236 if (r < 0)
1237 log_syntax(unit, LOG_ERR, filename, line, -r,
1238 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
74051b9b 1239
19f6d710 1240 type = unit_name_to_type(p ?: rvalue);
3ecaa09b 1241 if (type < 0) {
e8e581bf 1242 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3ecaa09b 1243 "Unit type not valid, ignoring: %s", rvalue);
c0b34696 1244 return 0;
871d7de4
LP
1245 }
1246
3ecaa09b
LP
1247 if (type == u->type) {
1248 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1249 "Trigger cannot be of same type, ignoring: %s", rvalue);
1250 return 0;
1251 }
1252
19f6d710 1253 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
57020a3a 1254 if (r < 0) {
e8e581bf 1255 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 1256 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
c0b34696 1257 return 0;
871d7de4
LP
1258 }
1259
1260 return 0;
1261}
1262
e8e581bf
ZJS
1263int config_parse_path_spec(const char *unit,
1264 const char *filename,
1265 unsigned line,
1266 const char *section,
71a61510 1267 unsigned section_line,
e8e581bf
ZJS
1268 const char *lvalue,
1269 int ltype,
1270 const char *rvalue,
1271 void *data,
1272 void *userdata) {
01f78473
LP
1273
1274 Path *p = data;
1275 PathSpec *s;
1276 PathType b;
7fd1b19b 1277 _cleanup_free_ char *k = NULL;
19f6d710 1278 int r;
01f78473
LP
1279
1280 assert(filename);
1281 assert(lvalue);
1282 assert(rvalue);
1283 assert(data);
1284
74051b9b
LP
1285 if (isempty(rvalue)) {
1286 /* Empty assignment clears list */
1287 path_free_specs(p);
1288 return 0;
1289 }
1290
93e4c84b
LP
1291 b = path_type_from_string(lvalue);
1292 if (b < 0) {
e8e581bf
ZJS
1293 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1294 "Failed to parse path type, ignoring: %s", lvalue);
c0b34696 1295 return 0;
01f78473
LP
1296 }
1297
19f6d710
LP
1298 r = unit_full_printf(UNIT(p), rvalue, &k);
1299 if (r < 0) {
487060c2
LP
1300 k = strdup(rvalue);
1301 if (!k)
1302 return log_oom();
1303 else
19f6d710 1304 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf
ZJS
1305 "Failed to resolve unit specifiers on %s. Ignoring.",
1306 rvalue);
487060c2 1307 }
93e4c84b
LP
1308
1309 if (!path_is_absolute(k)) {
e8e581bf
ZJS
1310 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1311 "Path is not absolute, ignoring: %s", k);
c0b34696 1312 return 0;
01f78473
LP
1313 }
1314
93e4c84b 1315 s = new0(PathSpec, 1);
543295ad 1316 if (!s)
93e4c84b 1317 return log_oom();
01f78473 1318
718db961 1319 s->unit = UNIT(p);
93e4c84b 1320 s->path = path_kill_slashes(k);
543295ad 1321 k = NULL;
01f78473
LP
1322 s->type = b;
1323 s->inotify_fd = -1;
1324
71fda00f 1325 LIST_PREPEND(spec, p->specs, s);
01f78473
LP
1326
1327 return 0;
1328}
1329
e8e581bf
ZJS
1330int config_parse_socket_service(const char *unit,
1331 const char *filename,
1332 unsigned line,
1333 const char *section,
71a61510 1334 unsigned section_line,
e8e581bf
ZJS
1335 const char *lvalue,
1336 int ltype,
1337 const char *rvalue,
1338 void *data,
1339 void *userdata) {
d9ff321a 1340
718db961 1341 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
d9ff321a
LP
1342 Socket *s = data;
1343 int r;
4ff77f66 1344 Unit *x;
74051b9b 1345 _cleanup_free_ char *p = NULL;
d9ff321a
LP
1346
1347 assert(filename);
1348 assert(lvalue);
1349 assert(rvalue);
1350 assert(data);
1351
19f6d710
LP
1352 r = unit_name_printf(UNIT(s), rvalue, &p);
1353 if (r < 0)
718db961 1354 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
74051b9b 1355
19f6d710 1356 if (!endswith(p ?: rvalue, ".service")) {
718db961 1357 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
d9ff321a
LP
1358 return 0;
1359 }
1360
19f6d710 1361 r = manager_load_unit(UNIT(s)->manager, p ?: rvalue, NULL, &error, &x);
4ff77f66 1362 if (r < 0) {
718db961 1363 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
d9ff321a
LP
1364 return 0;
1365 }
1366
4ff77f66
LP
1367 unit_ref_set(&s->service, x);
1368
d9ff321a
LP
1369 return 0;
1370}
1371
e8e581bf
ZJS
1372int config_parse_service_sockets(const char *unit,
1373 const char *filename,
1374 unsigned line,
1375 const char *section,
71a61510 1376 unsigned section_line,
e8e581bf
ZJS
1377 const char *lvalue,
1378 int ltype,
1379 const char *rvalue,
1380 void *data,
1381 void *userdata) {
f976f3f6
LP
1382
1383 Service *s = data;
1384 int r;
f976f3f6
LP
1385 char *state, *w;
1386 size_t l;
1387
1388 assert(filename);
1389 assert(lvalue);
1390 assert(rvalue);
1391 assert(data);
1392
f976f3f6 1393 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 1394 _cleanup_free_ char *t = NULL, *k = NULL;
f976f3f6 1395
57020a3a
LP
1396 t = strndup(w, l);
1397 if (!t)
74051b9b 1398 return log_oom();
f976f3f6 1399
19f6d710
LP
1400 r = unit_name_printf(UNIT(s), t, &k);
1401 if (r < 0)
1402 log_syntax(unit, LOG_ERR, filename, line, -r,
1403 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
57020a3a 1404
19f6d710 1405 if (!endswith(k ?: t, ".socket")) {
e8e581bf 1406 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
19f6d710 1407 "Unit must be of type socket, ignoring: %s", k ?: t);
f976f3f6
LP
1408 continue;
1409 }
1410
19f6d710 1411 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
57020a3a 1412 if (r < 0)
e8e581bf
ZJS
1413 log_syntax(unit, LOG_ERR, filename, line, -r,
1414 "Failed to add dependency on %s, ignoring: %s",
19f6d710 1415 k ?: t, strerror(-r));
f976f3f6 1416
19f6d710 1417 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
57020a3a 1418 if (r < 0)
f976f3f6
LP
1419 return r;
1420 }
1421
1422 return 0;
1423}
1424
e8e581bf
ZJS
1425int config_parse_service_timeout(const char *unit,
1426 const char *filename,
1427 unsigned line,
1428 const char *section,
71a61510 1429 unsigned section_line,
e8e581bf
ZJS
1430 const char *lvalue,
1431 int ltype,
1432 const char *rvalue,
1433 void *data,
1434 void *userdata) {
98709151
LN
1435
1436 Service *s = userdata;
1437 int r;
1438
1439 assert(filename);
1440 assert(lvalue);
1441 assert(rvalue);
1442 assert(s);
1443
71a61510 1444 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 1445 rvalue, data, userdata);
74051b9b 1446 if (r < 0)
d568a335 1447 return r;
98709151 1448
d568a335
MS
1449 if (streq(lvalue, "TimeoutSec")) {
1450 s->start_timeout_defined = true;
1451 s->timeout_stop_usec = s->timeout_start_usec;
1452 } else if (streq(lvalue, "TimeoutStartSec"))
1453 s->start_timeout_defined = true;
1454
1455 return 0;
98709151
LN
1456}
1457
e8e581bf
ZJS
1458int config_parse_unit_env_file(const char *unit,
1459 const char *filename,
1460 unsigned line,
1461 const char *section,
71a61510 1462 unsigned section_line,
e8e581bf
ZJS
1463 const char *lvalue,
1464 int ltype,
1465 const char *rvalue,
1466 void *data,
1467 void *userdata) {
ddb26e18 1468
853b8397 1469 char ***env = data;
8fef7659 1470 Unit *u = userdata;
19f6d710
LP
1471 _cleanup_free_ char *n = NULL;
1472 const char *s;
853b8397 1473 int r;
ddb26e18
LP
1474
1475 assert(filename);
1476 assert(lvalue);
1477 assert(rvalue);
1478 assert(data);
1479
74051b9b
LP
1480 if (isempty(rvalue)) {
1481 /* Empty assignment frees the list */
74051b9b
LP
1482 strv_free(*env);
1483 *env = NULL;
1484 return 0;
1485 }
1486
19f6d710
LP
1487 r = unit_full_printf(u, rvalue, &n);
1488 if (r < 0)
1489 log_syntax(unit, LOG_ERR, filename, line, r,
1490 "Failed to resolve specifiers, ignoring: %s", rvalue);
8fef7659 1491
19f6d710 1492 s = n ?: rvalue;
8fef7659 1493 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
e8e581bf
ZJS
1494 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1495 "Path '%s' is not absolute, ignoring.", s);
afe4bfe2
LP
1496 return 0;
1497 }
1498
853b8397
LP
1499 r = strv_extend(env, s);
1500 if (r < 0)
1501 return log_oom();
1502
1503 return 0;
1504}
1505
e8e581bf
ZJS
1506int config_parse_environ(const char *unit,
1507 const char *filename,
1508 unsigned line,
1509 const char *section,
71a61510 1510 unsigned section_line,
e8e581bf
ZJS
1511 const char *lvalue,
1512 int ltype,
1513 const char *rvalue,
1514 void *data,
1515 void *userdata) {
853b8397
LP
1516
1517 Unit *u = userdata;
1518 char*** env = data, *w, *state;
1519 size_t l;
1520 _cleanup_free_ char *k = NULL;
19f6d710 1521 int r;
853b8397
LP
1522
1523 assert(filename);
1524 assert(lvalue);
1525 assert(rvalue);
97d0e5f8 1526 assert(data);
853b8397
LP
1527
1528 if (isempty(rvalue)) {
1529 /* Empty assignment resets the list */
1530 strv_free(*env);
1531 *env = NULL;
1532 return 0;
1533 }
1534
19f6d710
LP
1535 if (u) {
1536 r = unit_full_printf(u, rvalue, &k);
1537 if (r < 0)
1538 log_syntax(unit, LOG_ERR, filename, line, -r,
1539 "Failed to resolve specifiers, ignoring: %s", rvalue);
1540 }
97d0e5f8 1541
19f6d710
LP
1542 if (!k)
1543 k = strdup(rvalue);
8fef7659 1544 if (!k)
74051b9b 1545 return log_oom();
ddb26e18 1546
853b8397
LP
1547 FOREACH_WORD_QUOTED(w, l, k, state) {
1548 _cleanup_free_ char *n;
1549 char **x;
1550
1551 n = cunescape_length(w, l);
1552 if (!n)
1553 return log_oom();
1554
1555 if (!env_assignment_is_valid(n)) {
e8e581bf
ZJS
1556 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1557 "Invalid environment assignment, ignoring: %s", rvalue);
853b8397
LP
1558 continue;
1559 }
1560
1561 x = strv_env_set(*env, n);
1562 if (!x)
1563 return log_oom();
1564
1565 strv_free(*env);
1566 *env = x;
1567 }
ddb26e18 1568
8c7be95e 1569 return 0;
ddb26e18
LP
1570}
1571
e8e581bf
ZJS
1572int config_parse_ip_tos(const char *unit,
1573 const char *filename,
1574 unsigned line,
1575 const char *section,
71a61510 1576 unsigned section_line,
e8e581bf
ZJS
1577 const char *lvalue,
1578 int ltype,
1579 const char *rvalue,
1580 void *data,
1581 void *userdata) {
4fd5948e
LP
1582
1583 int *ip_tos = data, x;
4fd5948e
LP
1584
1585 assert(filename);
1586 assert(lvalue);
1587 assert(rvalue);
1588 assert(data);
1589
f8b69d1d
MS
1590 x = ip_tos_from_string(rvalue);
1591 if (x < 0) {
e8e581bf
ZJS
1592 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1593 "Failed to parse IP TOS value, ignoring: %s", rvalue);
f8b69d1d
MS
1594 return 0;
1595 }
4fd5948e
LP
1596
1597 *ip_tos = x;
1598 return 0;
1599}
1600
e8e581bf
ZJS
1601int config_parse_unit_condition_path(const char *unit,
1602 const char *filename,
1603 unsigned line,
1604 const char *section,
71a61510 1605 unsigned section_line,
e8e581bf
ZJS
1606 const char *lvalue,
1607 int ltype,
1608 const char *rvalue,
1609 void *data,
1610 void *userdata) {
52661efd 1611
2b583ce6 1612 ConditionType cond = ltype;
52661efd 1613 Unit *u = data;
267632f0 1614 bool trigger, negate;
52661efd 1615 Condition *c;
2fbe635a 1616 _cleanup_free_ char *p = NULL;
19f6d710 1617 int r;
52661efd
LP
1618
1619 assert(filename);
1620 assert(lvalue);
1621 assert(rvalue);
1622 assert(data);
1623
74051b9b
LP
1624 if (isempty(rvalue)) {
1625 /* Empty assignment resets the list */
1626 condition_free_list(u->conditions);
1627 u->conditions = NULL;
1628 return 0;
1629 }
1630
ab7f148f
LP
1631 trigger = rvalue[0] == '|';
1632 if (trigger)
267632f0
LP
1633 rvalue++;
1634
ab7f148f
LP
1635 negate = rvalue[0] == '!';
1636 if (negate)
52661efd
LP
1637 rvalue++;
1638
19f6d710
LP
1639 r = unit_full_printf(u, rvalue, &p);
1640 if (r < 0)
1641 log_syntax(unit, LOG_ERR, filename, line, -r,
1642 "Failed to resolve specifiers, ignoring: %s", rvalue);
1643 if (!p) {
1644 p = strdup(rvalue);
1645 if (!p)
1646 return log_oom();
1647 }
095b2d7a
AK
1648
1649 if (!path_is_absolute(p)) {
e8e581bf
ZJS
1650 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1651 "Path in condition not absolute, ignoring: %s", p);
52661efd
LP
1652 return 0;
1653 }
1654
095b2d7a 1655 c = condition_new(cond, p, trigger, negate);
ab7f148f 1656 if (!c)
74051b9b 1657 return log_oom();
52661efd 1658
71fda00f 1659 LIST_PREPEND(conditions, u->conditions, c);
52661efd
LP
1660 return 0;
1661}
1662
e8e581bf
ZJS
1663int config_parse_unit_condition_string(const char *unit,
1664 const char *filename,
1665 unsigned line,
1666 const char *section,
71a61510 1667 unsigned section_line,
e8e581bf
ZJS
1668 const char *lvalue,
1669 int ltype,
1670 const char *rvalue,
1671 void *data,
1672 void *userdata) {
039655a4 1673
41584525 1674 ConditionType cond = ltype;
039655a4 1675 Unit *u = data;
267632f0 1676 bool trigger, negate;
039655a4 1677 Condition *c;
2fbe635a 1678 _cleanup_free_ char *s = NULL;
19f6d710 1679 int r;
039655a4
LP
1680
1681 assert(filename);
1682 assert(lvalue);
1683 assert(rvalue);
1684 assert(data);
1685
74051b9b
LP
1686 if (isempty(rvalue)) {
1687 /* Empty assignment resets the list */
1688 condition_free_list(u->conditions);
1689 u->conditions = NULL;
1690 return 0;
1691 }
1692
c0d6e764
LP
1693 trigger = rvalue[0] == '|';
1694 if (trigger)
267632f0
LP
1695 rvalue++;
1696
c0d6e764
LP
1697 negate = rvalue[0] == '!';
1698 if (negate)
039655a4
LP
1699 rvalue++;
1700
19f6d710
LP
1701 r = unit_full_printf(u, rvalue, &s);
1702 if (r < 0)
1703 log_syntax(unit, LOG_ERR, filename, line, -r,
1704 "Failed to resolve specifiers, ignoring: %s", rvalue);
1705 if (!s) {
1706 s = strdup(rvalue);
1707 if (!s)
1708 return log_oom();
1709 }
095b2d7a
AK
1710
1711 c = condition_new(cond, s, trigger, negate);
c0d6e764
LP
1712 if (!c)
1713 return log_oom();
039655a4 1714
71fda00f 1715 LIST_PREPEND(conditions, u->conditions, c);
039655a4
LP
1716 return 0;
1717}
1718
e8e581bf
ZJS
1719int config_parse_unit_condition_null(const char *unit,
1720 const char *filename,
1721 unsigned line,
1722 const char *section,
71a61510 1723 unsigned section_line,
e8e581bf
ZJS
1724 const char *lvalue,
1725 int ltype,
1726 const char *rvalue,
1727 void *data,
1728 void *userdata) {
d257ddef
LP
1729
1730 Unit *u = data;
1731 Condition *c;
267632f0 1732 bool trigger, negate;
d257ddef
LP
1733 int b;
1734
1735 assert(filename);
1736 assert(lvalue);
1737 assert(rvalue);
1738 assert(data);
1739
74051b9b
LP
1740 if (isempty(rvalue)) {
1741 /* Empty assignment resets the list */
1742 condition_free_list(u->conditions);
1743 u->conditions = NULL;
1744 return 0;
1745 }
1746
1747 trigger = rvalue[0] == '|';
1748 if (trigger)
267632f0
LP
1749 rvalue++;
1750
74051b9b
LP
1751 negate = rvalue[0] == '!';
1752 if (negate)
d257ddef
LP
1753 rvalue++;
1754
74051b9b
LP
1755 b = parse_boolean(rvalue);
1756 if (b < 0) {
e8e581bf
ZJS
1757 log_syntax(unit, LOG_ERR, filename, line, -b,
1758 "Failed to parse boolean value in condition, ignoring: %s",
1759 rvalue);
d257ddef
LP
1760 return 0;
1761 }
1762
1763 if (!b)
1764 negate = !negate;
1765
74051b9b
LP
1766 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
1767 if (!c)
1768 return log_oom();
d257ddef 1769
71fda00f 1770 LIST_PREPEND(conditions, u->conditions, c);
d257ddef
LP
1771 return 0;
1772}
1773
f975e971 1774DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
4b939747 1775DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
c952c6ec 1776
a57f7e2c
LP
1777int config_parse_unit_requires_mounts_for(
1778 const char *unit,
1779 const char *filename,
1780 unsigned line,
1781 const char *section,
71a61510 1782 unsigned section_line,
a57f7e2c
LP
1783 const char *lvalue,
1784 int ltype,
1785 const char *rvalue,
1786 void *data,
1787 void *userdata) {
7c8fa05c
LP
1788
1789 Unit *u = userdata;
a57f7e2c
LP
1790 char *state;
1791 size_t l;
1792 char *w;
7c8fa05c
LP
1793
1794 assert(filename);
1795 assert(lvalue);
1796 assert(rvalue);
1797 assert(data);
1798
a57f7e2c 1799 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
8a7935a2 1800 int r;
a57f7e2c
LP
1801 _cleanup_free_ char *n;
1802
1803 n = strndup(w, l);
1804 if (!n)
1805 return log_oom();
7c8fa05c 1806
a57f7e2c
LP
1807 if (!utf8_is_valid(n)) {
1808 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1809 "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
1810 continue;
1811 }
7c8fa05c 1812
a57f7e2c
LP
1813 r = unit_require_mounts_for(u, n);
1814 if (r < 0) {
1815 log_syntax(unit, LOG_ERR, filename, line, r,
1816 "Failed to add required mount for, ignoring: %s", rvalue);
1817 continue;
1818 }
1819 }
7c8fa05c 1820
8a7935a2 1821 return 0;
7c8fa05c 1822}
9e372868 1823
e8e581bf
ZJS
1824int config_parse_documentation(const char *unit,
1825 const char *filename,
1826 unsigned line,
1827 const char *section,
71a61510 1828 unsigned section_line,
e8e581bf
ZJS
1829 const char *lvalue,
1830 int ltype,
1831 const char *rvalue,
1832 void *data,
1833 void *userdata) {
49dbfa7b
LP
1834
1835 Unit *u = userdata;
1836 int r;
1837 char **a, **b;
1838
1839 assert(filename);
1840 assert(lvalue);
1841 assert(rvalue);
1842 assert(u);
1843
74051b9b
LP
1844 if (isempty(rvalue)) {
1845 /* Empty assignment resets the list */
1846 strv_free(u->documentation);
1847 u->documentation = NULL;
1848 return 0;
1849 }
1850
71a61510 1851 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 1852 rvalue, data, userdata);
49dbfa7b
LP
1853 if (r < 0)
1854 return r;
1855
1856 for (a = b = u->documentation; a && *a; a++) {
1857
1858 if (is_valid_documentation_url(*a))
1859 *(b++) = *a;
1860 else {
e8e581bf
ZJS
1861 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1862 "Invalid URL, ignoring: %s", *a);
49dbfa7b
LP
1863 free(*a);
1864 }
1865 }
f6d2d421
ZJS
1866 if (b)
1867 *b = NULL;
49dbfa7b
LP
1868
1869 return r;
1870}
1871
8351ceae 1872static void syscall_set(uint32_t *p, int nr) {
843fc7f7 1873 nr = SYSCALL_TO_INDEX(nr);
8351ceae
LP
1874 p[nr >> 4] |= 1 << (nr & 31);
1875}
1876
1877static void syscall_unset(uint32_t *p, int nr) {
843fc7f7 1878 nr = SYSCALL_TO_INDEX(nr);
8351ceae
LP
1879 p[nr >> 4] &= ~(1 << (nr & 31));
1880}
1881
e8e581bf
ZJS
1882int config_parse_syscall_filter(const char *unit,
1883 const char *filename,
1884 unsigned line,
1885 const char *section,
71a61510 1886 unsigned section_line,
e8e581bf
ZJS
1887 const char *lvalue,
1888 int ltype,
1889 const char *rvalue,
1890 void *data,
1891 void *userdata) {
8351ceae
LP
1892
1893 ExecContext *c = data;
1894 Unit *u = userdata;
b5fb3789 1895 bool invert = false;
8351ceae
LP
1896 char *w;
1897 size_t l;
1898 char *state;
1899
1900 assert(filename);
1901 assert(lvalue);
1902 assert(rvalue);
1903 assert(u);
1904
74051b9b
LP
1905 if (isempty(rvalue)) {
1906 /* Empty assignment resets the list */
1907 free(c->syscall_filter);
1908 c->syscall_filter = NULL;
1909 return 0;
1910 }
1911
8351ceae
LP
1912 if (rvalue[0] == '~') {
1913 invert = true;
1914 rvalue++;
1915 }
1916
1917 if (!c->syscall_filter) {
1918 size_t n;
1919
1920 n = (syscall_max() + 31) >> 4;
1921 c->syscall_filter = new(uint32_t, n);
1922 if (!c->syscall_filter)
74051b9b 1923 return log_oom();
8351ceae
LP
1924
1925 memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
1926
1927 /* Add these by default */
1928 syscall_set(c->syscall_filter, __NR_execve);
1929 syscall_set(c->syscall_filter, __NR_rt_sigreturn);
1930#ifdef __NR_sigreturn
1931 syscall_set(c->syscall_filter, __NR_sigreturn);
1932#endif
1933 syscall_set(c->syscall_filter, __NR_exit_group);
1934 syscall_set(c->syscall_filter, __NR_exit);
1935 }
1936
1937 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1938 int id;
7fd1b19b 1939 _cleanup_free_ char *t = NULL;
8351ceae
LP
1940
1941 t = strndup(w, l);
1942 if (!t)
74051b9b 1943 return log_oom();
8351ceae
LP
1944
1945 id = syscall_from_name(t);
8351ceae 1946 if (id < 0) {
e8e581bf
ZJS
1947 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1948 "Failed to parse syscall, ignoring: %s", t);
8351ceae
LP
1949 continue;
1950 }
1951
1952 if (invert)
1953 syscall_unset(c->syscall_filter, id);
1954 else
1955 syscall_set(c->syscall_filter, id);
1956 }
1957
1958 c->no_new_privileges = true;
1959
1960 return 0;
1961}
1962
a016b922
LP
1963int config_parse_unit_slice(
1964 const char *unit,
1965 const char *filename,
1966 unsigned line,
1967 const char *section,
71a61510 1968 unsigned section_line,
a016b922
LP
1969 const char *lvalue,
1970 int ltype,
1971 const char *rvalue,
1972 void *data,
1973 void *userdata) {
1974
1975 _cleanup_free_ char *k = NULL;
1976 Unit *u = userdata, *slice;
1977 int r;
1978
1979 assert(filename);
1980 assert(lvalue);
1981 assert(rvalue);
1982 assert(u);
1983
19f6d710
LP
1984 r = unit_name_printf(u, rvalue, &k);
1985 if (r < 0)
1986 log_syntax(unit, LOG_ERR, filename, line, -r,
a016b922 1987 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
19f6d710
LP
1988 if (!k) {
1989 k = strdup(rvalue);
1990 if (!k)
1991 return log_oom();
1992 }
a016b922 1993
19f6d710 1994 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
a016b922
LP
1995 if (r < 0) {
1996 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 1997 "Failed to load slice unit %s. Ignoring.", k);
a016b922
LP
1998 return 0;
1999 }
2000
2001 if (slice->type != UNIT_SLICE) {
2002 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
19f6d710 2003 "Slice unit %s is not a slice. Ignoring.", k);
a016b922
LP
2004 return 0;
2005 }
2006
2007 unit_ref_set(&u->slice, slice);
2008 return 0;
2009}
2010
4ad49000
LP
2011DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2012
2013int config_parse_cpu_shares(
2014 const char *unit,
2015 const char *filename,
2016 unsigned line,
2017 const char *section,
71a61510 2018 unsigned section_line,
4ad49000
LP
2019 const char *lvalue,
2020 int ltype,
2021 const char *rvalue,
2022 void *data,
2023 void *userdata) {
2024
2025 CGroupContext *c = data;
2026 unsigned long lu;
2027 int r;
2028
2029 assert(filename);
2030 assert(lvalue);
2031 assert(rvalue);
2032
2033 if (isempty(rvalue)) {
2034 c->cpu_shares = 1024;
2035 return 0;
2036 }
2037
2038 r = safe_atolu(rvalue, &lu);
2039 if (r < 0 || lu <= 0) {
2040 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2041 "CPU shares '%s' invalid. Ignoring.", rvalue);
2042 return 0;
2043 }
2044
2045 c->cpu_shares = lu;
2046 return 0;
2047}
2048
2049int config_parse_memory_limit(
2050 const char *unit,
2051 const char *filename,
2052 unsigned line,
2053 const char *section,
71a61510 2054 unsigned section_line,
4ad49000
LP
2055 const char *lvalue,
2056 int ltype,
2057 const char *rvalue,
2058 void *data,
2059 void *userdata) {
2060
2061 CGroupContext *c = data;
4ad49000
LP
2062 off_t bytes;
2063 int r;
2064
4ad49000 2065 if (isempty(rvalue)) {
ddca82ac 2066 c->memory_limit = (uint64_t) -1;
4ad49000
LP
2067 return 0;
2068 }
2069
2070 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2071
2072 r = parse_bytes(rvalue, &bytes);
2073 if (r < 0) {
2074 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2075 "Memory limit '%s' invalid. Ignoring.", rvalue);
2076 return 0;
2077 }
2078
ddca82ac 2079 c->memory_limit = (uint64_t) bytes;
4ad49000
LP
2080 return 0;
2081}
2082
2083int config_parse_device_allow(
2084 const char *unit,
2085 const char *filename,
2086 unsigned line,
2087 const char *section,
71a61510 2088 unsigned section_line,
4ad49000
LP
2089 const char *lvalue,
2090 int ltype,
2091 const char *rvalue,
2092 void *data,
2093 void *userdata) {
2094
2095 _cleanup_free_ char *path = NULL;
2096 CGroupContext *c = data;
2097 CGroupDeviceAllow *a;
2098 const char *m;
2099 size_t n;
2100
2101 if (isempty(rvalue)) {
2102 while (c->device_allow)
2103 cgroup_context_free_device_allow(c, c->device_allow);
2104
2105 return 0;
2106 }
2107
2108 n = strcspn(rvalue, WHITESPACE);
2109 path = strndup(rvalue, n);
2110 if (!path)
2111 return log_oom();
2112
2113 if (!path_startswith(path, "/dev")) {
2114 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2115 "Invalid device node path '%s'. Ignoring.", path);
2116 return 0;
2117 }
2118
2119 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2120 if (isempty(m))
2121 m = "rwm";
2122
2123 if (!in_charset(m, "rwm")) {
2124 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2125 "Invalid device rights '%s'. Ignoring.", m);
2126 return 0;
2127 }
2128
2129 a = new0(CGroupDeviceAllow, 1);
2130 if (!a)
2131 return log_oom();
2132
2133 a->path = path;
2134 path = NULL;
2135 a->r = !!strchr(m, 'r');
2136 a->w = !!strchr(m, 'w');
2137 a->m = !!strchr(m, 'm');
2138
71fda00f 2139 LIST_PREPEND(device_allow, c->device_allow, a);
4ad49000
LP
2140 return 0;
2141}
2142
2143int config_parse_blockio_weight(
2144 const char *unit,
2145 const char *filename,
2146 unsigned line,
2147 const char *section,
71a61510 2148 unsigned section_line,
4ad49000
LP
2149 const char *lvalue,
2150 int ltype,
2151 const char *rvalue,
2152 void *data,
2153 void *userdata) {
2154
8e7076ca
LP
2155 CGroupContext *c = data;
2156 unsigned long lu;
2157 int r;
2158
2159 assert(filename);
2160 assert(lvalue);
2161 assert(rvalue);
2162
2163 if (isempty(rvalue)) {
2164 c->blockio_weight = 1000;
2165 return 0;
2166 }
2167
2168 r = safe_atolu(rvalue, &lu);
2169 if (r < 0 || lu < 10 || lu > 1000) {
2170 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2171 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2172 return 0;
2173 }
2174
2175 c->blockio_weight = lu;
2176
2177 return 0;
2178}
2179
2180int config_parse_blockio_device_weight(
2181 const char *unit,
2182 const char *filename,
2183 unsigned line,
2184 const char *section,
71a61510 2185 unsigned section_line,
8e7076ca
LP
2186 const char *lvalue,
2187 int ltype,
2188 const char *rvalue,
2189 void *data,
2190 void *userdata) {
2191
4ad49000 2192 _cleanup_free_ char *path = NULL;
8e7076ca 2193 CGroupBlockIODeviceWeight *w;
4ad49000
LP
2194 CGroupContext *c = data;
2195 unsigned long lu;
2196 const char *weight;
2197 size_t n;
2198 int r;
2199
2200 assert(filename);
2201 assert(lvalue);
2202 assert(rvalue);
2203
2204 if (isempty(rvalue)) {
4ad49000
LP
2205 while (c->blockio_device_weights)
2206 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2207
2208 return 0;
2209 }
2210
2211 n = strcspn(rvalue, WHITESPACE);
2212 weight = rvalue + n;
8e7076ca
LP
2213 if (!*weight) {
2214 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2215 "Expected block device and device weight. Ignoring.");
2216 return 0;
2217 }
4ad49000 2218
8e7076ca
LP
2219 path = strndup(rvalue, n);
2220 if (!path)
2221 return log_oom();
4ad49000 2222
8e7076ca
LP
2223 if (!path_startswith(path, "/dev")) {
2224 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2225 "Invalid device node path '%s'. Ignoring.", path);
2226 return 0;
2227 }
4ad49000 2228
8e7076ca 2229 weight += strspn(weight, WHITESPACE);
4ad49000
LP
2230 r = safe_atolu(weight, &lu);
2231 if (r < 0 || lu < 10 || lu > 1000) {
2232 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2233 "Block IO weight '%s' invalid. Ignoring.", rvalue);
2234 return 0;
2235 }
2236
4ad49000 2237
8e7076ca
LP
2238 w = new0(CGroupBlockIODeviceWeight, 1);
2239 if (!w)
2240 return log_oom();
4ad49000 2241
8e7076ca
LP
2242 w->path = path;
2243 path = NULL;
4ad49000 2244
8e7076ca 2245 w->weight = lu;
4ad49000 2246
71fda00f 2247 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
2248 return 0;
2249}
2250
2251int config_parse_blockio_bandwidth(
2252 const char *unit,
2253 const char *filename,
2254 unsigned line,
2255 const char *section,
71a61510 2256 unsigned section_line,
4ad49000
LP
2257 const char *lvalue,
2258 int ltype,
2259 const char *rvalue,
2260 void *data,
2261 void *userdata) {
2262
2263 _cleanup_free_ char *path = NULL;
2264 CGroupBlockIODeviceBandwidth *b;
2265 CGroupContext *c = data;
2266 const char *bandwidth;
2267 off_t bytes;
47c0980d 2268 bool read;
4ad49000
LP
2269 size_t n;
2270 int r;
2271
2272 assert(filename);
2273 assert(lvalue);
2274 assert(rvalue);
2275
47c0980d
G
2276 read = streq("BlockIOReadBandwidth", lvalue);
2277
4ad49000 2278 if (isempty(rvalue)) {
47c0980d
G
2279 CGroupBlockIODeviceBandwidth *next;
2280
2281 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2282 if (b->read == read)
2283 cgroup_context_free_blockio_device_bandwidth(c, b);
4ad49000
LP
2284
2285 return 0;
2286 }
2287
2288 n = strcspn(rvalue, WHITESPACE);
2289 bandwidth = rvalue + n;
2290 bandwidth += strspn(bandwidth, WHITESPACE);
2291
2292 if (!*bandwidth) {
2293 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2294 "Expected space separated pair of device node and bandwidth. Ignoring.");
2295 return 0;
2296 }
2297
2298 path = strndup(rvalue, n);
2299 if (!path)
2300 return log_oom();
2301
2302 if (!path_startswith(path, "/dev")) {
2303 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2304 "Invalid device node path '%s'. Ignoring.", path);
2305 return 0;
2306 }
2307
2308 r = parse_bytes(bandwidth, &bytes);
2309 if (r < 0 || bytes <= 0) {
2310 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2311 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
2312 return 0;
2313 }
2314
2315 b = new0(CGroupBlockIODeviceBandwidth, 1);
2316 if (!b)
2317 return log_oom();
2318
2319 b->path = path;
2320 path = NULL;
2321 b->bandwidth = (uint64_t) bytes;
47c0980d 2322 b->read = read;
4ad49000 2323
71fda00f 2324 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
4ad49000
LP
2325
2326 return 0;
2327}
2328
071830ff 2329#define FOLLOW_MAX 8
87f0e418 2330
9e2f7c11 2331static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
0301abf4 2332 unsigned c = 0;
87f0e418
LP
2333 int fd, r;
2334 FILE *f;
0301abf4 2335 char *id = NULL;
87f0e418
LP
2336
2337 assert(filename);
2338 assert(*filename);
2339 assert(_f);
2340 assert(names);
2341
0301abf4
LP
2342 /* This will update the filename pointer if the loaded file is
2343 * reached by a symlink. The old string will be freed. */
87f0e418 2344
0301abf4 2345 for (;;) {
2c7108c4 2346 char *target, *name;
87f0e418 2347
0301abf4
LP
2348 if (c++ >= FOLLOW_MAX)
2349 return -ELOOP;
2350
b08d03ff
LP
2351 path_kill_slashes(*filename);
2352
87f0e418 2353 /* Add the file name we are currently looking at to
8f05424d
LP
2354 * the names of this unit, but only if it is a valid
2355 * unit name. */
9eb977db 2356 name = path_get_file_name(*filename);
87f0e418 2357
15e11d81 2358 if (unit_name_is_valid(name, true)) {
8f05424d 2359
15e11d81
LP
2360 id = set_get(names, name);
2361 if (!id) {
2362 id = strdup(name);
2363 if (!id)
8f05424d 2364 return -ENOMEM;
87f0e418 2365
ef42202a
ZJS
2366 r = set_consume(names, id);
2367 if (r < 0)
8f05424d 2368 return r;
87f0e418 2369 }
87f0e418
LP
2370 }
2371
0301abf4 2372 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
2373 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
2374 if (fd >= 0)
87f0e418
LP
2375 break;
2376
0301abf4
LP
2377 if (errno != ELOOP)
2378 return -errno;
2379
87f0e418 2380 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
2381 r = readlink_and_make_absolute(*filename, &target);
2382 if (r < 0)
0301abf4 2383 return r;
87f0e418 2384
0301abf4 2385 free(*filename);
2c7108c4 2386 *filename = target;
87f0e418
LP
2387 }
2388
9946996c
LP
2389 f = fdopen(fd, "re");
2390 if (!f) {
87f0e418 2391 r = -errno;
9e2f7c11 2392 close_nointr_nofail(fd);
0301abf4 2393 return r;
87f0e418
LP
2394 }
2395
2396 *_f = f;
9e2f7c11 2397 *_final = id;
0301abf4 2398 return 0;
87f0e418
LP
2399}
2400
23a177ef
LP
2401static int merge_by_names(Unit **u, Set *names, const char *id) {
2402 char *k;
2403 int r;
2404
2405 assert(u);
2406 assert(*u);
2407 assert(names);
2408
2409 /* Let's try to add in all symlink names we found */
2410 while ((k = set_steal_first(names))) {
2411
2412 /* First try to merge in the other name into our
2413 * unit */
9946996c
LP
2414 r = unit_merge_by_name(*u, k);
2415 if (r < 0) {
23a177ef
LP
2416 Unit *other;
2417
2418 /* Hmm, we couldn't merge the other unit into
2419 * ours? Then let's try it the other way
2420 * round */
2421
ac155bb8 2422 other = manager_get_unit((*u)->manager, k);
23a177ef
LP
2423 free(k);
2424
9946996c
LP
2425 if (other) {
2426 r = unit_merge(other, *u);
2427 if (r >= 0) {
23a177ef
LP
2428 *u = other;
2429 return merge_by_names(u, names, NULL);
2430 }
9946996c 2431 }
23a177ef
LP
2432
2433 return r;
2434 }
2435
2436 if (id == k)
2437 unit_choose_id(*u, id);
2438
2439 free(k);
2440 }
2441
2442 return 0;
2443}
2444
e537352b 2445static int load_from_path(Unit *u, const char *path) {
0301abf4 2446 int r;
e48614c4
ZJS
2447 _cleanup_set_free_free_ Set *symlink_names = NULL;
2448 _cleanup_fclose_ FILE *f = NULL;
2449 _cleanup_free_ char *filename = NULL;
2450 char *id = NULL;
23a177ef 2451 Unit *merged;
45fb0699 2452 struct stat st;
23a177ef
LP
2453
2454 assert(u);
e537352b 2455 assert(path);
3efd4195 2456
f975e971
LP
2457 symlink_names = set_new(string_hash_func, string_compare_func);
2458 if (!symlink_names)
87f0e418 2459 return -ENOMEM;
3efd4195 2460
036643a2
LP
2461 if (path_is_absolute(path)) {
2462
9946996c 2463 filename = strdup(path);
e48614c4
ZJS
2464 if (!filename)
2465 return -ENOMEM;
036643a2 2466
9946996c
LP
2467 r = open_follow(&filename, &f, symlink_names, &id);
2468 if (r < 0) {
036643a2
LP
2469 free(filename);
2470 filename = NULL;
2471
2472 if (r != -ENOENT)
e48614c4 2473 return r;
036643a2
LP
2474 }
2475
2476 } else {
2477 char **p;
2478
ac155bb8 2479 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
036643a2
LP
2480
2481 /* Instead of opening the path right away, we manually
2482 * follow all symlinks and add their name to our unit
2483 * name set while doing so */
9946996c 2484 filename = path_make_absolute(path, *p);
e48614c4
ZJS
2485 if (!filename)
2486 return -ENOMEM;
036643a2 2487
ac155bb8
MS
2488 if (u->manager->unit_path_cache &&
2489 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
2490 r = -ENOENT;
2491 else
2492 r = open_follow(&filename, &f, symlink_names, &id);
2493
2494 if (r < 0) {
036643a2
LP
2495 free(filename);
2496 filename = NULL;
2497
2498 if (r != -ENOENT)
e48614c4 2499 return r;
036643a2
LP
2500
2501 /* Empty the symlink names for the next run */
9946996c 2502 set_clear_free(symlink_names);
036643a2
LP
2503 continue;
2504 }
2505
2506 break;
2507 }
2508 }
034c6ed7 2509
e48614c4 2510 if (!filename)
8f05424d 2511 /* Hmm, no suitable file found? */
e48614c4 2512 return 0;
87f0e418 2513
23a177ef 2514 merged = u;
9946996c
LP
2515 r = merge_by_names(&merged, symlink_names, id);
2516 if (r < 0)
e48614c4 2517 return r;
87f0e418 2518
23a177ef 2519 if (merged != u) {
ac155bb8 2520 u->load_state = UNIT_MERGED;
e48614c4 2521 return 0;
034c6ed7
LP
2522 }
2523
e48614c4
ZJS
2524 if (fstat(fileno(f), &st) < 0)
2525 return -errno;
45fb0699 2526
00dc5d76 2527 if (null_or_empty(&st))
ac155bb8 2528 u->load_state = UNIT_MASKED;
00dc5d76 2529 else {
c2756a68
LP
2530 u->load_state = UNIT_LOADED;
2531
00dc5d76 2532 /* Now, parse the file contents */
e8e581bf
ZJS
2533 r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
2534 config_item_perf_lookup,
db5c0122 2535 (void*) load_fragment_gperf_lookup, false, true, u);
f975e971 2536 if (r < 0)
e48614c4 2537 return r;
00dc5d76 2538 }
b08d03ff 2539
ac155bb8
MS
2540 free(u->fragment_path);
2541 u->fragment_path = filename;
0301abf4 2542 filename = NULL;
87f0e418 2543
ac155bb8 2544 u->fragment_mtime = timespec_load(&st.st_mtim);
45fb0699 2545
1b64d026
LP
2546 if (u->source_path) {
2547 if (stat(u->source_path, &st) >= 0)
2548 u->source_mtime = timespec_load(&st.st_mtim);
2549 else
2550 u->source_mtime = 0;
2551 }
2552
e48614c4 2553 return 0;
0301abf4
LP
2554}
2555
e537352b 2556int unit_load_fragment(Unit *u) {
23a177ef 2557 int r;
294d81f1
LP
2558 Iterator i;
2559 const char *t;
0301abf4
LP
2560
2561 assert(u);
ac155bb8
MS
2562 assert(u->load_state == UNIT_STUB);
2563 assert(u->id);
23a177ef 2564
294d81f1
LP
2565 /* First, try to find the unit under its id. We always look
2566 * for unit files in the default directories, to make it easy
2567 * to override things by placing things in /etc/systemd/system */
9946996c
LP
2568 r = load_from_path(u, u->id);
2569 if (r < 0)
294d81f1
LP
2570 return r;
2571
2572 /* Try to find an alias we can load this with */
ac155bb8
MS
2573 if (u->load_state == UNIT_STUB)
2574 SET_FOREACH(t, u->names, i) {
294d81f1 2575
ac155bb8 2576 if (t == u->id)
294d81f1
LP
2577 continue;
2578
9946996c
LP
2579 r = load_from_path(u, t);
2580 if (r < 0)
294d81f1
LP
2581 return r;
2582
ac155bb8 2583 if (u->load_state != UNIT_STUB)
294d81f1
LP
2584 break;
2585 }
23a177ef 2586
294d81f1 2587 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 2588 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 2589
9946996c
LP
2590 r = load_from_path(u, u->fragment_path);
2591 if (r < 0)
23a177ef 2592 return r;
0301abf4 2593
ac155bb8 2594 if (u->load_state == UNIT_STUB) {
6ccb1b44
LP
2595 /* Hmm, this didn't work? Then let's get rid
2596 * of the fragment path stored for us, so that
2597 * we don't point to an invalid location. */
ac155bb8
MS
2598 free(u->fragment_path);
2599 u->fragment_path = NULL;
6ccb1b44
LP
2600 }
2601 }
2602
294d81f1 2603 /* Look for a template */
ac155bb8 2604 if (u->load_state == UNIT_STUB && u->instance) {
294d81f1
LP
2605 char *k;
2606
9946996c
LP
2607 k = unit_name_template(u->id);
2608 if (!k)
294d81f1
LP
2609 return -ENOMEM;
2610
2611 r = load_from_path(u, k);
2612 free(k);
0301abf4 2613
294d81f1 2614 if (r < 0)
9e2f7c11 2615 return r;
890f434c 2616
ac155bb8
MS
2617 if (u->load_state == UNIT_STUB)
2618 SET_FOREACH(t, u->names, i) {
87f0e418 2619
ac155bb8 2620 if (t == u->id)
23a177ef 2621 continue;
071830ff 2622
9946996c
LP
2623 k = unit_name_template(t);
2624 if (!k)
294d81f1
LP
2625 return -ENOMEM;
2626
2627 r = load_from_path(u, k);
2628 free(k);
2629
2630 if (r < 0)
23a177ef 2631 return r;
890f434c 2632
ac155bb8 2633 if (u->load_state != UNIT_STUB)
23a177ef
LP
2634 break;
2635 }
071830ff
LP
2636 }
2637
23a177ef 2638 return 0;
3efd4195 2639}
e537352b
LP
2640
2641void unit_dump_config_items(FILE *f) {
f975e971
LP
2642 static const struct {
2643 const ConfigParserCallback callback;
2644 const char *rvalue;
2645 } table[] = {
2646 { config_parse_int, "INTEGER" },
2647 { config_parse_unsigned, "UNSIGNED" },
9ba1a159 2648 { config_parse_bytes_size, "SIZE" },
f975e971
LP
2649 { config_parse_bool, "BOOLEAN" },
2650 { config_parse_string, "STRING" },
2651 { config_parse_path, "PATH" },
2652 { config_parse_unit_path_printf, "PATH" },
2653 { config_parse_strv, "STRING [...]" },
2654 { config_parse_exec_nice, "NICE" },
2655 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2656 { config_parse_exec_io_class, "IOCLASS" },
2657 { config_parse_exec_io_priority, "IOPRIORITY" },
2658 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2659 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2660 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2661 { config_parse_mode, "MODE" },
2662 { config_parse_unit_env_file, "FILE" },
2663 { config_parse_output, "OUTPUT" },
2664 { config_parse_input, "INPUT" },
2665 { config_parse_facility, "FACILITY" },
2666 { config_parse_level, "LEVEL" },
2667 { config_parse_exec_capabilities, "CAPABILITIES" },
2668 { config_parse_exec_secure_bits, "SECUREBITS" },
ec8927ca 2669 { config_parse_bounding_set, "BOUNDINGSET" },
f975e971 2670 { config_parse_limit, "LIMIT" },
f975e971 2671 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
2672 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2673 { config_parse_service_type, "SERVICETYPE" },
2674 { config_parse_service_restart, "SERVICERESTART" },
2675#ifdef HAVE_SYSV_COMPAT
2676 { config_parse_sysv_priority, "SYSVPRIORITY" },
2677#else
2678 { config_parse_warn_compat, "NOTSUPPORTED" },
2679#endif
2680 { config_parse_kill_mode, "KILLMODE" },
2681 { config_parse_kill_signal, "SIGNAL" },
2682 { config_parse_socket_listen, "SOCKET [...]" },
2683 { config_parse_socket_bind, "SOCKETBIND" },
2684 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 2685 { config_parse_sec, "SECONDS" },
d88a251b 2686 { config_parse_nsec, "NANOSECONDS" },
f975e971 2687 { config_parse_path_strv, "PATH [...]" },
7c8fa05c 2688 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
2689 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2690 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 2691 { config_parse_trigger_unit, "UNIT" },
f975e971 2692 { config_parse_timer, "TIMER" },
f975e971 2693 { config_parse_path_spec, "PATH" },
f975e971
LP
2694 { config_parse_notify_access, "ACCESS" },
2695 { config_parse_ip_tos, "TOS" },
2696 { config_parse_unit_condition_path, "CONDITION" },
2697 { config_parse_unit_condition_string, "CONDITION" },
2698 { config_parse_unit_condition_null, "CONDITION" },
a016b922 2699 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
2700 { config_parse_documentation, "URL" },
2701 { config_parse_service_timeout, "SECONDS" },
2702 { config_parse_start_limit_action, "ACTION" },
2703 { config_parse_set_status, "STATUS" },
2704 { config_parse_service_sockets, "SOCKETS" },
7f0386f6
LP
2705 { config_parse_environ, "ENVIRON" },
2706 { config_parse_syscall_filter, "SYSCALL" },
2707 { config_parse_cpu_shares, "SHARES" },
2708 { config_parse_memory_limit, "LIMIT" },
2709 { config_parse_device_allow, "DEVICE" },
2710 { config_parse_device_policy, "POLICY" },
2711 { config_parse_blockio_bandwidth, "BANDWIDTH" },
2712 { config_parse_blockio_weight, "WEIGHT" },
2713 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
2714 { config_parse_long, "LONG" },
2715 { config_parse_socket_service, "SERVICE" },
f975e971
LP
2716 };
2717
2718 const char *prev = NULL;
2719 const char *i;
2720
2721 assert(f);
e537352b 2722
f975e971
LP
2723 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2724 const char *rvalue = "OTHER", *lvalue;
2725 unsigned j;
2726 size_t prefix_len;
2727 const char *dot;
2728 const ConfigPerfItem *p;
2729
2730 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2731
2732 dot = strchr(i, '.');
2733 lvalue = dot ? dot + 1 : i;
2734 prefix_len = dot-i;
2735
2736 if (dot)
641906e9 2737 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
2738 if (prev)
2739 fputc('\n', f);
2740
2741 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2742 }
2743
2744 for (j = 0; j < ELEMENTSOF(table); j++)
2745 if (p->parse == table[j].callback) {
2746 rvalue = table[j].rvalue;
2747 break;
2748 }
2749
2750 fprintf(f, "%s=%s\n", lvalue, rvalue);
2751 prev = i;
2752 }
e537352b 2753}