]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/load-fragment.c
cgroup: expose cgroup attributes as unit properties on the bus
[thirdparty/systemd.git] / src / 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
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
87f0e418 22#include <linux/oom.h>
3efd4195
LP
23#include <assert.h>
24#include <errno.h>
25#include <string.h>
87f0e418
LP
26#include <unistd.h>
27#include <fcntl.h>
94f04347
LP
28#include <sched.h>
29#include <sys/prctl.h>
15ae422b 30#include <sys/mount.h>
25e870b5 31#include <linux/fs.h>
45fb0699 32#include <sys/stat.h>
3d57c6ab
LP
33#include <sys/time.h>
34#include <sys/resource.h>
3efd4195 35
87f0e418 36#include "unit.h"
3efd4195
LP
37#include "strv.h"
38#include "conf-parser.h"
39#include "load-fragment.h"
16354eff 40#include "log.h"
9eba9da4 41#include "ioprio.h"
94f04347
LP
42#include "securebits.h"
43#include "missing.h"
9e2f7c11 44#include "unit-name.h"
398ef8ba 45#include "bus-errors.h"
3efd4195 46
07459bb6 47#ifndef HAVE_SYSV_COMPAT
f975e971 48int config_parse_warn_compat(
07459bb6
FF
49 const char *filename,
50 unsigned line,
51 const char *section,
52 const char *lvalue,
2b583ce6 53 int ltype,
07459bb6
FF
54 const char *rvalue,
55 void *data,
56 void *userdata) {
57
58 log_debug("[%s:%u] Support for option %s= has been disabled at compile time and is ignored", filename, line, lvalue);
59 return 0;
60}
61#endif
62
f975e971 63int config_parse_unit_deps(
3efd4195
LP
64 const char *filename,
65 unsigned line,
66 const char *section,
67 const char *lvalue,
2b583ce6 68 int ltype,
3efd4195
LP
69 const char *rvalue,
70 void *data,
71 void *userdata) {
72
f975e971 73 UnitDependency d = ltype;
87f0e418 74 Unit *u = userdata;
3efd4195
LP
75 char *w;
76 size_t l;
77 char *state;
78
79 assert(filename);
80 assert(lvalue);
81 assert(rvalue);
3efd4195 82
f60f22df 83 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
9e2f7c11 84 char *t, *k;
3efd4195 85 int r;
3efd4195
LP
86
87 if (!(t = strndup(w, l)))
88 return -ENOMEM;
89
9e2f7c11 90 k = unit_name_printf(u, t);
3efd4195
LP
91 free(t);
92
9e2f7c11
LP
93 if (!k)
94 return -ENOMEM;
95
701cc384 96 r = unit_add_dependency_by_name(u, d, k, NULL, true);
9e2f7c11 97
c0b34696
LP
98 if (r < 0) {
99 log_error("Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
100 free(k);
101 return 0;
102 }
103
104 free(k);
3efd4195
LP
105 }
106
107 return 0;
108}
109
f975e971 110int config_parse_unit_names(
87d1515d
LP
111 const char *filename,
112 unsigned line,
113 const char *section,
114 const char *lvalue,
2b583ce6 115 int ltype,
87d1515d
LP
116 const char *rvalue,
117 void *data,
118 void *userdata) {
119
87f0e418 120 Unit *u = userdata;
87d1515d
LP
121 char *w;
122 size_t l;
123 char *state;
124
125 assert(filename);
126 assert(lvalue);
127 assert(rvalue);
128 assert(data);
129
f60f22df 130 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
9e2f7c11 131 char *t, *k;
87d1515d 132 int r;
87d1515d
LP
133
134 if (!(t = strndup(w, l)))
135 return -ENOMEM;
136
9e2f7c11 137 k = unit_name_printf(u, t);
87d1515d 138 free(t);
23a177ef 139
9e2f7c11
LP
140 if (!k)
141 return -ENOMEM;
142
143 r = unit_merge_by_name(u, k);
9e2f7c11 144
c0b34696
LP
145 if (r < 0) {
146 log_error("Failed to add name %s, ignoring: %s", k, strerror(-r));
147 free(k);
148 return 0;
149 }
150
151 free(k);
87d1515d
LP
152 }
153
154 return 0;
155}
156
f975e971 157int config_parse_unit_string_printf(
932921b5
LP
158 const char *filename,
159 unsigned line,
160 const char *section,
161 const char *lvalue,
2b583ce6 162 int ltype,
932921b5
LP
163 const char *rvalue,
164 void *data,
165 void *userdata) {
166
167 Unit *u = userdata;
f2d3769a 168 char **s = data;
932921b5
LP
169 char *k;
170
171 assert(filename);
172 assert(lvalue);
173 assert(rvalue);
f2d3769a
LP
174 assert(s);
175 assert(u);
932921b5
LP
176
177 if (!(k = unit_full_printf(u, rvalue)))
178 return -ENOMEM;
179
f2d3769a 180 free(*s);
932921b5 181 if (*k)
f2d3769a 182 *s = k;
932921b5
LP
183 else {
184 free(k);
f2d3769a 185 *s = NULL;
932921b5
LP
186 }
187
188 return 0;
189}
190
f975e971 191int config_parse_unit_strv_printf(
8fef7659
LP
192 const char *filename,
193 unsigned line,
194 const char *section,
195 const char *lvalue,
196 int ltype,
197 const char *rvalue,
198 void *data,
199 void *userdata) {
200
201 Unit *u = userdata;
202 char *k;
203 int r;
204
205 assert(filename);
206 assert(lvalue);
207 assert(rvalue);
208 assert(u);
209
210 k = unit_full_printf(u, rvalue);
211 if (!k)
212 return -ENOMEM;
213
214 r = config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata);
215 free(k);
216
217 return r;
218}
219
f975e971 220int config_parse_unit_path_printf(
6ea832a2
LP
221 const char *filename,
222 unsigned line,
223 const char *section,
224 const char *lvalue,
225 int ltype,
226 const char *rvalue,
227 void *data,
228 void *userdata) {
229
230 Unit *u = userdata;
231 char **s = data;
232 char *k;
233
234 assert(filename);
235 assert(lvalue);
236 assert(rvalue);
237 assert(s);
238 assert(u);
239
240 if (!(k = unit_full_printf(u, rvalue)))
241 return -ENOMEM;
242
243 if (!path_is_absolute(k)) {
244 log_error("[%s:%u] Not an absolute path: %s", filename, line, k);
245 free(k);
246 return -EINVAL;
247 }
248
249 path_kill_slashes(k);
250
251 free(*s);
252 *s = k;
253
254 return 0;
255}
256
f975e971 257int config_parse_socket_listen(
42f4e3c4
LP
258 const char *filename,
259 unsigned line,
260 const char *section,
261 const char *lvalue,
2b583ce6 262 int ltype,
42f4e3c4
LP
263 const char *rvalue,
264 void *data,
265 void *userdata) {
266
49f91047 267 SocketPort *p, *tail;
542563ba 268 Socket *s;
16354eff 269
42f4e3c4
LP
270 assert(filename);
271 assert(lvalue);
272 assert(rvalue);
273 assert(data);
274
542563ba
LP
275 s = (Socket*) data;
276
277 if (!(p = new0(SocketPort, 1)))
278 return -ENOMEM;
279
280 if (streq(lvalue, "ListenFIFO")) {
281 p->type = SOCKET_FIFO;
282
1fd45a90 283 if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
542563ba
LP
284 free(p);
285 return -ENOMEM;
286 }
01f78473
LP
287
288 path_kill_slashes(p->path);
b0a3f2bc
LP
289
290 } else if (streq(lvalue, "ListenSpecial")) {
291 p->type = SOCKET_SPECIAL;
292
1fd45a90 293 if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
b0a3f2bc
LP
294 free(p);
295 return -ENOMEM;
296 }
297
298 path_kill_slashes(p->path);
299
916abb21
LP
300 } else if (streq(lvalue, "ListenMessageQueue")) {
301
302 p->type = SOCKET_MQUEUE;
303
1fd45a90 304 if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
916abb21
LP
305 free(p);
306 return -ENOMEM;
307 }
308
309 path_kill_slashes(p->path);
310
7a22745a 311 } else if (streq(lvalue, "ListenNetlink")) {
1fd45a90
LP
312 char *k;
313 int r;
314
7a22745a 315 p->type = SOCKET_SOCKET;
1fd45a90
LP
316 k = unit_full_printf(UNIT(s), rvalue);
317 r = socket_address_parse_netlink(&p->address, k);
318 free(k);
7a22745a 319
1fd45a90 320 if (r < 0) {
7a22745a
LP
321 log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
322 free(p);
323 return 0;
324 }
325
542563ba 326 } else {
1fd45a90
LP
327 char *k;
328 int r;
329
542563ba 330 p->type = SOCKET_SOCKET;
1fd45a90
LP
331 k = unit_full_printf(UNIT(s), rvalue);
332 r = socket_address_parse(&p->address, k);
333 free(k);
542563ba 334
1fd45a90 335 if (r < 0) {
c0b34696 336 log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
542563ba 337 free(p);
c0b34696 338 return 0;
542563ba
LP
339 }
340
341 if (streq(lvalue, "ListenStream"))
342 p->address.type = SOCK_STREAM;
343 else if (streq(lvalue, "ListenDatagram"))
344 p->address.type = SOCK_DGRAM;
345 else {
346 assert(streq(lvalue, "ListenSequentialPacket"));
347 p->address.type = SOCK_SEQPACKET;
348 }
349
350 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
c0b34696 351 log_error("[%s:%u] Address family not supported, ignoring: %s", filename, line, rvalue);
542563ba 352 free(p);
c0b34696 353 return 0;
542563ba 354 }
16354eff
LP
355 }
356
542563ba 357 p->fd = -1;
49f91047
LP
358
359 if (s->ports) {
360 LIST_FIND_TAIL(SocketPort, port, s->ports, tail);
361 LIST_INSERT_AFTER(SocketPort, port, s->ports, tail, p);
362 } else
363 LIST_PREPEND(SocketPort, port, s->ports, p);
542563ba 364
16354eff 365 return 0;
42f4e3c4
LP
366}
367
f975e971 368int config_parse_socket_bind(
42f4e3c4
LP
369 const char *filename,
370 unsigned line,
371 const char *section,
372 const char *lvalue,
2b583ce6 373 int ltype,
42f4e3c4
LP
374 const char *rvalue,
375 void *data,
376 void *userdata) {
377
542563ba 378 Socket *s;
c0120d99 379 SocketAddressBindIPv6Only b;
42f4e3c4
LP
380
381 assert(filename);
382 assert(lvalue);
383 assert(rvalue);
384 assert(data);
385
542563ba
LP
386 s = (Socket*) data;
387
c0120d99
LP
388 if ((b = socket_address_bind_ipv6_only_from_string(rvalue)) < 0) {
389 int r;
390
391 if ((r = parse_boolean(rvalue)) < 0) {
c0b34696
LP
392 log_error("[%s:%u] Failed to parse bind IPv6 only value, ignoring: %s", filename, line, rvalue);
393 return 0;
c0120d99 394 }
42f4e3c4 395
c0120d99
LP
396 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
397 } else
398 s->bind_ipv6_only = b;
542563ba 399
42f4e3c4
LP
400 return 0;
401}
402
f975e971 403int config_parse_exec_nice(
034c6ed7
LP
404 const char *filename,
405 unsigned line,
406 const char *section,
407 const char *lvalue,
2b583ce6 408 int ltype,
034c6ed7
LP
409 const char *rvalue,
410 void *data,
411 void *userdata) {
412
fb33a393 413 ExecContext *c = data;
bd40a2d8 414 int priority;
034c6ed7
LP
415
416 assert(filename);
417 assert(lvalue);
418 assert(rvalue);
419 assert(data);
420
bd40a2d8 421 if (safe_atoi(rvalue, &priority) < 0) {
c0b34696
LP
422 log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue);
423 return 0;
034c6ed7
LP
424 }
425
426 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
c0b34696
LP
427 log_error("[%s:%u] Nice priority out of range, ignoring: %s", filename, line, rvalue);
428 return 0;
034c6ed7
LP
429 }
430
fb33a393 431 c->nice = priority;
71155933 432 c->nice_set = true;
fb33a393 433
034c6ed7
LP
434 return 0;
435}
436
f975e971 437int config_parse_exec_oom_score_adjust(
034c6ed7
LP
438 const char *filename,
439 unsigned line,
440 const char *section,
441 const char *lvalue,
2b583ce6 442 int ltype,
034c6ed7
LP
443 const char *rvalue,
444 void *data,
445 void *userdata) {
446
fb33a393 447 ExecContext *c = data;
bd40a2d8 448 int oa;
034c6ed7
LP
449
450 assert(filename);
451 assert(lvalue);
452 assert(rvalue);
453 assert(data);
454
bd40a2d8 455 if (safe_atoi(rvalue, &oa) < 0) {
dd6c17b1 456 log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue);
c0b34696 457 return 0;
034c6ed7
LP
458 }
459
dd6c17b1
LP
460 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
461 log_error("[%s:%u] OOM score adjust value out of range, ignoring: %s", filename, line, rvalue);
c0b34696 462 return 0;
034c6ed7
LP
463 }
464
dd6c17b1
LP
465 c->oom_score_adjust = oa;
466 c->oom_score_adjust_set = true;
fb33a393 467
034c6ed7
LP
468 return 0;
469}
470
f975e971 471int config_parse_exec(
034c6ed7
LP
472 const char *filename,
473 unsigned line,
474 const char *section,
475 const char *lvalue,
2b583ce6 476 int ltype,
034c6ed7
LP
477 const char *rvalue,
478 void *data,
479 void *userdata) {
480
61e5d8ed
LP
481 ExecCommand **e = data, *nce;
482 char *path, **n;
034c6ed7 483 unsigned k;
034c6ed7
LP
484
485 assert(filename);
486 assert(lvalue);
487 assert(rvalue);
61e5d8ed 488 assert(e);
034c6ed7 489
6c666e26
LP
490 /* We accept an absolute path as first argument, or
491 * alternatively an absolute prefixed with @ to allow
492 * overriding of argv[0]. */
493
f975e971
LP
494 e += ltype;
495
61e5d8ed
LP
496 for (;;) {
497 char *w;
498 size_t l;
499 char *state;
b708e7ce 500 bool honour_argv0 = false, ignore = false;
6c666e26 501
61e5d8ed
LP
502 path = NULL;
503 nce = NULL;
504 n = NULL;
6c666e26 505
61e5d8ed 506 rvalue += strspn(rvalue, WHITESPACE);
034c6ed7 507
61e5d8ed
LP
508 if (rvalue[0] == 0)
509 break;
034c6ed7 510
b708e7ce
LP
511 if (rvalue[0] == '-') {
512 ignore = true;
513 rvalue ++;
514 }
515
516 if (rvalue[0] == '@') {
517 honour_argv0 = true;
518 rvalue ++;
519 }
61e5d8ed 520
b708e7ce 521 if (*rvalue != '/') {
c0b34696
LP
522 log_error("[%s:%u] Invalid executable path in command line, ignoring: %s", filename, line, rvalue);
523 return 0;
6c666e26 524 }
034c6ed7 525
61e5d8ed
LP
526 k = 0;
527 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
f90cf44c 528 if (strncmp(w, ";", MAX(l, 1U)) == 0)
61e5d8ed 529 break;
034c6ed7 530
61e5d8ed
LP
531 k++;
532 }
034c6ed7 533
b708e7ce 534 if (!(n = new(char*, k + !honour_argv0)))
61e5d8ed
LP
535 return -ENOMEM;
536
537 k = 0;
61e5d8ed 538 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
f90cf44c 539 if (strncmp(w, ";", MAX(l, 1U)) == 0)
61e5d8ed
LP
540 break;
541
b708e7ce
LP
542 if (honour_argv0 && w == rvalue) {
543 assert(!path);
544 if (!(path = cunescape_length(w, l)))
61e5d8ed 545 goto fail;
61e5d8ed
LP
546 } else {
547 if (!(n[k++] = cunescape_length(w, l)))
548 goto fail;
549 }
550 }
551
552 n[k] = NULL;
553
554 if (!n[0]) {
c0b34696 555 log_error("[%s:%u] Invalid command line, ignoring: %s", filename, line, rvalue);
61e5d8ed 556 strv_free(n);
c0b34696 557 return 0;
61e5d8ed
LP
558 }
559
560 if (!path)
561 if (!(path = strdup(n[0])))
562 goto fail;
6c666e26 563
61e5d8ed 564 assert(path_is_absolute(path));
6c666e26 565
61e5d8ed
LP
566 if (!(nce = new0(ExecCommand, 1)))
567 goto fail;
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
588 return -ENOMEM;
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
f975e971 594int config_parse_socket_bindtodevice(
acbb0225
LP
595 const char *filename,
596 unsigned line,
597 const char *section,
598 const char *lvalue,
2b583ce6 599 int ltype,
acbb0225
LP
600 const char *rvalue,
601 void *data,
602 void *userdata) {
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, "*")) {
613 if (!(n = strdup(rvalue)))
614 return -ENOMEM;
615 } else
616 n = NULL;
617
618 free(s->bind_to_device);
619 s->bind_to_device = n;
620
621 return 0;
622}
623
f975e971
LP
624DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
625DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
87f0e418 626
f975e971 627int config_parse_facility(
071830ff
LP
628 const char *filename,
629 unsigned line,
630 const char *section,
631 const char *lvalue,
2b583ce6 632 int ltype,
071830ff
LP
633 const char *rvalue,
634 void *data,
635 void *userdata) {
636
071830ff 637
94f04347 638 int *o = data, x;
071830ff
LP
639
640 assert(filename);
641 assert(lvalue);
642 assert(rvalue);
643 assert(data);
644
7d76f312 645 if ((x = log_facility_unshifted_from_string(rvalue)) < 0) {
c0b34696
LP
646 log_error("[%s:%u] Failed to parse log facility, ignoring: %s", filename, line, rvalue);
647 return 0;
0d87eb42 648 }
94f04347 649
7d76f312 650 *o = (x << 3) | LOG_PRI(*o);
071830ff
LP
651
652 return 0;
653}
654
f975e971 655int config_parse_level(
071830ff
LP
656 const char *filename,
657 unsigned line,
658 const char *section,
659 const char *lvalue,
2b583ce6 660 int ltype,
071830ff
LP
661 const char *rvalue,
662 void *data,
663 void *userdata) {
664
071830ff 665
94f04347 666 int *o = data, x;
071830ff
LP
667
668 assert(filename);
669 assert(lvalue);
670 assert(rvalue);
671 assert(data);
672
0d87eb42 673 if ((x = log_level_from_string(rvalue)) < 0) {
c0b34696
LP
674 log_error("[%s:%u] Failed to parse log level, ignoring: %s", filename, line, rvalue);
675 return 0;
0d87eb42 676 }
071830ff 677
7d76f312 678 *o = (*o & LOG_FACMASK) | x;
94f04347
LP
679 return 0;
680}
681
f975e971 682int config_parse_exec_io_class(
94f04347
LP
683 const char *filename,
684 unsigned line,
685 const char *section,
686 const char *lvalue,
2b583ce6 687 int ltype,
94f04347
LP
688 const char *rvalue,
689 void *data,
690 void *userdata) {
691
692 ExecContext *c = data;
693 int x;
694
695 assert(filename);
696 assert(lvalue);
697 assert(rvalue);
698 assert(data);
699
0d87eb42 700 if ((x = ioprio_class_from_string(rvalue)) < 0) {
c0b34696
LP
701 log_error("[%s:%u] Failed to parse IO scheduling class, ignoring: %s", filename, line, rvalue);
702 return 0;
0d87eb42 703 }
94f04347
LP
704
705 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
706 c->ioprio_set = true;
707
708 return 0;
709}
710
f975e971 711int config_parse_exec_io_priority(
94f04347
LP
712 const char *filename,
713 unsigned line,
714 const char *section,
715 const char *lvalue,
2b583ce6 716 int ltype,
94f04347
LP
717 const char *rvalue,
718 void *data,
719 void *userdata) {
720
721 ExecContext *c = data;
722 int i;
723
724 assert(filename);
725 assert(lvalue);
726 assert(rvalue);
727 assert(data);
728
729 if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) {
c0b34696
LP
730 log_error("[%s:%u] Failed to parse io priority, ignoring: %s", filename, line, rvalue);
731 return 0;
071830ff
LP
732 }
733
94f04347
LP
734 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
735 c->ioprio_set = true;
736
071830ff
LP
737 return 0;
738}
739
f975e971 740int config_parse_exec_cpu_sched_policy(
9eba9da4
LP
741 const char *filename,
742 unsigned line,
743 const char *section,
744 const char *lvalue,
2b583ce6 745 int ltype,
9eba9da4
LP
746 const char *rvalue,
747 void *data,
748 void *userdata) {
749
94f04347
LP
750
751 ExecContext *c = data;
752 int x;
753
754 assert(filename);
755 assert(lvalue);
756 assert(rvalue);
757 assert(data);
758
0d87eb42 759 if ((x = sched_policy_from_string(rvalue)) < 0) {
c0b34696
LP
760 log_error("[%s:%u] Failed to parse CPU scheduling policy, ignoring: %s", filename, line, rvalue);
761 return 0;
0d87eb42 762 }
94f04347
LP
763
764 c->cpu_sched_policy = x;
765 c->cpu_sched_set = true;
766
767 return 0;
768}
769
f975e971 770int config_parse_exec_cpu_sched_prio(
94f04347
LP
771 const char *filename,
772 unsigned line,
773 const char *section,
774 const char *lvalue,
2b583ce6 775 int ltype,
94f04347
LP
776 const char *rvalue,
777 void *data,
778 void *userdata) {
9eba9da4
LP
779
780 ExecContext *c = data;
781 int i;
782
783 assert(filename);
784 assert(lvalue);
785 assert(rvalue);
786 assert(data);
787
94f04347
LP
788 /* On Linux RR/FIFO have the same range */
789 if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) {
c0b34696
LP
790 log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, rvalue);
791 return 0;
94f04347 792 }
9eba9da4 793
94f04347
LP
794 c->cpu_sched_priority = i;
795 c->cpu_sched_set = true;
796
797 return 0;
798}
799
f975e971 800int config_parse_exec_cpu_affinity(
94f04347
LP
801 const char *filename,
802 unsigned line,
803 const char *section,
804 const char *lvalue,
2b583ce6 805 int ltype,
94f04347
LP
806 const char *rvalue,
807 void *data,
808 void *userdata) {
809
810 ExecContext *c = data;
811 char *w;
812 size_t l;
813 char *state;
814
815 assert(filename);
816 assert(lvalue);
817 assert(rvalue);
818 assert(data);
819
f60f22df 820 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
94f04347
LP
821 char *t;
822 int r;
823 unsigned cpu;
824
825 if (!(t = strndup(w, l)))
826 return -ENOMEM;
827
487393e9
LP
828 r = safe_atou(t, &cpu);
829 free(t);
830
82c121a4
LP
831 if (!(c->cpuset))
832 if (!(c->cpuset = cpu_set_malloc(&c->cpuset_ncpus)))
833 return -ENOMEM;
834
82c121a4 835 if (r < 0 || cpu >= c->cpuset_ncpus) {
c0b34696
LP
836 log_error("[%s:%u] Failed to parse CPU affinity, ignoring: %s", filename, line, rvalue);
837 return 0;
9eba9da4 838 }
94f04347 839
82c121a4 840 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
9eba9da4
LP
841 }
842
94f04347
LP
843 return 0;
844}
845
f975e971 846int config_parse_exec_capabilities(
94f04347
LP
847 const char *filename,
848 unsigned line,
849 const char *section,
850 const char *lvalue,
2b583ce6 851 int ltype,
94f04347
LP
852 const char *rvalue,
853 void *data,
854 void *userdata) {
855
856 ExecContext *c = data;
857 cap_t cap;
858
859 assert(filename);
860 assert(lvalue);
861 assert(rvalue);
862 assert(data);
863
864 if (!(cap = cap_from_text(rvalue))) {
865 if (errno == ENOMEM)
866 return -ENOMEM;
867
c0b34696
LP
868 log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue);
869 return 0;
94f04347
LP
870 }
871
872 if (c->capabilities)
873 cap_free(c->capabilities);
874 c->capabilities = cap;
875
876 return 0;
877}
878
f975e971 879int config_parse_exec_secure_bits(
94f04347
LP
880 const char *filename,
881 unsigned line,
882 const char *section,
883 const char *lvalue,
2b583ce6 884 int ltype,
94f04347
LP
885 const char *rvalue,
886 void *data,
887 void *userdata) {
888
889 ExecContext *c = data;
890 char *w;
891 size_t l;
892 char *state;
893
894 assert(filename);
895 assert(lvalue);
896 assert(rvalue);
897 assert(data);
898
f60f22df 899 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
94f04347
LP
900 if (first_word(w, "keep-caps"))
901 c->secure_bits |= SECURE_KEEP_CAPS;
902 else if (first_word(w, "keep-caps-locked"))
903 c->secure_bits |= SECURE_KEEP_CAPS_LOCKED;
904 else if (first_word(w, "no-setuid-fixup"))
905 c->secure_bits |= SECURE_NO_SETUID_FIXUP;
906 else if (first_word(w, "no-setuid-fixup-locked"))
907 c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED;
908 else if (first_word(w, "noroot"))
909 c->secure_bits |= SECURE_NOROOT;
910 else if (first_word(w, "noroot-locked"))
911 c->secure_bits |= SECURE_NOROOT_LOCKED;
9eba9da4 912 else {
c0b34696
LP
913 log_error("[%s:%u] Failed to parse secure bits, ignoring: %s", filename, line, rvalue);
914 return 0;
9eba9da4
LP
915 }
916 }
917
94f04347
LP
918 return 0;
919}
920
f975e971 921int config_parse_exec_bounding_set(
94f04347
LP
922 const char *filename,
923 unsigned line,
924 const char *section,
925 const char *lvalue,
2b583ce6 926 int ltype,
94f04347
LP
927 const char *rvalue,
928 void *data,
929 void *userdata) {
930
931 ExecContext *c = data;
932 char *w;
933 size_t l;
934 char *state;
260abb78
LP
935 bool invert = false;
936 uint64_t sum = 0;
94f04347
LP
937
938 assert(filename);
939 assert(lvalue);
940 assert(rvalue);
941 assert(data);
942
260abb78
LP
943 if (rvalue[0] == '~') {
944 invert = true;
945 rvalue++;
946 }
947
948 /* Note that we store this inverted internally, since the
949 * kernel wants it like this. But we actually expose it
950 * non-inverted everywhere to have a fully normalized
951 * interface. */
952
f60f22df 953 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
94f04347
LP
954 char *t;
955 int r;
956 cap_value_t cap;
957
958 if (!(t = strndup(w, l)))
959 return -ENOMEM;
960
961 r = cap_from_name(t, &cap);
962 free(t);
963
964 if (r < 0) {
c0b34696
LP
965 log_error("[%s:%u] Failed to parse capability bounding set, ignoring: %s", filename, line, rvalue);
966 return 0;
94f04347
LP
967 }
968
260abb78 969 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
94f04347 970 }
9eba9da4 971
260abb78
LP
972 if (invert)
973 c->capability_bounding_set_drop |= sum;
974 else
975 c->capability_bounding_set_drop |= ~sum;
976
9eba9da4
LP
977 return 0;
978}
979
f975e971 980int config_parse_exec_timer_slack_nsec(
9eba9da4
LP
981 const char *filename,
982 unsigned line,
983 const char *section,
984 const char *lvalue,
2b583ce6 985 int ltype,
9eba9da4
LP
986 const char *rvalue,
987 void *data,
988 void *userdata) {
989
990 ExecContext *c = data;
94f04347 991 unsigned long u;
9eba9da4
LP
992
993 assert(filename);
994 assert(lvalue);
995 assert(rvalue);
996 assert(data);
997
bd40a2d8 998 if (safe_atolu(rvalue, &u) < 0) {
c0b34696
LP
999 log_error("[%s:%u] Failed to parse time slack value, ignoring: %s", filename, line, rvalue);
1000 return 0;
9eba9da4
LP
1001 }
1002
03fae018 1003 c->timer_slack_nsec = u;
94f04347
LP
1004
1005 return 0;
1006}
1007
f975e971 1008int config_parse_limit(
94f04347
LP
1009 const char *filename,
1010 unsigned line,
1011 const char *section,
1012 const char *lvalue,
2b583ce6 1013 int ltype,
94f04347
LP
1014 const char *rvalue,
1015 void *data,
1016 void *userdata) {
1017
1018 struct rlimit **rl = data;
1019 unsigned long long u;
94f04347
LP
1020
1021 assert(filename);
1022 assert(lvalue);
1023 assert(rvalue);
1024 assert(data);
1025
f975e971
LP
1026 rl += ltype;
1027
3d57c6ab
LP
1028 if (streq(rvalue, "infinity"))
1029 u = (unsigned long long) RLIM_INFINITY;
1030 else if (safe_atollu(rvalue, &u) < 0) {
c0b34696
LP
1031 log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue);
1032 return 0;
94f04347
LP
1033 }
1034
1035 if (!*rl)
1036 if (!(*rl = new(struct rlimit, 1)))
1037 return -ENOMEM;
9eba9da4 1038
94f04347 1039 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
9eba9da4
LP
1040 return 0;
1041}
1042
f975e971 1043int config_parse_unit_cgroup(
8e274523
LP
1044 const char *filename,
1045 unsigned line,
1046 const char *section,
1047 const char *lvalue,
2b583ce6 1048 int ltype,
8e274523
LP
1049 const char *rvalue,
1050 void *data,
1051 void *userdata) {
1052
1053 Unit *u = userdata;
1054 char *w;
1055 size_t l;
1056 char *state;
1057
f60f22df 1058 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
f284f69a 1059 char *t, *k;
8e274523
LP
1060 int r;
1061
f284f69a
LP
1062 t = strndup(w, l);
1063 if (!t)
1064 return -ENOMEM;
1065
1066 k = unit_full_printf(u, t);
1067 free(t);
1068
1069 if (!k)
1070 return -ENOMEM;
1071
1072 t = cunescape(k);
1073 free(k);
1074
1075 if (!t)
8e274523
LP
1076 return -ENOMEM;
1077
1078 r = unit_add_cgroup_from_text(u, t);
1079 free(t);
1080
c0b34696
LP
1081 if (r < 0) {
1082 log_error("[%s:%u] Failed to parse cgroup value, ignoring: %s", filename, line, rvalue);
1083 return 0;
1084 }
8e274523
LP
1085 }
1086
1087 return 0;
1088}
1089
07459bb6 1090#ifdef HAVE_SYSV_COMPAT
f975e971 1091int config_parse_sysv_priority(
a9a1e00a
LP
1092 const char *filename,
1093 unsigned line,
1094 const char *section,
1095 const char *lvalue,
2b583ce6 1096 int ltype,
a9a1e00a
LP
1097 const char *rvalue,
1098 void *data,
1099 void *userdata) {
1100
1101 int *priority = data;
bd40a2d8 1102 int i;
a9a1e00a
LP
1103
1104 assert(filename);
1105 assert(lvalue);
1106 assert(rvalue);
1107 assert(data);
1108
bd40a2d8 1109 if (safe_atoi(rvalue, &i) < 0 || i < 0) {
c0b34696
LP
1110 log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue);
1111 return 0;
a9a1e00a
LP
1112 }
1113
1114 *priority = (int) i;
1115 return 0;
1116}
07459bb6 1117#endif
a9a1e00a 1118
f975e971 1119int config_parse_fsck_passno(
2ba545f1
LP
1120 const char *filename,
1121 unsigned line,
1122 const char *section,
1123 const char *lvalue,
2b583ce6 1124 int ltype,
2ba545f1
LP
1125 const char *rvalue,
1126 void *data,
1127 void *userdata) {
1128
1129 int *passno = data;
bd40a2d8 1130 int i;
2ba545f1
LP
1131
1132 assert(filename);
1133 assert(lvalue);
1134 assert(rvalue);
1135 assert(data);
1136
bd40a2d8 1137 if (safe_atoi(rvalue, &i) || i < 0) {
2ba545f1
LP
1138 log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue);
1139 return 0;
1140 }
1141
1142 *passno = (int) i;
1143 return 0;
1144}
1145
f975e971 1146DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
50159e6a 1147
f975e971 1148int config_parse_kill_signal(
2e22afe9
LP
1149 const char *filename,
1150 unsigned line,
1151 const char *section,
1152 const char *lvalue,
2b583ce6 1153 int ltype,
2e22afe9
LP
1154 const char *rvalue,
1155 void *data,
1156 void *userdata) {
1157
1158 int *sig = data;
1159 int r;
1160
1161 assert(filename);
1162 assert(lvalue);
1163 assert(rvalue);
1164 assert(sig);
1165
8a0867d6 1166 if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
c0b34696
LP
1167 log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue);
1168 return 0;
2e22afe9
LP
1169 }
1170
1171 *sig = r;
1172 return 0;
1173}
1174
f975e971 1175int config_parse_exec_mount_flags(
15ae422b
LP
1176 const char *filename,
1177 unsigned line,
1178 const char *section,
1179 const char *lvalue,
2b583ce6 1180 int ltype,
15ae422b
LP
1181 const char *rvalue,
1182 void *data,
1183 void *userdata) {
1184
1185 ExecContext *c = data;
1186 char *w;
1187 size_t l;
1188 char *state;
1189 unsigned long flags = 0;
1190
1191 assert(filename);
1192 assert(lvalue);
1193 assert(rvalue);
1194 assert(data);
1195
f60f22df 1196 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
f90cf44c 1197 if (strncmp(w, "shared", MAX(l, 6U)) == 0)
15ae422b 1198 flags |= MS_SHARED;
f90cf44c 1199 else if (strncmp(w, "slave", MAX(l, 5U)) == 0)
15ae422b 1200 flags |= MS_SLAVE;
f90cf44c 1201 else if (strncmp(w, "private", MAX(l, 7U)) == 0)
15ae422b
LP
1202 flags |= MS_PRIVATE;
1203 else {
c0b34696
LP
1204 log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue);
1205 return 0;
15ae422b
LP
1206 }
1207 }
1208
1209 c->mount_flags = flags;
1210 return 0;
1211}
1212
f975e971 1213int config_parse_timer(
871d7de4
LP
1214 const char *filename,
1215 unsigned line,
1216 const char *section,
1217 const char *lvalue,
2b583ce6 1218 int ltype,
871d7de4
LP
1219 const char *rvalue,
1220 void *data,
1221 void *userdata) {
1222
1223 Timer *t = data;
1224 usec_t u;
871d7de4
LP
1225 TimerValue *v;
1226 TimerBase b;
1227
1228 assert(filename);
1229 assert(lvalue);
1230 assert(rvalue);
1231 assert(data);
1232
1233 if ((b = timer_base_from_string(lvalue)) < 0) {
c0b34696
LP
1234 log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue);
1235 return 0;
871d7de4
LP
1236 }
1237
bd40a2d8 1238 if (parse_usec(rvalue, &u) < 0) {
c0b34696
LP
1239 log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue);
1240 return 0;
871d7de4
LP
1241 }
1242
1243 if (!(v = new0(TimerValue, 1)))
1244 return -ENOMEM;
1245
1246 v->base = b;
1247 v->value = u;
1248
1249 LIST_PREPEND(TimerValue, value, t->values, v);
1250
1251 return 0;
1252}
1253
f975e971 1254int config_parse_timer_unit(
871d7de4
LP
1255 const char *filename,
1256 unsigned line,
1257 const char *section,
1258 const char *lvalue,
2b583ce6 1259 int ltype,
871d7de4
LP
1260 const char *rvalue,
1261 void *data,
1262 void *userdata) {
1263
1264 Timer *t = data;
1265 int r;
398ef8ba
LP
1266 DBusError error;
1267
1268 assert(filename);
1269 assert(lvalue);
1270 assert(rvalue);
1271 assert(data);
1272
1273 dbus_error_init(&error);
871d7de4
LP
1274
1275 if (endswith(rvalue, ".timer")) {
c0b34696
LP
1276 log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue);
1277 return 0;
871d7de4
LP
1278 }
1279
398ef8ba 1280 if ((r = manager_load_unit(t->meta.manager, rvalue, NULL, NULL, &t->unit)) < 0) {
c0b34696 1281 log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
398ef8ba 1282 dbus_error_free(&error);
c0b34696 1283 return 0;
871d7de4
LP
1284 }
1285
1286 return 0;
1287}
1288
f975e971 1289int config_parse_path_spec(
01f78473
LP
1290 const char *filename,
1291 unsigned line,
1292 const char *section,
1293 const char *lvalue,
2b583ce6 1294 int ltype,
01f78473
LP
1295 const char *rvalue,
1296 void *data,
1297 void *userdata) {
1298
1299 Path *p = data;
1300 PathSpec *s;
1301 PathType b;
1302
1303 assert(filename);
1304 assert(lvalue);
1305 assert(rvalue);
1306 assert(data);
1307
1308 if ((b = path_type_from_string(lvalue)) < 0) {
c0b34696
LP
1309 log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue);
1310 return 0;
01f78473
LP
1311 }
1312
1313 if (!path_is_absolute(rvalue)) {
c0b34696
LP
1314 log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, rvalue);
1315 return 0;
01f78473
LP
1316 }
1317
1318 if (!(s = new0(PathSpec, 1)))
1319 return -ENOMEM;
1320
1321 if (!(s->path = strdup(rvalue))) {
1322 free(s);
1323 return -ENOMEM;
1324 }
1325
1326 path_kill_slashes(s->path);
1327
1328 s->type = b;
1329 s->inotify_fd = -1;
1330
1331 LIST_PREPEND(PathSpec, spec, p->specs, s);
1332
1333 return 0;
1334}
1335
f975e971 1336int config_parse_path_unit(
01f78473
LP
1337 const char *filename,
1338 unsigned line,
1339 const char *section,
1340 const char *lvalue,
2b583ce6 1341 int ltype,
01f78473
LP
1342 const char *rvalue,
1343 void *data,
1344 void *userdata) {
1345
1346 Path *t = data;
1347 int r;
398ef8ba
LP
1348 DBusError error;
1349
1350 assert(filename);
1351 assert(lvalue);
1352 assert(rvalue);
1353 assert(data);
1354
1355 dbus_error_init(&error);
01f78473
LP
1356
1357 if (endswith(rvalue, ".path")) {
c0b34696
LP
1358 log_error("[%s:%u] Unit cannot be of type path, ignoring: %s", filename, line, rvalue);
1359 return 0;
01f78473
LP
1360 }
1361
398ef8ba 1362 if ((r = manager_load_unit(t->meta.manager, rvalue, NULL, &error, &t->unit)) < 0) {
c0b34696 1363 log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
398ef8ba 1364 dbus_error_free(&error);
c0b34696 1365 return 0;
01f78473
LP
1366 }
1367
1368 return 0;
1369}
1370
f975e971 1371int config_parse_socket_service(
d9ff321a
LP
1372 const char *filename,
1373 unsigned line,
1374 const char *section,
1375 const char *lvalue,
2b583ce6 1376 int ltype,
d9ff321a
LP
1377 const char *rvalue,
1378 void *data,
1379 void *userdata) {
1380
1381 Socket *s = data;
1382 int r;
1383 DBusError error;
1384
1385 assert(filename);
1386 assert(lvalue);
1387 assert(rvalue);
1388 assert(data);
1389
1390 dbus_error_init(&error);
1391
f976f3f6
LP
1392 if (!endswith(rvalue, ".service")) {
1393 log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue);
d9ff321a
LP
1394 return 0;
1395 }
1396
1397 if ((r = manager_load_unit(s->meta.manager, rvalue, NULL, &error, (Unit**) &s->service)) < 0) {
1398 log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
1399 dbus_error_free(&error);
1400 return 0;
1401 }
1402
1403 return 0;
1404}
1405
f975e971 1406int config_parse_service_sockets(
f976f3f6
LP
1407 const char *filename,
1408 unsigned line,
1409 const char *section,
1410 const char *lvalue,
2b583ce6 1411 int ltype,
f976f3f6
LP
1412 const char *rvalue,
1413 void *data,
1414 void *userdata) {
1415
1416 Service *s = data;
1417 int r;
1418 DBusError error;
1419 char *state, *w;
1420 size_t l;
1421
1422 assert(filename);
1423 assert(lvalue);
1424 assert(rvalue);
1425 assert(data);
1426
1427 dbus_error_init(&error);
1428
1429 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1430 char *t;
1431 Unit *sock;
1432
1433 if (!(t = strndup(w, l)))
1434 return -ENOMEM;
1435
1436 if (!endswith(t, ".socket")) {
1437 log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue);
1438 free(t);
1439 continue;
1440 }
1441
1442 r = manager_load_unit(s->meta.manager, t, NULL, &error, &sock);
1443 free(t);
1444
1445 if (r < 0) {
1446 log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
1447 dbus_error_free(&error);
1448 continue;
1449 }
1450
1451 if ((r = set_ensure_allocated(&s->configured_sockets, trivial_hash_func, trivial_compare_func)) < 0)
1452 return r;
1453
1454 if ((r = set_put(s->configured_sockets, sock)) < 0)
1455 return r;
1456 }
1457
1458 return 0;
1459}
1460
f975e971 1461int config_parse_unit_env_file(
ddb26e18
LP
1462 const char *filename,
1463 unsigned line,
1464 const char *section,
1465 const char *lvalue,
2b583ce6 1466 int ltype,
ddb26e18
LP
1467 const char *rvalue,
1468 void *data,
1469 void *userdata) {
1470
8c7be95e 1471 char ***env = data, **k;
8fef7659
LP
1472 Unit *u = userdata;
1473 char *s;
ddb26e18
LP
1474
1475 assert(filename);
1476 assert(lvalue);
1477 assert(rvalue);
1478 assert(data);
1479
8fef7659
LP
1480 s = unit_full_printf(u, rvalue);
1481 if (!s)
1482 return -ENOMEM;
1483
1484 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
1485 log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s);
1486 free(s);
afe4bfe2
LP
1487 return 0;
1488 }
1489
8fef7659
LP
1490 k = strv_append(*env, s);
1491 free(s);
1492 if (!k)
8c7be95e 1493 return -ENOMEM;
ddb26e18 1494
8c7be95e
LP
1495 strv_free(*env);
1496 *env = k;
ddb26e18 1497
8c7be95e 1498 return 0;
ddb26e18
LP
1499}
1500
f975e971 1501int config_parse_ip_tos(
4fd5948e
LP
1502 const char *filename,
1503 unsigned line,
1504 const char *section,
1505 const char *lvalue,
2b583ce6 1506 int ltype,
4fd5948e
LP
1507 const char *rvalue,
1508 void *data,
1509 void *userdata) {
1510
1511 int *ip_tos = data, x;
4fd5948e
LP
1512
1513 assert(filename);
1514 assert(lvalue);
1515 assert(rvalue);
1516 assert(data);
1517
1518 if ((x = ip_tos_from_string(rvalue)) < 0)
bd40a2d8 1519 if (safe_atoi(rvalue, &x) < 0) {
c0b34696
LP
1520 log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue);
1521 return 0;
4fd5948e
LP
1522 }
1523
1524 *ip_tos = x;
1525 return 0;
1526}
1527
f975e971 1528int config_parse_unit_condition_path(
52661efd
LP
1529 const char *filename,
1530 unsigned line,
1531 const char *section,
1532 const char *lvalue,
2b583ce6 1533 int ltype,
52661efd
LP
1534 const char *rvalue,
1535 void *data,
1536 void *userdata) {
1537
2b583ce6 1538 ConditionType cond = ltype;
52661efd 1539 Unit *u = data;
267632f0 1540 bool trigger, negate;
52661efd
LP
1541 Condition *c;
1542
1543 assert(filename);
1544 assert(lvalue);
1545 assert(rvalue);
1546 assert(data);
1547
267632f0
LP
1548 if ((trigger = rvalue[0] == '|'))
1549 rvalue++;
1550
52661efd
LP
1551 if ((negate = rvalue[0] == '!'))
1552 rvalue++;
1553
1554 if (!path_is_absolute(rvalue)) {
d257ddef 1555 log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, rvalue);
52661efd
LP
1556 return 0;
1557 }
1558
2b583ce6 1559 if (!(c = condition_new(cond, rvalue, trigger, negate)))
52661efd
LP
1560 return -ENOMEM;
1561
1562 LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
1563 return 0;
1564}
1565
f975e971 1566int config_parse_unit_condition_string(
039655a4
LP
1567 const char *filename,
1568 unsigned line,
1569 const char *section,
1570 const char *lvalue,
2b583ce6 1571 int ltype,
039655a4
LP
1572 const char *rvalue,
1573 void *data,
1574 void *userdata) {
1575
41584525 1576 ConditionType cond = ltype;
039655a4 1577 Unit *u = data;
267632f0 1578 bool trigger, negate;
039655a4
LP
1579 Condition *c;
1580
1581 assert(filename);
1582 assert(lvalue);
1583 assert(rvalue);
1584 assert(data);
1585
267632f0
LP
1586 if ((trigger = rvalue[0] == '|'))
1587 rvalue++;
1588
039655a4
LP
1589 if ((negate = rvalue[0] == '!'))
1590 rvalue++;
1591
41584525 1592 if (!(c = condition_new(cond, rvalue, trigger, negate)))
039655a4
LP
1593 return -ENOMEM;
1594
1595 LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
1596 return 0;
1597}
1598
f975e971 1599int config_parse_unit_condition_null(
d257ddef
LP
1600 const char *filename,
1601 unsigned line,
1602 const char *section,
1603 const char *lvalue,
2b583ce6 1604 int ltype,
d257ddef
LP
1605 const char *rvalue,
1606 void *data,
1607 void *userdata) {
1608
1609 Unit *u = data;
1610 Condition *c;
267632f0 1611 bool trigger, negate;
d257ddef
LP
1612 int b;
1613
1614 assert(filename);
1615 assert(lvalue);
1616 assert(rvalue);
1617 assert(data);
1618
267632f0
LP
1619 if ((trigger = rvalue[0] == '|'))
1620 rvalue++;
1621
d257ddef
LP
1622 if ((negate = rvalue[0] == '!'))
1623 rvalue++;
1624
1625 if ((b = parse_boolean(rvalue)) < 0) {
1626 log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue);
1627 return 0;
1628 }
1629
1630 if (!b)
1631 negate = !negate;
1632
267632f0 1633 if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate)))
d257ddef
LP
1634 return -ENOMEM;
1635
1636 LIST_PREPEND(Condition, conditions, u->meta.conditions, c);
1637 return 0;
1638}
1639
f975e971 1640DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
c952c6ec 1641
ab1f0633
LP
1642int config_parse_unit_cgroup_attr(
1643 const char *filename,
1644 unsigned line,
1645 const char *section,
1646 const char *lvalue,
1647 int ltype,
1648 const char *rvalue,
1649 void *data,
1650 void *userdata) {
1651
1652 Unit *u = data;
1653 char **l;
1654 int r;
1655
1656 assert(filename);
1657 assert(lvalue);
1658 assert(rvalue);
1659 assert(data);
1660
1661 l = strv_split_quoted(rvalue);
1662 if (!l)
1663 return -ENOMEM;
1664
1665 if (strv_length(l) != 2) {
1666 log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
1667 strv_free(l);
1668 return 0;
1669 }
1670
1671 r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL);
1672 strv_free(l);
1673
1674 if (r < 0) {
1675 log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
1676 return 0;
1677 }
1678
1679 return 0;
1680}
1681
1682int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
1683 Unit *u = data;
1684 int r;
1685 unsigned long ul;
1686 char *t;
1687
1688 assert(filename);
1689 assert(lvalue);
1690 assert(rvalue);
1691 assert(data);
1692
1693 if (safe_atolu(rvalue, &ul) < 0 || ul < 1) {
1694 log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue);
1695 return 0;
1696 }
1697
1698 if (asprintf(&t, "%lu", ul) < 0)
1699 return -ENOMEM;
1700
1701 r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL);
1702 free(t);
1703
1704 if (r < 0) {
1705 log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
1706 return 0;
1707 }
1708
1709 return 0;
1710}
1711
1712int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
1713 Unit *u = data;
1714 int r;
1715 off_t sz;
1716 char *t;
1717
1718 assert(filename);
1719 assert(lvalue);
1720 assert(rvalue);
1721 assert(data);
1722
1723 if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) {
1724 log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue);
1725 return 0;
1726 }
1727
1728 if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
1729 return -ENOMEM;
1730
1731 r = unit_add_cgroup_attribute(u,
1732 "memory",
1733 streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
1734 t, NULL);
1735 free(t);
1736
1737 if (r < 0) {
1738 log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
1739 return 0;
1740 }
1741
1742 return 0;
1743}
1744
1745static int device_map(const char *controller, const char *name, const char *value, char **ret) {
1746 struct stat st;
1747 char **l;
1748
1749 l = strv_split_quoted(value);
1750 if (!l)
1751 return -ENOMEM;
1752
1753 assert(strv_length(l) >= 1);
1754
1755 if (streq(l[0], "*")) {
1756
1757 if (asprintf(ret, "a *:*%s%s",
1758 isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
1759 strv_free(l);
1760 return -ENOMEM;
1761 }
1762
1763 } else {
1764 if (lstat(l[0], &st) < 0) {
1765 log_warning("Couldn't stat device %s", l[0]);
1766 strv_free(l);
1767 return -errno;
1768 }
1769
1770 if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
1771 log_warning("%s is not a device.", l[0]);
1772 strv_free(l);
1773 return -ENODEV;
1774 }
1775
1776 if (asprintf(ret, "%c %u:%u%s%s",
1777 S_ISCHR(st.st_mode) ? 'c' : 'b',
1778 major(st.st_rdev), minor(st.st_rdev),
1779 isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
1780
1781 strv_free(l);
1782 return -ENOMEM;
1783 }
1784 }
1785
1786 strv_free(l);
1787 return 0;
1788}
1789
1790int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
1791 Unit *u = data;
1792 char **l;
1793 int r;
1794 unsigned k;
1795
1796 assert(filename);
1797 assert(lvalue);
1798 assert(rvalue);
1799 assert(data);
1800
1801 l = strv_split_quoted(rvalue);
1802 if (!l)
1803 return -ENOMEM;
1804
1805 k = strv_length(l);
1806 if (k < 1 || k > 2) {
1807 log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
1808 strv_free(l);
1809 return 0;
1810 }
1811
1812 if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) {
1813 log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue);
1814 strv_free(l);
1815 return 0;
1816 }
1817
1818 if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
1819 log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
1820 strv_free(l);
1821 return 0;
1822 }
1823 strv_free(l);
1824
1825 r = unit_add_cgroup_attribute(u, "devices",
1826 streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
1827 rvalue, device_map);
1828
1829 if (r < 0) {
1830 log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
1831 return 0;
1832 }
1833
1834 return 0;
1835}
1836
071830ff 1837#define FOLLOW_MAX 8
87f0e418 1838
9e2f7c11 1839static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
0301abf4 1840 unsigned c = 0;
87f0e418
LP
1841 int fd, r;
1842 FILE *f;
0301abf4 1843 char *id = NULL;
87f0e418
LP
1844
1845 assert(filename);
1846 assert(*filename);
1847 assert(_f);
1848 assert(names);
1849
0301abf4
LP
1850 /* This will update the filename pointer if the loaded file is
1851 * reached by a symlink. The old string will be freed. */
87f0e418 1852
0301abf4 1853 for (;;) {
2c7108c4 1854 char *target, *name;
87f0e418 1855
0301abf4
LP
1856 if (c++ >= FOLLOW_MAX)
1857 return -ELOOP;
1858
b08d03ff
LP
1859 path_kill_slashes(*filename);
1860
87f0e418 1861 /* Add the file name we are currently looking at to
8f05424d
LP
1862 * the names of this unit, but only if it is a valid
1863 * unit name. */
0301abf4 1864 name = file_name_from_path(*filename);
87f0e418 1865
15e11d81 1866 if (unit_name_is_valid(name, true)) {
8f05424d 1867
15e11d81
LP
1868 id = set_get(names, name);
1869 if (!id) {
1870 id = strdup(name);
1871 if (!id)
8f05424d 1872 return -ENOMEM;
87f0e418 1873
15e11d81
LP
1874 r = set_put(names, id);
1875 if (r < 0) {
8f05424d
LP
1876 free(id);
1877 return r;
1878 }
87f0e418 1879 }
87f0e418
LP
1880 }
1881
0301abf4
LP
1882 /* Try to open the file name, but don't if its a symlink */
1883 if ((fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0)
87f0e418
LP
1884 break;
1885
0301abf4
LP
1886 if (errno != ELOOP)
1887 return -errno;
1888
87f0e418 1889 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
2c7108c4 1890 if ((r = readlink_and_make_absolute(*filename, &target)) < 0)
0301abf4 1891 return r;
87f0e418 1892
0301abf4 1893 free(*filename);
2c7108c4 1894 *filename = target;
87f0e418
LP
1895 }
1896
8f05424d 1897 if (!(f = fdopen(fd, "re"))) {
87f0e418 1898 r = -errno;
9e2f7c11 1899 close_nointr_nofail(fd);
0301abf4 1900 return r;
87f0e418
LP
1901 }
1902
1903 *_f = f;
9e2f7c11 1904 *_final = id;
0301abf4 1905 return 0;
87f0e418
LP
1906}
1907
23a177ef
LP
1908static int merge_by_names(Unit **u, Set *names, const char *id) {
1909 char *k;
1910 int r;
1911
1912 assert(u);
1913 assert(*u);
1914 assert(names);
1915
1916 /* Let's try to add in all symlink names we found */
1917 while ((k = set_steal_first(names))) {
1918
1919 /* First try to merge in the other name into our
1920 * unit */
1921 if ((r = unit_merge_by_name(*u, k)) < 0) {
1922 Unit *other;
1923
1924 /* Hmm, we couldn't merge the other unit into
1925 * ours? Then let's try it the other way
1926 * round */
1927
1928 other = manager_get_unit((*u)->meta.manager, k);
1929 free(k);
1930
1931 if (other)
1932 if ((r = unit_merge(other, *u)) >= 0) {
1933 *u = other;
1934 return merge_by_names(u, names, NULL);
1935 }
1936
1937 return r;
1938 }
1939
1940 if (id == k)
1941 unit_choose_id(*u, id);
1942
1943 free(k);
1944 }
1945
1946 return 0;
1947}
1948
e537352b 1949static int load_from_path(Unit *u, const char *path) {
0301abf4 1950 int r;
87f0e418 1951 Set *symlink_names;
23a177ef
LP
1952 FILE *f = NULL;
1953 char *filename = NULL, *id = NULL;
1954 Unit *merged;
45fb0699 1955 struct stat st;
23a177ef
LP
1956
1957 assert(u);
e537352b 1958 assert(path);
3efd4195 1959
f975e971
LP
1960 symlink_names = set_new(string_hash_func, string_compare_func);
1961 if (!symlink_names)
87f0e418 1962 return -ENOMEM;
3efd4195 1963
036643a2
LP
1964 if (path_is_absolute(path)) {
1965
1966 if (!(filename = strdup(path))) {
1967 r = -ENOMEM;
1968 goto finish;
1969 }
1970
1971 if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) {
1972 free(filename);
1973 filename = NULL;
1974
1975 if (r != -ENOENT)
1976 goto finish;
1977 }
1978
1979 } else {
1980 char **p;
1981
84e3543e 1982 STRV_FOREACH(p, u->meta.manager->lookup_paths.unit_path) {
036643a2
LP
1983
1984 /* Instead of opening the path right away, we manually
1985 * follow all symlinks and add their name to our unit
1986 * name set while doing so */
1987 if (!(filename = path_make_absolute(path, *p))) {
1988 r = -ENOMEM;
1989 goto finish;
1990 }
1991
fe51822e
LP
1992 if (u->meta.manager->unit_path_cache &&
1993 !set_get(u->meta.manager->unit_path_cache, filename))
1994 r = -ENOENT;
1995 else
1996 r = open_follow(&filename, &f, symlink_names, &id);
1997
1998 if (r < 0) {
036643a2
LP
1999 char *sn;
2000
2001 free(filename);
2002 filename = NULL;
2003
2004 if (r != -ENOENT)
2005 goto finish;
2006
2007 /* Empty the symlink names for the next run */
2008 while ((sn = set_steal_first(symlink_names)))
2009 free(sn);
3efd4195 2010
036643a2
LP
2011 continue;
2012 }
2013
2014 break;
2015 }
2016 }
034c6ed7 2017
036643a2 2018 if (!filename) {
8f05424d 2019 /* Hmm, no suitable file found? */
23a177ef 2020 r = 0;
0301abf4
LP
2021 goto finish;
2022 }
87f0e418 2023
23a177ef
LP
2024 merged = u;
2025 if ((r = merge_by_names(&merged, symlink_names, id)) < 0)
0301abf4 2026 goto finish;
87f0e418 2027
23a177ef 2028 if (merged != u) {
e537352b 2029 u->meta.load_state = UNIT_MERGED;
23a177ef
LP
2030 r = 0;
2031 goto finish;
034c6ed7
LP
2032 }
2033
45fb0699
LP
2034 zero(st);
2035 if (fstat(fileno(f), &st) < 0) {
2036 r = -errno;
2037 goto finish;
2038 }
2039
00dc5d76 2040 if (null_or_empty(&st))
6daf4f90 2041 u->meta.load_state = UNIT_MASKED;
00dc5d76
LP
2042 else {
2043 /* Now, parse the file contents */
f975e971
LP
2044 r = config_parse(filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, (void*) load_fragment_gperf_lookup, false, u);
2045 if (r < 0)
00dc5d76
LP
2046 goto finish;
2047
2048 u->meta.load_state = UNIT_LOADED;
2049 }
b08d03ff 2050
6be1e7d5
LP
2051 free(u->meta.fragment_path);
2052 u->meta.fragment_path = filename;
0301abf4 2053 filename = NULL;
87f0e418 2054
45fb0699
LP
2055 u->meta.fragment_mtime = timespec_load(&st.st_mtim);
2056
23a177ef 2057 r = 0;
87f0e418
LP
2058
2059finish:
53ec43c6 2060 set_free_free(symlink_names);
0301abf4
LP
2061 free(filename);
2062
23a177ef
LP
2063 if (f)
2064 fclose(f);
2065
0301abf4
LP
2066 return r;
2067}
2068
e537352b 2069int unit_load_fragment(Unit *u) {
23a177ef 2070 int r;
294d81f1
LP
2071 Iterator i;
2072 const char *t;
0301abf4
LP
2073
2074 assert(u);
294d81f1
LP
2075 assert(u->meta.load_state == UNIT_STUB);
2076 assert(u->meta.id);
23a177ef 2077
294d81f1
LP
2078 /* First, try to find the unit under its id. We always look
2079 * for unit files in the default directories, to make it easy
2080 * to override things by placing things in /etc/systemd/system */
2081 if ((r = load_from_path(u, u->meta.id)) < 0)
2082 return r;
2083
2084 /* Try to find an alias we can load this with */
2085 if (u->meta.load_state == UNIT_STUB)
2086 SET_FOREACH(t, u->meta.names, i) {
2087
2088 if (t == u->meta.id)
2089 continue;
2090
2091 if ((r = load_from_path(u, t)) < 0)
2092 return r;
2093
2094 if (u->meta.load_state != UNIT_STUB)
2095 break;
2096 }
23a177ef 2097
294d81f1 2098 /* And now, try looking for it under the suggested (originally linked) path */
6ccb1b44
LP
2099 if (u->meta.load_state == UNIT_STUB && u->meta.fragment_path) {
2100
e537352b 2101 if ((r = load_from_path(u, u->meta.fragment_path)) < 0)
23a177ef 2102 return r;
0301abf4 2103
6ccb1b44
LP
2104 if (u->meta.load_state == UNIT_STUB) {
2105 /* Hmm, this didn't work? Then let's get rid
2106 * of the fragment path stored for us, so that
2107 * we don't point to an invalid location. */
2108 free(u->meta.fragment_path);
2109 u->meta.fragment_path = NULL;
2110 }
2111 }
2112
294d81f1
LP
2113 /* Look for a template */
2114 if (u->meta.load_state == UNIT_STUB && u->meta.instance) {
2115 char *k;
2116
2117 if (!(k = unit_name_template(u->meta.id)))
2118 return -ENOMEM;
2119
2120 r = load_from_path(u, k);
2121 free(k);
0301abf4 2122
294d81f1 2123 if (r < 0)
9e2f7c11 2124 return r;
890f434c 2125
e537352b 2126 if (u->meta.load_state == UNIT_STUB)
23a177ef 2127 SET_FOREACH(t, u->meta.names, i) {
87f0e418 2128
9e2f7c11 2129 if (t == u->meta.id)
23a177ef 2130 continue;
071830ff 2131
294d81f1
LP
2132 if (!(k = unit_name_template(t)))
2133 return -ENOMEM;
2134
2135 r = load_from_path(u, k);
2136 free(k);
2137
2138 if (r < 0)
23a177ef 2139 return r;
890f434c 2140
e537352b 2141 if (u->meta.load_state != UNIT_STUB)
23a177ef
LP
2142 break;
2143 }
071830ff
LP
2144 }
2145
23a177ef 2146 return 0;
3efd4195 2147}
e537352b
LP
2148
2149void unit_dump_config_items(FILE *f) {
f975e971
LP
2150 static const struct {
2151 const ConfigParserCallback callback;
2152 const char *rvalue;
2153 } table[] = {
2154 { config_parse_int, "INTEGER" },
2155 { config_parse_unsigned, "UNSIGNED" },
2156 { config_parse_size, "SIZE" },
2157 { config_parse_bool, "BOOLEAN" },
2158 { config_parse_string, "STRING" },
2159 { config_parse_path, "PATH" },
2160 { config_parse_unit_path_printf, "PATH" },
2161 { config_parse_strv, "STRING [...]" },
2162 { config_parse_exec_nice, "NICE" },
2163 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
2164 { config_parse_exec_io_class, "IOCLASS" },
2165 { config_parse_exec_io_priority, "IOPRIORITY" },
2166 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
2167 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
2168 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
2169 { config_parse_mode, "MODE" },
2170 { config_parse_unit_env_file, "FILE" },
2171 { config_parse_output, "OUTPUT" },
2172 { config_parse_input, "INPUT" },
2173 { config_parse_facility, "FACILITY" },
2174 { config_parse_level, "LEVEL" },
2175 { config_parse_exec_capabilities, "CAPABILITIES" },
2176 { config_parse_exec_secure_bits, "SECUREBITS" },
2177 { config_parse_exec_bounding_set, "BOUNDINGSET" },
2178 { config_parse_exec_timer_slack_nsec, "TIMERSLACK" },
2179 { config_parse_limit, "LIMIT" },
2180 { config_parse_unit_cgroup, "CGROUP [...]" },
2181 { config_parse_unit_deps, "UNIT [...]" },
2182 { config_parse_unit_names, "UNIT [...]" },
2183 { config_parse_exec, "PATH [ARGUMENT [...]]" },
2184 { config_parse_service_type, "SERVICETYPE" },
2185 { config_parse_service_restart, "SERVICERESTART" },
2186#ifdef HAVE_SYSV_COMPAT
2187 { config_parse_sysv_priority, "SYSVPRIORITY" },
2188#else
2189 { config_parse_warn_compat, "NOTSUPPORTED" },
2190#endif
2191 { config_parse_kill_mode, "KILLMODE" },
2192 { config_parse_kill_signal, "SIGNAL" },
2193 { config_parse_socket_listen, "SOCKET [...]" },
2194 { config_parse_socket_bind, "SOCKETBIND" },
2195 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
2196 { config_parse_usec, "SECONDS" },
2197 { config_parse_path_strv, "PATH [...]" },
2198 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
2199 { config_parse_unit_string_printf, "STRING" },
2200 { config_parse_timer, "TIMER" },
2201 { config_parse_timer_unit, "NAME" },
2202 { config_parse_path_spec, "PATH" },
2203 { config_parse_path_unit, "UNIT" },
2204 { config_parse_notify_access, "ACCESS" },
2205 { config_parse_ip_tos, "TOS" },
2206 { config_parse_unit_condition_path, "CONDITION" },
2207 { config_parse_unit_condition_string, "CONDITION" },
2208 { config_parse_unit_condition_null, "CONDITION" },
2209 };
2210
2211 const char *prev = NULL;
2212 const char *i;
2213
2214 assert(f);
e537352b 2215
f975e971
LP
2216 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
2217 const char *rvalue = "OTHER", *lvalue;
2218 unsigned j;
2219 size_t prefix_len;
2220 const char *dot;
2221 const ConfigPerfItem *p;
2222
2223 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
2224
2225 dot = strchr(i, '.');
2226 lvalue = dot ? dot + 1 : i;
2227 prefix_len = dot-i;
2228
2229 if (dot)
2230 if (!prev || strncmp(prev, i, prefix_len+1) != 0) {
2231 if (prev)
2232 fputc('\n', f);
2233
2234 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
2235 }
2236
2237 for (j = 0; j < ELEMENTSOF(table); j++)
2238 if (p->parse == table[j].callback) {
2239 rvalue = table[j].rvalue;
2240 break;
2241 }
2242
2243 fprintf(f, "%s=%s\n", lvalue, rvalue);
2244 prev = i;
2245 }
e537352b 2246}