]> git.ipfire.org Git - people/ms/systemd.git/blame - load-fragment.c
load-fragment: replace % specifiers in descriptions
[people/ms/systemd.git] / load-fragment.c
CommitLineData
3efd4195
LP
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
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>
3efd4195 31
87f0e418 32#include "unit.h"
3efd4195
LP
33#include "strv.h"
34#include "conf-parser.h"
35#include "load-fragment.h"
16354eff 36#include "log.h"
9eba9da4 37#include "ioprio.h"
94f04347
LP
38#include "securebits.h"
39#include "missing.h"
9e2f7c11 40#include "unit-name.h"
3efd4195 41
0d87eb42
LP
42#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \
43 static int function( \
44 const char *filename, \
45 unsigned line, \
46 const char *section, \
47 const char *lvalue, \
48 const char *rvalue, \
49 void *data, \
50 void *userdata) { \
51 \
52 type *i = data, x; \
53 \
54 assert(filename); \
55 assert(lvalue); \
56 assert(rvalue); \
57 assert(data); \
58 \
59 if ((x = name##_from_string(rvalue)) < 0) { \
60 log_error("[%s:%u] " msg ": %s", filename, line, rvalue); \
61 return -EBADMSG; \
62 } \
63 \
64 *i = x; \
65 \
66 return 0; \
67 }
68
42f4e3c4 69static int config_parse_deps(
3efd4195
LP
70 const char *filename,
71 unsigned line,
72 const char *section,
73 const char *lvalue,
74 const char *rvalue,
75 void *data,
76 void *userdata) {
77
87f0e418
LP
78 UnitDependency d = PTR_TO_UINT(data);
79 Unit *u = userdata;
3efd4195
LP
80 char *w;
81 size_t l;
82 char *state;
83
84 assert(filename);
85 assert(lvalue);
86 assert(rvalue);
3efd4195 87
f62c0e4f 88 FOREACH_WORD(w, l, rvalue, state) {
9e2f7c11 89 char *t, *k;
3efd4195 90 int r;
3efd4195
LP
91
92 if (!(t = strndup(w, l)))
93 return -ENOMEM;
94
9e2f7c11 95 k = unit_name_printf(u, t);
3efd4195
LP
96 free(t);
97
9e2f7c11
LP
98 if (!k)
99 return -ENOMEM;
100
701cc384 101 r = unit_add_dependency_by_name(u, d, k, NULL, true);
9e2f7c11
LP
102 free(k);
103
3efd4195
LP
104 if (r < 0)
105 return r;
3efd4195
LP
106 }
107
108 return 0;
109}
110
42f4e3c4 111static int config_parse_names(
87d1515d
LP
112 const char *filename,
113 unsigned line,
114 const char *section,
115 const char *lvalue,
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
f62c0e4f 130 FOREACH_WORD(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);
144 free(k);
145
23a177ef
LP
146 if (r < 0)
147 return r;
87d1515d
LP
148 }
149
150 return 0;
151}
152
932921b5
LP
153static int config_parse_description(
154 const char *filename,
155 unsigned line,
156 const char *section,
157 const char *lvalue,
158 const char *rvalue,
159 void *data,
160 void *userdata) {
161
162 Unit *u = userdata;
163 char *k;
164
165 assert(filename);
166 assert(lvalue);
167 assert(rvalue);
168 assert(data);
169
170 if (!(k = unit_full_printf(u, rvalue)))
171 return -ENOMEM;
172
173 free(u->meta.description);
174
175 if (*k)
176 u->meta.description = k;
177 else {
178 free(k);
179 u->meta.description = NULL;
180 }
181
182 return 0;
183}
184
42f4e3c4
LP
185static int config_parse_listen(
186 const char *filename,
187 unsigned line,
188 const char *section,
189 const char *lvalue,
190 const char *rvalue,
191 void *data,
192 void *userdata) {
193
16354eff 194 int r;
542563ba
LP
195 SocketPort *p;
196 Socket *s;
16354eff 197
42f4e3c4
LP
198 assert(filename);
199 assert(lvalue);
200 assert(rvalue);
201 assert(data);
202
542563ba
LP
203 s = (Socket*) data;
204
205 if (!(p = new0(SocketPort, 1)))
206 return -ENOMEM;
207
208 if (streq(lvalue, "ListenFIFO")) {
209 p->type = SOCKET_FIFO;
210
211 if (!(p->path = strdup(rvalue))) {
212 free(p);
213 return -ENOMEM;
214 }
215 } else {
216 p->type = SOCKET_SOCKET;
217
218 if ((r = socket_address_parse(&p->address, rvalue)) < 0) {
219 log_error("[%s:%u] Failed to parse address value: %s", filename, line, rvalue);
220 free(p);
221 return r;
222 }
223
224 if (streq(lvalue, "ListenStream"))
225 p->address.type = SOCK_STREAM;
226 else if (streq(lvalue, "ListenDatagram"))
227 p->address.type = SOCK_DGRAM;
228 else {
229 assert(streq(lvalue, "ListenSequentialPacket"));
230 p->address.type = SOCK_SEQPACKET;
231 }
232
233 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
234 free(p);
235 return -EPROTONOSUPPORT;
236 }
16354eff
LP
237 }
238
542563ba 239 p->fd = -1;
034c6ed7 240 LIST_PREPEND(SocketPort, port, s->ports, p);
542563ba 241
16354eff 242 return 0;
42f4e3c4
LP
243}
244
034c6ed7 245static int config_parse_socket_bind(
42f4e3c4
LP
246 const char *filename,
247 unsigned line,
248 const char *section,
249 const char *lvalue,
250 const char *rvalue,
251 void *data,
252 void *userdata) {
253
542563ba
LP
254 int r;
255 Socket *s;
42f4e3c4
LP
256
257 assert(filename);
258 assert(lvalue);
259 assert(rvalue);
260 assert(data);
261
542563ba
LP
262 s = (Socket*) data;
263
264 if ((r = parse_boolean(rvalue)) < 0) {
265 log_error("[%s:%u] Failed to parse bind IPv6 only value: %s", filename, line, rvalue);
266 return r;
16354eff 267 }
42f4e3c4 268
542563ba
LP
269 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
270
42f4e3c4
LP
271 return 0;
272}
273
034c6ed7
LP
274static int config_parse_nice(
275 const char *filename,
276 unsigned line,
277 const char *section,
278 const char *lvalue,
279 const char *rvalue,
280 void *data,
281 void *userdata) {
282
fb33a393
LP
283 ExecContext *c = data;
284 int priority, r;
034c6ed7
LP
285
286 assert(filename);
287 assert(lvalue);
288 assert(rvalue);
289 assert(data);
290
291 if ((r = safe_atoi(rvalue, &priority)) < 0) {
292 log_error("[%s:%u] Failed to parse nice priority: %s", filename, line, rvalue);
293 return r;
294 }
295
296 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
297 log_error("[%s:%u] Nice priority out of range: %s", filename, line, rvalue);
298 return -ERANGE;
299 }
300
fb33a393
LP
301 c->nice = priority;
302 c->nice_set = false;
303
034c6ed7
LP
304 return 0;
305}
306
307static int config_parse_oom_adjust(
308 const char *filename,
309 unsigned line,
310 const char *section,
311 const char *lvalue,
312 const char *rvalue,
313 void *data,
314 void *userdata) {
315
fb33a393
LP
316 ExecContext *c = data;
317 int oa, r;
034c6ed7
LP
318
319 assert(filename);
320 assert(lvalue);
321 assert(rvalue);
322 assert(data);
323
324 if ((r = safe_atoi(rvalue, &oa)) < 0) {
325 log_error("[%s:%u] Failed to parse OOM adjust value: %s", filename, line, rvalue);
326 return r;
327 }
328
329 if (oa < OOM_DISABLE || oa > OOM_ADJUST_MAX) {
330 log_error("[%s:%u] OOM adjust value out of range: %s", filename, line, rvalue);
331 return -ERANGE;
332 }
333
fb33a393
LP
334 c->oom_adjust = oa;
335 c->oom_adjust_set = true;
336
034c6ed7
LP
337 return 0;
338}
339
b5a0699f 340static int config_parse_mode(
034c6ed7
LP
341 const char *filename,
342 unsigned line,
343 const char *section,
344 const char *lvalue,
345 const char *rvalue,
346 void *data,
347 void *userdata) {
348
349 mode_t *m = data;
350 long l;
351 char *x = NULL;
352
353 assert(filename);
354 assert(lvalue);
355 assert(rvalue);
356 assert(data);
357
358 errno = 0;
359 l = strtol(rvalue, &x, 8);
360 if (!x || *x || errno) {
b5a0699f 361 log_error("[%s:%u] Failed to parse mode value: %s", filename, line, rvalue);
034c6ed7
LP
362 return errno ? -errno : -EINVAL;
363 }
364
b5a0699f
LP
365 if (l < 0000 || l > 07777) {
366 log_error("[%s:%u] mode value out of range: %s", filename, line, rvalue);
034c6ed7
LP
367 return -ERANGE;
368 }
369
370 *m = (mode_t) l;
371 return 0;
372}
373
374static int config_parse_exec(
375 const char *filename,
376 unsigned line,
377 const char *section,
378 const char *lvalue,
379 const char *rvalue,
380 void *data,
381 void *userdata) {
382
a6a80b4f 383 ExecCommand **e = data, *nce = NULL;
034c6ed7
LP
384 char **n;
385 char *w;
386 unsigned k;
387 size_t l;
388 char *state;
389
390 assert(filename);
391 assert(lvalue);
392 assert(rvalue);
393 assert(data);
394
395 k = 0;
396 FOREACH_WORD_QUOTED(w, l, rvalue, state)
397 k++;
398
399 if (!(n = new(char*, k+1)))
400 return -ENOMEM;
401
44d8db9e 402 k = 0;
034c6ed7
LP
403 FOREACH_WORD_QUOTED(w, l, rvalue, state)
404 if (!(n[k++] = strndup(w, l)))
405 goto fail;
406
407 n[k] = NULL;
408
0301abf4 409 if (!n[0] || !path_is_absolute(n[0])) {
034c6ed7
LP
410 log_error("[%s:%u] Invalid executable path in command line: %s", filename, line, rvalue);
411 strv_free(n);
412 return -EINVAL;
413 }
414
415 if (!(nce = new0(ExecCommand, 1)))
416 goto fail;
417
418 nce->argv = n;
419 if (!(nce->path = strdup(n[0])))
420 goto fail;
421
a6a80b4f 422 exec_command_append_list(e, nce);
034c6ed7
LP
423
424 return 0;
425
426fail:
427 for (; k > 0; k--)
428 free(n[k-1]);
429 free(n);
430
431 free(nce);
432
433 return -ENOMEM;
434}
435
436static int config_parse_usec(
437 const char *filename,
438 unsigned line,
439 const char *section,
440 const char *lvalue,
441 const char *rvalue,
442 void *data,
443 void *userdata) {
444
445 usec_t *usec = data;
034c6ed7
LP
446 int r;
447
448 assert(filename);
449 assert(lvalue);
450 assert(rvalue);
451 assert(data);
452
24a6e4a4 453 if ((r = parse_usec(rvalue, usec)) < 0) {
034c6ed7
LP
454 log_error("[%s:%u] Failed to parse time value: %s", filename, line, rvalue);
455 return r;
456 }
457
034c6ed7
LP
458 return 0;
459}
460
0d87eb42
LP
461DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
462DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
034c6ed7 463
47be870b 464static int config_parse_bindtodevice(
acbb0225
LP
465 const char *filename,
466 unsigned line,
467 const char *section,
468 const char *lvalue,
469 const char *rvalue,
470 void *data,
471 void *userdata) {
472
473 Socket *s = data;
474 char *n;
475
476 assert(filename);
477 assert(lvalue);
478 assert(rvalue);
479 assert(data);
480
481 if (rvalue[0] && !streq(rvalue, "*")) {
482 if (!(n = strdup(rvalue)))
483 return -ENOMEM;
484 } else
485 n = NULL;
486
487 free(s->bind_to_device);
488 s->bind_to_device = n;
489
490 return 0;
491}
492
0d87eb42
LP
493DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
494DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
87f0e418 495
47be870b 496static int config_parse_facility(
071830ff
LP
497 const char *filename,
498 unsigned line,
499 const char *section,
500 const char *lvalue,
501 const char *rvalue,
502 void *data,
503 void *userdata) {
504
071830ff 505
94f04347 506 int *o = data, x;
071830ff
LP
507
508 assert(filename);
509 assert(lvalue);
510 assert(rvalue);
511 assert(data);
512
0d87eb42
LP
513 if ((x = log_facility_from_string(rvalue)) < 0) {
514 log_error("[%s:%u] Failed to parse log facility: %s", filename, line, rvalue);
515 return -EBADMSG;
516 }
94f04347
LP
517
518 *o = LOG_MAKEPRI(x, LOG_PRI(*o));
071830ff
LP
519
520 return 0;
521}
522
47be870b 523static int config_parse_level(
071830ff
LP
524 const char *filename,
525 unsigned line,
526 const char *section,
527 const char *lvalue,
528 const char *rvalue,
529 void *data,
530 void *userdata) {
531
071830ff 532
94f04347 533 int *o = data, x;
071830ff
LP
534
535 assert(filename);
536 assert(lvalue);
537 assert(rvalue);
538 assert(data);
539
0d87eb42
LP
540 if ((x = log_level_from_string(rvalue)) < 0) {
541 log_error("[%s:%u] Failed to parse log level: %s", filename, line, rvalue);
542 return -EBADMSG;
543 }
071830ff 544
94f04347
LP
545 *o = LOG_MAKEPRI(LOG_FAC(*o), x);
546 return 0;
547}
548
47be870b 549static int config_parse_io_class(
94f04347
LP
550 const char *filename,
551 unsigned line,
552 const char *section,
553 const char *lvalue,
554 const char *rvalue,
555 void *data,
556 void *userdata) {
557
558 ExecContext *c = data;
559 int x;
560
561 assert(filename);
562 assert(lvalue);
563 assert(rvalue);
564 assert(data);
565
0d87eb42
LP
566 if ((x = ioprio_class_from_string(rvalue)) < 0) {
567 log_error("[%s:%u] Failed to parse IO scheduling class: %s", filename, line, rvalue);
568 return -EBADMSG;
569 }
94f04347
LP
570
571 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
572 c->ioprio_set = true;
573
574 return 0;
575}
576
47be870b 577static int config_parse_io_priority(
94f04347
LP
578 const char *filename,
579 unsigned line,
580 const char *section,
581 const char *lvalue,
582 const char *rvalue,
583 void *data,
584 void *userdata) {
585
586 ExecContext *c = data;
587 int i;
588
589 assert(filename);
590 assert(lvalue);
591 assert(rvalue);
592 assert(data);
593
594 if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) {
595 log_error("[%s:%u] Failed to parse io priority: %s", filename, line, rvalue);
596 return -EBADMSG;
071830ff
LP
597 }
598
94f04347
LP
599 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
600 c->ioprio_set = true;
601
071830ff
LP
602 return 0;
603}
604
47be870b 605static int config_parse_cpu_sched_policy(
9eba9da4
LP
606 const char *filename,
607 unsigned line,
608 const char *section,
609 const char *lvalue,
610 const char *rvalue,
611 void *data,
612 void *userdata) {
613
94f04347
LP
614
615 ExecContext *c = data;
616 int x;
617
618 assert(filename);
619 assert(lvalue);
620 assert(rvalue);
621 assert(data);
622
0d87eb42
LP
623 if ((x = sched_policy_from_string(rvalue)) < 0) {
624 log_error("[%s:%u] Failed to parse CPU scheduling policy: %s", filename, line, rvalue);
625 return -EBADMSG;
626 }
94f04347
LP
627
628 c->cpu_sched_policy = x;
629 c->cpu_sched_set = true;
630
631 return 0;
632}
633
47be870b 634static int config_parse_cpu_sched_prio(
94f04347
LP
635 const char *filename,
636 unsigned line,
637 const char *section,
638 const char *lvalue,
639 const char *rvalue,
640 void *data,
641 void *userdata) {
9eba9da4
LP
642
643 ExecContext *c = data;
644 int i;
645
646 assert(filename);
647 assert(lvalue);
648 assert(rvalue);
649 assert(data);
650
94f04347
LP
651 /* On Linux RR/FIFO have the same range */
652 if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) {
653 log_error("[%s:%u] Failed to parse CPU scheduling priority: %s", filename, line, rvalue);
654 return -EBADMSG;
655 }
9eba9da4 656
94f04347
LP
657 c->cpu_sched_priority = i;
658 c->cpu_sched_set = true;
659
660 return 0;
661}
662
47be870b 663static int config_parse_cpu_affinity(
94f04347
LP
664 const char *filename,
665 unsigned line,
666 const char *section,
667 const char *lvalue,
668 const char *rvalue,
669 void *data,
670 void *userdata) {
671
672 ExecContext *c = data;
673 char *w;
674 size_t l;
675 char *state;
676
677 assert(filename);
678 assert(lvalue);
679 assert(rvalue);
680 assert(data);
681
f62c0e4f 682 FOREACH_WORD(w, l, rvalue, state) {
94f04347
LP
683 char *t;
684 int r;
685 unsigned cpu;
686
687 if (!(t = strndup(w, l)))
688 return -ENOMEM;
689
690 r = safe_atou(t, &cpu);
691 free(t);
692
693 if (r < 0 || cpu >= CPU_SETSIZE) {
694 log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue);
695 return -EBADMSG;
9eba9da4 696 }
94f04347
LP
697
698 CPU_SET(cpu, &c->cpu_affinity);
9eba9da4
LP
699 }
700
94f04347 701 c->cpu_affinity_set = true;
9eba9da4 702
94f04347
LP
703 return 0;
704}
705
47be870b 706static int config_parse_capabilities(
94f04347
LP
707 const char *filename,
708 unsigned line,
709 const char *section,
710 const char *lvalue,
711 const char *rvalue,
712 void *data,
713 void *userdata) {
714
715 ExecContext *c = data;
716 cap_t cap;
717
718 assert(filename);
719 assert(lvalue);
720 assert(rvalue);
721 assert(data);
722
723 if (!(cap = cap_from_text(rvalue))) {
724 if (errno == ENOMEM)
725 return -ENOMEM;
726
727 log_error("[%s:%u] Failed to parse capabilities: %s", filename, line, rvalue);
728 return -EBADMSG;
729 }
730
731 if (c->capabilities)
732 cap_free(c->capabilities);
733 c->capabilities = cap;
734
735 return 0;
736}
737
47be870b 738static int config_parse_secure_bits(
94f04347
LP
739 const char *filename,
740 unsigned line,
741 const char *section,
742 const char *lvalue,
743 const char *rvalue,
744 void *data,
745 void *userdata) {
746
747 ExecContext *c = data;
748 char *w;
749 size_t l;
750 char *state;
751
752 assert(filename);
753 assert(lvalue);
754 assert(rvalue);
755 assert(data);
756
f62c0e4f 757 FOREACH_WORD(w, l, rvalue, state) {
94f04347
LP
758 if (first_word(w, "keep-caps"))
759 c->secure_bits |= SECURE_KEEP_CAPS;
760 else if (first_word(w, "keep-caps-locked"))
761 c->secure_bits |= SECURE_KEEP_CAPS_LOCKED;
762 else if (first_word(w, "no-setuid-fixup"))
763 c->secure_bits |= SECURE_NO_SETUID_FIXUP;
764 else if (first_word(w, "no-setuid-fixup-locked"))
765 c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED;
766 else if (first_word(w, "noroot"))
767 c->secure_bits |= SECURE_NOROOT;
768 else if (first_word(w, "noroot-locked"))
769 c->secure_bits |= SECURE_NOROOT_LOCKED;
9eba9da4 770 else {
94f04347 771 log_error("[%s:%u] Failed to parse secure bits: %s", filename, line, rvalue);
9eba9da4
LP
772 return -EBADMSG;
773 }
774 }
775
94f04347
LP
776 return 0;
777}
778
47be870b 779static int config_parse_bounding_set(
94f04347
LP
780 const char *filename,
781 unsigned line,
782 const char *section,
783 const char *lvalue,
784 const char *rvalue,
785 void *data,
786 void *userdata) {
787
788 ExecContext *c = data;
789 char *w;
790 size_t l;
791 char *state;
792
793 assert(filename);
794 assert(lvalue);
795 assert(rvalue);
796 assert(data);
797
f62c0e4f 798 FOREACH_WORD(w, l, rvalue, state) {
94f04347
LP
799 char *t;
800 int r;
801 cap_value_t cap;
802
803 if (!(t = strndup(w, l)))
804 return -ENOMEM;
805
806 r = cap_from_name(t, &cap);
807 free(t);
808
809 if (r < 0) {
810 log_error("[%s:%u] Failed to parse capability bounding set: %s", filename, line, rvalue);
811 return -EBADMSG;
812 }
813
814 c->capability_bounding_set_drop |= 1 << cap;
815 }
9eba9da4
LP
816
817 return 0;
818}
819
94f04347 820static int config_parse_timer_slack_ns(
9eba9da4
LP
821 const char *filename,
822 unsigned line,
823 const char *section,
824 const char *lvalue,
825 const char *rvalue,
826 void *data,
827 void *userdata) {
828
829 ExecContext *c = data;
94f04347
LP
830 unsigned long u;
831 int r;
9eba9da4
LP
832
833 assert(filename);
834 assert(lvalue);
835 assert(rvalue);
836 assert(data);
837
94f04347
LP
838 if ((r = safe_atolu(rvalue, &u)) < 0) {
839 log_error("[%s:%u] Failed to parse time slack value: %s", filename, line, rvalue);
840 return r;
9eba9da4
LP
841 }
842
94f04347
LP
843 c->timer_slack_ns = u;
844
845 return 0;
846}
847
848static int config_parse_limit(
849 const char *filename,
850 unsigned line,
851 const char *section,
852 const char *lvalue,
853 const char *rvalue,
854 void *data,
855 void *userdata) {
856
857 struct rlimit **rl = data;
858 unsigned long long u;
859 int r;
860
861 assert(filename);
862 assert(lvalue);
863 assert(rvalue);
864 assert(data);
865
866 if ((r = safe_atollu(rvalue, &u)) < 0) {
867 log_error("[%s:%u] Failed to parse resource value: %s", filename, line, rvalue);
868 return r;
869 }
870
871 if (!*rl)
872 if (!(*rl = new(struct rlimit, 1)))
873 return -ENOMEM;
9eba9da4 874
94f04347 875 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
9eba9da4
LP
876 return 0;
877}
878
8e274523
LP
879static int config_parse_cgroup(
880 const char *filename,
881 unsigned line,
882 const char *section,
883 const char *lvalue,
884 const char *rvalue,
885 void *data,
886 void *userdata) {
887
888 Unit *u = userdata;
889 char *w;
890 size_t l;
891 char *state;
892
893 FOREACH_WORD(w, l, rvalue, state) {
894 char *t;
895 int r;
896
897 if (!(t = strndup(w, l)))
898 return -ENOMEM;
899
900 r = unit_add_cgroup_from_text(u, t);
901 free(t);
902
903 if (r < 0)
904 return r;
905 }
906
907 return 0;
908}
909
a9a1e00a
LP
910static int config_parse_sysv_priority(
911 const char *filename,
912 unsigned line,
913 const char *section,
914 const char *lvalue,
915 const char *rvalue,
916 void *data,
917 void *userdata) {
918
919 int *priority = data;
920 int r, i;
921
922 assert(filename);
923 assert(lvalue);
924 assert(rvalue);
925 assert(data);
926
927 if ((r = safe_atoi(rvalue, &i)) < 0 || i < 0) {
928 log_error("[%s:%u] Failed to parse SysV start priority: %s", filename, line, rvalue);
929 return r;
930 }
931
932 *priority = (int) i;
933 return 0;
934}
935
0d87eb42 936DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
50159e6a 937
15ae422b
LP
938static int config_parse_mount_flags(
939 const char *filename,
940 unsigned line,
941 const char *section,
942 const char *lvalue,
943 const char *rvalue,
944 void *data,
945 void *userdata) {
946
947 ExecContext *c = data;
948 char *w;
949 size_t l;
950 char *state;
951 unsigned long flags = 0;
952
953 assert(filename);
954 assert(lvalue);
955 assert(rvalue);
956 assert(data);
957
958 FOREACH_WORD(w, l, rvalue, state) {
959 if (strncmp(w, "shared", l) == 0)
960 flags |= MS_SHARED;
961 else if (strncmp(w, "slave", l) == 0)
962 flags |= MS_SLAVE;
963 else if (strncmp(w, "private", l) == 0)
964 flags |= MS_PRIVATE;
965 else {
966 log_error("[%s:%u] Failed to parse mount flags: %s", filename, line, rvalue);
967 return -EINVAL;
968 }
969 }
970
971 c->mount_flags = flags;
972 return 0;
973}
974
071830ff 975#define FOLLOW_MAX 8
87f0e418 976
9e2f7c11 977static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
0301abf4 978 unsigned c = 0;
87f0e418
LP
979 int fd, r;
980 FILE *f;
0301abf4 981 char *id = NULL;
87f0e418
LP
982
983 assert(filename);
984 assert(*filename);
985 assert(_f);
986 assert(names);
987
0301abf4
LP
988 /* This will update the filename pointer if the loaded file is
989 * reached by a symlink. The old string will be freed. */
87f0e418 990
0301abf4 991 for (;;) {
87f0e418
LP
992 char *target, *k, *name;
993
0301abf4
LP
994 if (c++ >= FOLLOW_MAX)
995 return -ELOOP;
996
b08d03ff
LP
997 path_kill_slashes(*filename);
998
87f0e418
LP
999 /* Add the file name we are currently looking at to
1000 * the names of this unit */
0301abf4
LP
1001 name = file_name_from_path(*filename);
1002 if (!(id = set_get(names, name))) {
87f0e418 1003
0301abf4
LP
1004 if (!(id = strdup(name)))
1005 return -ENOMEM;
87f0e418 1006
0301abf4
LP
1007 if ((r = set_put(names, id)) < 0) {
1008 free(id);
1009 return r;
87f0e418 1010 }
87f0e418
LP
1011 }
1012
0301abf4
LP
1013 /* Try to open the file name, but don't if its a symlink */
1014 if ((fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0)
87f0e418
LP
1015 break;
1016
0301abf4
LP
1017 if (errno != ELOOP)
1018 return -errno;
1019
87f0e418 1020 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
0301abf4
LP
1021 if ((r = readlink_malloc(*filename, &target)) < 0)
1022 return r;
87f0e418 1023
c25fb0ed 1024 k = file_in_same_dir(*filename, target);
87f0e418
LP
1025 free(target);
1026
0301abf4
LP
1027 if (!k)
1028 return -ENOMEM;
87f0e418 1029
0301abf4
LP
1030 free(*filename);
1031 *filename = k;
87f0e418
LP
1032 }
1033
1034 if (!(f = fdopen(fd, "r"))) {
1035 r = -errno;
9e2f7c11 1036 close_nointr_nofail(fd);
0301abf4 1037 return r;
87f0e418
LP
1038 }
1039
1040 *_f = f;
9e2f7c11 1041 *_final = id;
0301abf4 1042 return 0;
87f0e418
LP
1043}
1044
23a177ef
LP
1045static int merge_by_names(Unit **u, Set *names, const char *id) {
1046 char *k;
1047 int r;
1048
1049 assert(u);
1050 assert(*u);
1051 assert(names);
1052
1053 /* Let's try to add in all symlink names we found */
1054 while ((k = set_steal_first(names))) {
1055
1056 /* First try to merge in the other name into our
1057 * unit */
1058 if ((r = unit_merge_by_name(*u, k)) < 0) {
1059 Unit *other;
1060
1061 /* Hmm, we couldn't merge the other unit into
1062 * ours? Then let's try it the other way
1063 * round */
1064
1065 other = manager_get_unit((*u)->meta.manager, k);
1066 free(k);
1067
1068 if (other)
1069 if ((r = unit_merge(other, *u)) >= 0) {
1070 *u = other;
1071 return merge_by_names(u, names, NULL);
1072 }
1073
1074 return r;
1075 }
1076
1077 if (id == k)
1078 unit_choose_id(*u, id);
1079
1080 free(k);
1081 }
1082
1083 return 0;
1084}
1085
e537352b
LP
1086static void dump_items(FILE *f, const ConfigItem *items) {
1087 const ConfigItem *i;
1088 const char *prev_section = NULL;
1089 bool not_first = false;
1090
1091 struct {
1092 ConfigParserCallback callback;
1093 const char *rvalue;
1094 } table[] = {
1095 { config_parse_int, "INTEGER" },
1096 { config_parse_unsigned, "UNSIGNED" },
1097 { config_parse_size, "SIZE" },
1098 { config_parse_bool, "BOOLEAN" },
1099 { config_parse_string, "STRING" },
1100 { config_parse_path, "PATH" },
1101 { config_parse_strv, "STRING [...]" },
1102 { config_parse_nice, "NICE" },
1103 { config_parse_oom_adjust, "OOMADJUST" },
1104 { config_parse_io_class, "IOCLASS" },
1105 { config_parse_io_priority, "IOPRIORITY" },
1106 { config_parse_cpu_sched_policy, "CPUSCHEDPOLICY" },
1107 { config_parse_cpu_sched_prio, "CPUSCHEDPRIO" },
1108 { config_parse_cpu_affinity, "CPUAFFINITY" },
1109 { config_parse_mode, "MODE" },
1110 { config_parse_output, "OUTPUT" },
1111 { config_parse_input, "INPUT" },
1112 { config_parse_facility, "FACILITY" },
1113 { config_parse_level, "LEVEL" },
1114 { config_parse_capabilities, "CAPABILITIES" },
1115 { config_parse_secure_bits, "SECUREBITS" },
1116 { config_parse_bounding_set, "BOUNDINGSET" },
1117 { config_parse_timer_slack_ns, "TIMERSLACK" },
1118 { config_parse_limit, "LIMIT" },
1119 { config_parse_cgroup, "CGROUP [...]" },
1120 { config_parse_deps, "UNIT [...]" },
1121 { config_parse_names, "UNIT [...]" },
1122 { config_parse_exec, "PATH [ARGUMENT [...]]" },
1123 { config_parse_service_type, "SERVICETYPE" },
1124 { config_parse_service_restart, "SERVICERESTART" },
1125 { config_parse_sysv_priority, "SYSVPRIORITY" },
1126 { config_parse_kill_mode, "KILLMODE" },
1127 { config_parse_listen, "SOCKET [...]" },
1128 { config_parse_socket_bind, "SOCKETBIND" },
93ef5e80
LP
1129 { config_parse_bindtodevice, "NETWORKINTERFACE" },
1130 { config_parse_usec, "SECONDS" },
1131 { config_parse_path_strv, "PATH [...]" },
932921b5
LP
1132 { config_parse_mount_flags, "MOUNTFLAG [...]" },
1133 { config_parse_description, "DESCRIPTION" },
e537352b
LP
1134 };
1135
1136 assert(f);
1137 assert(items);
1138
1139 for (i = items; i->lvalue; i++) {
1140 unsigned j;
1141 const char *rvalue = "OTHER";
1142
1143 if (!streq_ptr(i->section, prev_section)) {
1144 if (!not_first)
1145 not_first = true;
1146 else
1147 fputc('\n', f);
1148
1149 fprintf(f, "[%s]\n", i->section);
1150 prev_section = i->section;
1151 }
1152
1153 for (j = 0; j < ELEMENTSOF(table); j++)
1154 if (i->parse == table[j].callback) {
1155 rvalue = table[j].rvalue;
1156 break;
1157 }
1158
1159 fprintf(f, "%s=%s\n", i->lvalue, rvalue);
1160 }
1161}
1162
1163static int load_from_path(Unit *u, const char *path) {
87f0e418
LP
1164
1165 static const char* const section_table[_UNIT_TYPE_MAX] = {
1166 [UNIT_SERVICE] = "Service",
1167 [UNIT_TIMER] = "Timer",
1168 [UNIT_SOCKET] = "Socket",
1169 [UNIT_TARGET] = "Target",
1170 [UNIT_DEVICE] = "Device",
1171 [UNIT_MOUNT] = "Mount",
1172 [UNIT_AUTOMOUNT] = "Automount",
1173 [UNIT_SNAPSHOT] = "Snapshot"
42f4e3c4
LP
1174 };
1175
034c6ed7 1176#define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \
9eba9da4
LP
1177 { "WorkingDirectory", config_parse_path, &(context).working_directory, section }, \
1178 { "RootDirectory", config_parse_path, &(context).root_directory, section }, \
1c01f82b
LP
1179 { "User", config_parse_string, &(context).user, section }, \
1180 { "Group", config_parse_string, &(context).group, section }, \
1181 { "SupplementaryGroups", config_parse_strv, &(context).supplementary_groups, section }, \
fb33a393
LP
1182 { "Nice", config_parse_nice, &(context), section }, \
1183 { "OOMAdjust", config_parse_oom_adjust, &(context), section }, \
9eba9da4 1184 { "IOSchedulingClass", config_parse_io_class, &(context), section }, \
94f04347
LP
1185 { "IOSchedulingPriority", config_parse_io_priority, &(context), section }, \
1186 { "CPUSchedulingPolicy", config_parse_cpu_sched_policy,&(context), section }, \
1187 { "CPUSchedulingPriority", config_parse_cpu_sched_prio, &(context), section }, \
38b48754 1188 { "CPUSchedulingResetOnFork", config_parse_bool, &(context).cpu_sched_reset_on_fork, section }, \
94f04347 1189 { "CPUAffinity", config_parse_cpu_affinity, &(context), section }, \
b5a0699f 1190 { "UMask", config_parse_mode, &(context).umask, section }, \
071830ff 1191 { "Environment", config_parse_strv, &(context).environment, section }, \
80876c20
LP
1192 { "StandardInput", config_parse_input, &(context).std_input, section }, \
1193 { "StandardOutput", config_parse_output, &(context).std_output, section }, \
1194 { "StandardError", config_parse_output, &(context).std_output, section }, \
1195 { "TTYPath", config_parse_path, &(context).tty_path, section }, \
071830ff
LP
1196 { "SyslogIdentifier", config_parse_string, &(context).syslog_identifier, section }, \
1197 { "SyslogFacility", config_parse_facility, &(context).syslog_priority, section }, \
94f04347
LP
1198 { "SyslogLevel", config_parse_level, &(context).syslog_priority, section }, \
1199 { "Capabilities", config_parse_capabilities, &(context), section }, \
1200 { "SecureBits", config_parse_secure_bits, &(context), section }, \
1201 { "CapabilityBoundingSetDrop", config_parse_bounding_set, &(context), section }, \
1202 { "TimerSlackNS", config_parse_timer_slack_ns, &(context), section }, \
1203 { "LimitCPU", config_parse_limit, &(context).rlimit[RLIMIT_CPU], section }, \
1204 { "LimitFSIZE", config_parse_limit, &(context).rlimit[RLIMIT_FSIZE], section }, \
1205 { "LimitDATA", config_parse_limit, &(context).rlimit[RLIMIT_DATA], section }, \
1206 { "LimitSTACK", config_parse_limit, &(context).rlimit[RLIMIT_STACK], section }, \
1207 { "LimitCORE", config_parse_limit, &(context).rlimit[RLIMIT_CORE], section }, \
1208 { "LimitRSS", config_parse_limit, &(context).rlimit[RLIMIT_RSS], section }, \
1209 { "LimitNOFILE", config_parse_limit, &(context).rlimit[RLIMIT_NOFILE], section }, \
1210 { "LimitAS", config_parse_limit, &(context).rlimit[RLIMIT_AS], section }, \
1211 { "LimitNPROC", config_parse_limit, &(context).rlimit[RLIMIT_NPROC], section }, \
1212 { "LimitMEMLOCK", config_parse_limit, &(context).rlimit[RLIMIT_MEMLOCK], section }, \
1213 { "LimitLOCKS", config_parse_limit, &(context).rlimit[RLIMIT_LOCKS], section }, \
1214 { "LimitSIGPENDING", config_parse_limit, &(context).rlimit[RLIMIT_SIGPENDING], section }, \
1215 { "LimitMSGQUEUE", config_parse_limit, &(context).rlimit[RLIMIT_MSGQUEUE], section }, \
1216 { "LimitNICE", config_parse_limit, &(context).rlimit[RLIMIT_NICE], section }, \
1217 { "LimitRTPRIO", config_parse_limit, &(context).rlimit[RLIMIT_RTPRIO], section }, \
451a074f 1218 { "LimitRTTIME", config_parse_limit, &(context).rlimit[RLIMIT_RTTIME], section }, \
15ae422b
LP
1219 { "ControlGroup", config_parse_cgroup, u, section }, \
1220 { "ReadWriteDirectories", config_parse_path_strv, &(context).read_write_dirs, section }, \
1221 { "ReadOnlyDirectories", config_parse_path_strv, &(context).read_only_dirs, section }, \
1222 { "InaccessibleDirectories",config_parse_path_strv, &(context).inaccessible_dirs, section }, \
1223 { "PrivateTmp", config_parse_bool, &(context).private_tmp, section }, \
1224 { "MountFlags", config_parse_mount_flags, &(context), section }
034c6ed7 1225
3efd4195 1226 const ConfigItem items[] = {
09477267 1227 { "Names", config_parse_names, u, "Unit" },
932921b5 1228 { "Description", config_parse_description, u, "Unit" },
09477267
LP
1229 { "Requires", config_parse_deps, UINT_TO_PTR(UNIT_REQUIRES), "Unit" },
1230 { "RequiresOverridable", config_parse_deps, UINT_TO_PTR(UNIT_REQUIRES_OVERRIDABLE), "Unit" },
1231 { "Requisite", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE), "Unit" },
1232 { "RequisiteOverridable", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE_OVERRIDABLE), "Unit" },
1233 { "Wants", config_parse_deps, UINT_TO_PTR(UNIT_WANTS), "Unit" },
1234 { "Conflicts", config_parse_deps, UINT_TO_PTR(UNIT_CONFLICTS), "Unit" },
1235 { "Before", config_parse_deps, UINT_TO_PTR(UNIT_BEFORE), "Unit" },
1236 { "After", config_parse_deps, UINT_TO_PTR(UNIT_AFTER), "Unit" },
1237 { "RecursiveStop", config_parse_bool, &u->meta.recursive_stop, "Unit" },
1238 { "StopWhenUnneeded", config_parse_bool, &u->meta.stop_when_unneeded, "Unit" },
1c01f82b
LP
1239
1240 { "PIDFile", config_parse_path, &u->service.pid_file, "Service" },
1241 { "ExecStartPre", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START_PRE, "Service" },
1242 { "ExecStart", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START, "Service" },
1243 { "ExecStartPost", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START_POST, "Service" },
1244 { "ExecReload", config_parse_exec, u->service.exec_command+SERVICE_EXEC_RELOAD, "Service" },
1245 { "ExecStop", config_parse_exec, u->service.exec_command+SERVICE_EXEC_STOP, "Service" },
1246 { "ExecStopPost", config_parse_exec, u->service.exec_command+SERVICE_EXEC_STOP_POST, "Service" },
1247 { "RestartSec", config_parse_usec, &u->service.restart_usec, "Service" },
1248 { "TimeoutSec", config_parse_usec, &u->service.timeout_usec, "Service" },
0d87eb42
LP
1249 { "Type", config_parse_service_type, &u->service.type, "Service" },
1250 { "Restart", config_parse_service_restart, &u->service.restart, "Service" },
81a2b7ce
LP
1251 { "PermissionsStartOnly", config_parse_bool, &u->service.permissions_start_only, "Service" },
1252 { "RootDirectoryStartOnly", config_parse_bool, &u->service.root_directory_start_only, "Service" },
8e274523 1253 { "ValidNoProcess", config_parse_bool, &u->service.valid_no_process, "Service" },
a9a1e00a 1254 { "SysVStartPriority", config_parse_sysv_priority, &u->service.sysv_start_priority, "Service" },
50159e6a 1255 { "KillMode", config_parse_kill_mode, &u->service.kill_mode, "Service" },
b778d555 1256 { "NonBlocking", config_parse_bool, &u->service.exec_context.non_blocking, "Service" },
05e343b7 1257 { "BusName", config_parse_string, &u->service.bus_name, "Service" },
87f0e418
LP
1258 EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
1259
1c01f82b
LP
1260 { "ListenStream", config_parse_listen, &u->socket, "Socket" },
1261 { "ListenDatagram", config_parse_listen, &u->socket, "Socket" },
1262 { "ListenSequentialPacket", config_parse_listen, &u->socket, "Socket" },
1263 { "ListenFIFO", config_parse_listen, &u->socket, "Socket" },
1264 { "BindIPv6Only", config_parse_socket_bind, &u->socket, "Socket" },
1265 { "Backlog", config_parse_unsigned, &u->socket.backlog, "Socket" },
acbb0225 1266 { "BindToDevice", config_parse_bindtodevice, &u->socket, "Socket" },
1c01f82b
LP
1267 { "ExecStartPre", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_START_PRE, "Socket" },
1268 { "ExecStartPost", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_START_POST, "Socket" },
1269 { "ExecStopPre", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_STOP_PRE, "Socket" },
1270 { "ExecStopPost", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_STOP_POST, "Socket" },
108736d0 1271 { "TimeoutSec", config_parse_usec, &u->socket.timeout_usec, "Socket" },
b5a0699f
LP
1272 { "DirectoryMode", config_parse_mode, &u->socket.directory_mode, "Socket" },
1273 { "SocketMode", config_parse_mode, &u->socket.socket_mode, "Socket" },
50159e6a 1274 { "KillMode", config_parse_kill_mode, &u->socket.kill_mode, "Socket" },
4f2d528d 1275 { "Accept", config_parse_bool, &u->socket.accept, "Socket" },
87f0e418
LP
1276 EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
1277
e537352b
LP
1278 { "What", config_parse_string, &u->mount.parameters_fragment.what, "Mount" },
1279 { "Where", config_parse_path, &u->mount.where, "Mount" },
1280 { "Options", config_parse_string, &u->mount.parameters_fragment.options, "Mount" },
1281 { "Type", config_parse_string, &u->mount.parameters_fragment.fstype, "Mount" },
1282 { "TimeoutSec", config_parse_usec, &u->mount.timeout_usec, "Mount" },
1283 { "KillMode", config_parse_kill_mode, &u->mount.kill_mode, "Mount" },
1284 EXEC_CONTEXT_CONFIG_ITEMS(u->mount.exec_context, "Mount"),
034c6ed7 1285
8d567588
LP
1286 { "Where", config_parse_path, &u->automount.where, "Automount" },
1287
3efd4195
LP
1288 { NULL, NULL, NULL, NULL }
1289 };
1290
034c6ed7 1291#undef EXEC_CONTEXT_CONFIG_ITEMS
42f4e3c4 1292
42f4e3c4 1293 const char *sections[3];
0301abf4
LP
1294 char *k;
1295 int r;
87f0e418 1296 Set *symlink_names;
23a177ef
LP
1297 FILE *f = NULL;
1298 char *filename = NULL, *id = NULL;
1299 Unit *merged;
1300
e537352b
LP
1301 if (!u) {
1302 /* Dirty dirty hack. */
1303 dump_items((FILE*) path, items);
1304 return 0;
1305 }
1306
23a177ef 1307 assert(u);
e537352b 1308 assert(path);
3efd4195 1309
09477267 1310 sections[0] = "Unit";
87f0e418 1311 sections[1] = section_table[u->meta.type];
42f4e3c4
LP
1312 sections[2] = NULL;
1313
87f0e418
LP
1314 if (!(symlink_names = set_new(string_hash_func, string_compare_func)))
1315 return -ENOMEM;
3efd4195 1316
036643a2
LP
1317 if (path_is_absolute(path)) {
1318
1319 if (!(filename = strdup(path))) {
1320 r = -ENOMEM;
1321 goto finish;
1322 }
1323
1324 if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) {
1325 free(filename);
1326 filename = NULL;
1327
1328 if (r != -ENOENT)
1329 goto finish;
1330 }
1331
1332 } else {
1333 char **p;
1334
1335 STRV_FOREACH(p, u->meta.manager->unit_path) {
1336
1337 /* Instead of opening the path right away, we manually
1338 * follow all symlinks and add their name to our unit
1339 * name set while doing so */
1340 if (!(filename = path_make_absolute(path, *p))) {
1341 r = -ENOMEM;
1342 goto finish;
1343 }
1344
1345 if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) {
1346 char *sn;
1347
1348 free(filename);
1349 filename = NULL;
1350
1351 if (r != -ENOENT)
1352 goto finish;
1353
1354 /* Empty the symlink names for the next run */
1355 while ((sn = set_steal_first(symlink_names)))
1356 free(sn);
3efd4195 1357
036643a2
LP
1358 continue;
1359 }
1360
1361 break;
1362 }
1363 }
034c6ed7 1364
036643a2 1365 if (!filename) {
23a177ef 1366 r = 0;
0301abf4
LP
1367 goto finish;
1368 }
87f0e418 1369
23a177ef
LP
1370 merged = u;
1371 if ((r = merge_by_names(&merged, symlink_names, id)) < 0)
0301abf4 1372 goto finish;
87f0e418 1373
23a177ef 1374 if (merged != u) {
e537352b 1375 u->meta.load_state = UNIT_MERGED;
23a177ef
LP
1376 r = 0;
1377 goto finish;
034c6ed7
LP
1378 }
1379
23a177ef
LP
1380 /* Now, parse the file contents */
1381 if ((r = config_parse(filename, f, sections, items, u)) < 0)
1382 goto finish;
b08d03ff 1383
6be1e7d5
LP
1384 free(u->meta.fragment_path);
1385 u->meta.fragment_path = filename;
0301abf4 1386 filename = NULL;
87f0e418 1387
e537352b 1388 u->meta.load_state = UNIT_LOADED;
23a177ef 1389 r = 0;
87f0e418
LP
1390
1391finish:
1392 while ((k = set_steal_first(symlink_names)))
1393 free(k);
23a177ef 1394
87f0e418 1395 set_free(symlink_names);
0301abf4
LP
1396 free(filename);
1397
23a177ef
LP
1398 if (f)
1399 fclose(f);
1400
0301abf4
LP
1401 return r;
1402}
1403
e537352b 1404int unit_load_fragment(Unit *u) {
23a177ef 1405 int r;
0301abf4
LP
1406
1407 assert(u);
23a177ef
LP
1408
1409 if (u->meta.fragment_path) {
1410
e537352b 1411 if ((r = load_from_path(u, u->meta.fragment_path)) < 0)
23a177ef 1412 return r;
0301abf4 1413
23a177ef 1414 } else {
0301abf4 1415 Iterator i;
890f434c 1416 const char *t;
0301abf4 1417
890f434c 1418 /* Try to find the unit under its id */
9e2f7c11
LP
1419 if ((r = load_from_path(u, u->meta.id)) < 0)
1420 return r;
890f434c
LP
1421
1422 /* Try to find an alias we can load this with */
e537352b 1423 if (u->meta.load_state == UNIT_STUB)
23a177ef 1424 SET_FOREACH(t, u->meta.names, i) {
87f0e418 1425
9e2f7c11 1426 if (t == u->meta.id)
23a177ef 1427 continue;
071830ff 1428
e537352b 1429 if ((r = load_from_path(u, t)) < 0)
23a177ef 1430 return r;
890f434c 1431
e537352b 1432 if (u->meta.load_state != UNIT_STUB)
23a177ef
LP
1433 break;
1434 }
9e2f7c11
LP
1435
1436 /* Now, follow the same logic, but look for a template */
1437 if (u->meta.load_state == UNIT_STUB && u->meta.instance) {
1438 char *k;
1439
1440 if (!(k = unit_name_template(u->meta.id)))
1441 return -ENOMEM;
1442
1443 r = load_from_path(u, k);
1444 free(k);
1445
1446 if (r < 0)
1447 return r;
1448
1449 if (u->meta.load_state == UNIT_STUB)
1450 SET_FOREACH(t, u->meta.names, i) {
1451
1452 if (t == u->meta.id)
1453 continue;
1454
1455 if (!(k = unit_name_template(t)))
1456 return -ENOMEM;
1457
1458 r = load_from_path(u, k);
1459 free(k);
1460
1461 if (r < 0)
1462 return r;
1463
1464 if (u->meta.load_state != UNIT_STUB)
1465 break;
1466 }
1467 }
071830ff
LP
1468 }
1469
23a177ef 1470 return 0;
3efd4195 1471}
e537352b
LP
1472
1473void unit_dump_config_items(FILE *f) {
1474 /* OK, this wins a prize for extreme ugliness. */
1475
1476 load_from_path(NULL, (const void*) f);
1477}