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