]> git.ipfire.org Git - people/ms/systemd.git/blame - load-fragment.c
greatly extend what we enforce as process properties
[people/ms/systemd.git] / load-fragment.c
CommitLineData
3efd4195
LP
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
2
87f0e418 3#include <linux/oom.h>
3efd4195
LP
4#include <assert.h>
5#include <errno.h>
6#include <string.h>
87f0e418
LP
7#include <unistd.h>
8#include <fcntl.h>
94f04347
LP
9#include <sched.h>
10#include <sys/prctl.h>
3efd4195 11
87f0e418 12#include "unit.h"
3efd4195
LP
13#include "strv.h"
14#include "conf-parser.h"
15#include "load-fragment.h"
16354eff 16#include "log.h"
9eba9da4 17#include "ioprio.h"
94f04347
LP
18#include "securebits.h"
19#include "missing.h"
3efd4195 20
42f4e3c4 21static int config_parse_deps(
3efd4195
LP
22 const char *filename,
23 unsigned line,
24 const char *section,
25 const char *lvalue,
26 const char *rvalue,
27 void *data,
28 void *userdata) {
29
87f0e418
LP
30 UnitDependency d = PTR_TO_UINT(data);
31 Unit *u = userdata;
3efd4195
LP
32 char *w;
33 size_t l;
34 char *state;
35
36 assert(filename);
37 assert(lvalue);
38 assert(rvalue);
3efd4195
LP
39
40 FOREACH_WORD(w, &l, rvalue, state) {
41 char *t;
42 int r;
3efd4195
LP
43
44 if (!(t = strndup(w, l)))
45 return -ENOMEM;
46
b19e7dc0 47 r = unit_add_dependency_by_name(u, d, t);
3efd4195
LP
48 free(t);
49
50 if (r < 0)
51 return r;
3efd4195
LP
52 }
53
54 return 0;
55}
56
42f4e3c4 57static int config_parse_names(
87d1515d
LP
58 const char *filename,
59 unsigned line,
60 const char *section,
61 const char *lvalue,
62 const char *rvalue,
63 void *data,
64 void *userdata) {
65
87f0e418 66 Unit *u = userdata;
87d1515d
LP
67 char *w;
68 size_t l;
69 char *state;
70
71 assert(filename);
72 assert(lvalue);
73 assert(rvalue);
74 assert(data);
75
76 FOREACH_WORD(w, &l, rvalue, state) {
77 char *t;
78 int r;
87f0e418 79 Unit *other;
87d1515d
LP
80
81 if (!(t = strndup(w, l)))
82 return -ENOMEM;
83
87f0e418 84 other = manager_get_unit(u->meta.manager, t);
87d1515d
LP
85
86 if (other) {
87
87f0e418 88 if (other != u) {
87d1515d 89
87f0e418 90 if (other->meta.load_state != UNIT_STUB) {
87d1515d
LP
91 free(t);
92 return -EEXIST;
93 }
94
87f0e418 95 if ((r = unit_merge(u, other)) < 0) {
87d1515d
LP
96 free(t);
97 return r;
98 }
99 }
100
101 } else {
87f0e418 102 if ((r = unit_add_name(u, t)) < 0) {
87d1515d
LP
103 free(t);
104 return r;
105 }
106 }
107
108 free(t);
109 }
110
111 return 0;
112}
113
42f4e3c4
LP
114static int config_parse_listen(
115 const char *filename,
116 unsigned line,
117 const char *section,
118 const char *lvalue,
119 const char *rvalue,
120 void *data,
121 void *userdata) {
122
16354eff 123 int r;
542563ba
LP
124 SocketPort *p;
125 Socket *s;
16354eff 126
42f4e3c4
LP
127 assert(filename);
128 assert(lvalue);
129 assert(rvalue);
130 assert(data);
131
542563ba
LP
132 s = (Socket*) data;
133
134 if (!(p = new0(SocketPort, 1)))
135 return -ENOMEM;
136
137 if (streq(lvalue, "ListenFIFO")) {
138 p->type = SOCKET_FIFO;
139
140 if (!(p->path = strdup(rvalue))) {
141 free(p);
142 return -ENOMEM;
143 }
144 } else {
145 p->type = SOCKET_SOCKET;
146
147 if ((r = socket_address_parse(&p->address, rvalue)) < 0) {
148 log_error("[%s:%u] Failed to parse address value: %s", filename, line, rvalue);
149 free(p);
150 return r;
151 }
152
153 if (streq(lvalue, "ListenStream"))
154 p->address.type = SOCK_STREAM;
155 else if (streq(lvalue, "ListenDatagram"))
156 p->address.type = SOCK_DGRAM;
157 else {
158 assert(streq(lvalue, "ListenSequentialPacket"));
159 p->address.type = SOCK_SEQPACKET;
160 }
161
162 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
163 free(p);
164 return -EPROTONOSUPPORT;
165 }
16354eff
LP
166 }
167
542563ba 168 p->fd = -1;
034c6ed7 169 LIST_PREPEND(SocketPort, port, s->ports, p);
542563ba 170
16354eff 171 return 0;
42f4e3c4
LP
172}
173
034c6ed7 174static int config_parse_socket_bind(
42f4e3c4
LP
175 const char *filename,
176 unsigned line,
177 const char *section,
178 const char *lvalue,
179 const char *rvalue,
180 void *data,
181 void *userdata) {
182
542563ba
LP
183 int r;
184 Socket *s;
42f4e3c4
LP
185
186 assert(filename);
187 assert(lvalue);
188 assert(rvalue);
189 assert(data);
190
542563ba
LP
191 s = (Socket*) data;
192
193 if ((r = parse_boolean(rvalue)) < 0) {
194 log_error("[%s:%u] Failed to parse bind IPv6 only value: %s", filename, line, rvalue);
195 return r;
16354eff 196 }
42f4e3c4 197
542563ba
LP
198 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
199
42f4e3c4
LP
200 return 0;
201}
202
034c6ed7
LP
203static int config_parse_nice(
204 const char *filename,
205 unsigned line,
206 const char *section,
207 const char *lvalue,
208 const char *rvalue,
209 void *data,
210 void *userdata) {
211
fb33a393
LP
212 ExecContext *c = data;
213 int priority, r;
034c6ed7
LP
214
215 assert(filename);
216 assert(lvalue);
217 assert(rvalue);
218 assert(data);
219
220 if ((r = safe_atoi(rvalue, &priority)) < 0) {
221 log_error("[%s:%u] Failed to parse nice priority: %s", filename, line, rvalue);
222 return r;
223 }
224
225 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
226 log_error("[%s:%u] Nice priority out of range: %s", filename, line, rvalue);
227 return -ERANGE;
228 }
229
fb33a393
LP
230 c->nice = priority;
231 c->nice_set = false;
232
034c6ed7
LP
233 return 0;
234}
235
236static int config_parse_oom_adjust(
237 const char *filename,
238 unsigned line,
239 const char *section,
240 const char *lvalue,
241 const char *rvalue,
242 void *data,
243 void *userdata) {
244
fb33a393
LP
245 ExecContext *c = data;
246 int oa, r;
034c6ed7
LP
247
248 assert(filename);
249 assert(lvalue);
250 assert(rvalue);
251 assert(data);
252
253 if ((r = safe_atoi(rvalue, &oa)) < 0) {
254 log_error("[%s:%u] Failed to parse OOM adjust value: %s", filename, line, rvalue);
255 return r;
256 }
257
258 if (oa < OOM_DISABLE || oa > OOM_ADJUST_MAX) {
259 log_error("[%s:%u] OOM adjust value out of range: %s", filename, line, rvalue);
260 return -ERANGE;
261 }
262
fb33a393
LP
263 c->oom_adjust = oa;
264 c->oom_adjust_set = true;
265
034c6ed7
LP
266 return 0;
267}
268
269static int config_parse_umask(
270 const char *filename,
271 unsigned line,
272 const char *section,
273 const char *lvalue,
274 const char *rvalue,
275 void *data,
276 void *userdata) {
277
278 mode_t *m = data;
279 long l;
280 char *x = NULL;
281
282 assert(filename);
283 assert(lvalue);
284 assert(rvalue);
285 assert(data);
286
287 errno = 0;
288 l = strtol(rvalue, &x, 8);
289 if (!x || *x || errno) {
290 log_error("[%s:%u] Failed to parse umask value: %s", filename, line, rvalue);
291 return errno ? -errno : -EINVAL;
292 }
293
294 if (l < 0000 || l > 0777) {
295 log_error("[%s:%u] umask value out of range: %s", filename, line, rvalue);
296 return -ERANGE;
297 }
298
299 *m = (mode_t) l;
300 return 0;
301}
302
303static int config_parse_exec(
304 const char *filename,
305 unsigned line,
306 const char *section,
307 const char *lvalue,
308 const char *rvalue,
309 void *data,
310 void *userdata) {
311
312 ExecCommand **e = data, *ee, *nce = NULL;
313 char **n;
314 char *w;
315 unsigned k;
316 size_t l;
317 char *state;
318
319 assert(filename);
320 assert(lvalue);
321 assert(rvalue);
322 assert(data);
323
324 k = 0;
325 FOREACH_WORD_QUOTED(w, l, rvalue, state)
326 k++;
327
328 if (!(n = new(char*, k+1)))
329 return -ENOMEM;
330
44d8db9e 331 k = 0;
034c6ed7
LP
332 FOREACH_WORD_QUOTED(w, l, rvalue, state)
333 if (!(n[k++] = strndup(w, l)))
334 goto fail;
335
336 n[k] = NULL;
337
0301abf4 338 if (!n[0] || !path_is_absolute(n[0])) {
034c6ed7
LP
339 log_error("[%s:%u] Invalid executable path in command line: %s", filename, line, rvalue);
340 strv_free(n);
341 return -EINVAL;
342 }
343
344 if (!(nce = new0(ExecCommand, 1)))
345 goto fail;
346
347 nce->argv = n;
348 if (!(nce->path = strdup(n[0])))
349 goto fail;
350
351 if (*e) {
352 /* It's kinda important that we keep the order here */
353 LIST_FIND_TAIL(ExecCommand, command, *e, ee);
354 LIST_INSERT_AFTER(ExecCommand, command, *e, ee, nce);
355 } else
356 *e = nce;
357
358 return 0;
359
360fail:
361 for (; k > 0; k--)
362 free(n[k-1]);
363 free(n);
364
365 free(nce);
366
367 return -ENOMEM;
368}
369
370static int config_parse_usec(
371 const char *filename,
372 unsigned line,
373 const char *section,
374 const char *lvalue,
375 const char *rvalue,
376 void *data,
377 void *userdata) {
378
379 usec_t *usec = data;
380 unsigned long long u;
381 int r;
382
383 assert(filename);
384 assert(lvalue);
385 assert(rvalue);
386 assert(data);
387
388 if ((r = safe_atollu(rvalue, &u)) < 0) {
389 log_error("[%s:%u] Failed to parse time value: %s", filename, line, rvalue);
390 return r;
391 }
392
393 /* We actually assume the user configures seconds. Later on we
394 * might choose to support suffixes for time values, to
395 * configure bigger or smaller units */
396
397 *usec = u * USEC_PER_SEC;
398
399 return 0;
400}
401
402static int config_parse_service_type(
403 const char *filename,
404 unsigned line,
405 const char *section,
406 const char *lvalue,
407 const char *rvalue,
408 void *data,
409 void *userdata) {
410
411 Service *s = data;
94f04347 412 ServiceType x;
034c6ed7
LP
413
414 assert(filename);
415 assert(lvalue);
416 assert(rvalue);
417 assert(data);
418
94f04347 419 if ((x = service_type_from_string(rvalue)) < 0) {
034c6ed7
LP
420 log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue);
421 return -EBADMSG;
422 }
423
94f04347
LP
424 s->type = x;
425
034c6ed7
LP
426 return 0;
427}
428
429static int config_parse_service_restart(
430 const char *filename,
431 unsigned line,
432 const char *section,
433 const char *lvalue,
434 const char *rvalue,
435 void *data,
436 void *userdata) {
437
438 Service *s = data;
94f04347 439 ServiceRestart x;
034c6ed7
LP
440
441 assert(filename);
442 assert(lvalue);
443 assert(rvalue);
444 assert(data);
445
94f04347
LP
446 if ((x = service_restart_from_string(rvalue)) < 0) {
447 log_error("[%s:%u] Failed to parse service restart specifier: %s", filename, line, rvalue);
034c6ed7
LP
448 return -EBADMSG;
449 }
450
94f04347
LP
451 s->restart = x;
452
034c6ed7
LP
453 return 0;
454}
455
acbb0225
LP
456int config_parse_bindtodevice(
457 const char *filename,
458 unsigned line,
459 const char *section,
460 const char *lvalue,
461 const char *rvalue,
462 void *data,
463 void *userdata) {
464
465 Socket *s = data;
466 char *n;
467
468 assert(filename);
469 assert(lvalue);
470 assert(rvalue);
471 assert(data);
472
473 if (rvalue[0] && !streq(rvalue, "*")) {
474 if (!(n = strdup(rvalue)))
475 return -ENOMEM;
476 } else
477 n = NULL;
478
479 free(s->bind_to_device);
480 s->bind_to_device = n;
481
482 return 0;
483}
484
071830ff
LP
485int config_parse_output(
486 const char *filename,
487 unsigned line,
488 const char *section,
489 const char *lvalue,
490 const char *rvalue,
491 void *data,
492 void *userdata) {
493
94f04347 494 ExecOutput *o = data, x;
071830ff
LP
495
496 assert(filename);
497 assert(lvalue);
498 assert(rvalue);
499 assert(data);
500
94f04347
LP
501 if ((x = exec_output_from_string(rvalue)) < 0) {
502 log_error("[%s:%u] Failed to parse output specifier: %s", filename, line, rvalue);
503 return -EBADMSG;
504 }
505
506 *o = x;
507
508 return 0;
509}
510
511int config_parse_input(
512 const char *filename,
513 unsigned line,
514 const char *section,
515 const char *lvalue,
516 const char *rvalue,
517 void *data,
518 void *userdata) {
519
520 ExecInput *i = data, x;
521
522 assert(filename);
523 assert(lvalue);
524 assert(rvalue);
525 assert(data);
526
527 if ((x = exec_input_from_string(rvalue)) < 0) {
528 log_error("[%s:%u] Failed to parse input specifier: %s", filename, line, rvalue);
071830ff
LP
529 return -EBADMSG;
530 }
531
94f04347
LP
532 *i = x;
533
071830ff
LP
534 return 0;
535}
87f0e418 536
071830ff
LP
537int config_parse_facility(
538 const char *filename,
539 unsigned line,
540 const char *section,
541 const char *lvalue,
542 const char *rvalue,
543 void *data,
544 void *userdata) {
545
071830ff 546
94f04347 547 int *o = data, x;
071830ff
LP
548
549 assert(filename);
550 assert(lvalue);
551 assert(rvalue);
552 assert(data);
553
94f04347 554 if ((x = log_facility_from_string(rvalue)) < 0)
071830ff
LP
555
556 /* Second try, let's see if this is a number. */
94f04347
LP
557 if (safe_atoi(rvalue, &x) < 0 || !log_facility_to_string(x)) {
558 log_error("[%s:%u] Failed to parse log facility: %s", filename, line, rvalue);
071830ff
LP
559 return -EBADMSG;
560 }
94f04347
LP
561
562 *o = LOG_MAKEPRI(x, LOG_PRI(*o));
071830ff
LP
563
564 return 0;
565}
566
567int config_parse_level(
568 const char *filename,
569 unsigned line,
570 const char *section,
571 const char *lvalue,
572 const char *rvalue,
573 void *data,
574 void *userdata) {
575
071830ff 576
94f04347 577 int *o = data, x;
071830ff
LP
578
579 assert(filename);
580 assert(lvalue);
581 assert(rvalue);
582 assert(data);
583
94f04347
LP
584 if ((x = log_level_from_string(rvalue)) < 0)
585
586 /* Second try, let's see if this is a number. */
587 if (safe_atoi(rvalue, &x) < 0 || !log_level_to_string(x)) {
588 log_error("[%s:%u] Failed to parse log level: %s", filename, line, rvalue);
589 return -EBADMSG;
071830ff
LP
590 }
591
94f04347
LP
592 *o = LOG_MAKEPRI(LOG_FAC(*o), x);
593 return 0;
594}
595
596int config_parse_io_class(
597 const char *filename,
598 unsigned line,
599 const char *section,
600 const char *lvalue,
601 const char *rvalue,
602 void *data,
603 void *userdata) {
604
605 ExecContext *c = data;
606 int x;
607
608 assert(filename);
609 assert(lvalue);
610 assert(rvalue);
611 assert(data);
612
613 if ((x = ioprio_class_from_string(rvalue)) < 0)
071830ff
LP
614
615 /* Second try, let's see if this is a number. */
94f04347
LP
616 if (safe_atoi(rvalue, &x) < 0 || !ioprio_class_to_string(x)) {
617 log_error("[%s:%u] Failed to parse IO scheduling class: %s", filename, line, rvalue);
071830ff
LP
618 return -EBADMSG;
619 }
94f04347
LP
620
621 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
622 c->ioprio_set = true;
623
624 return 0;
625}
626
627int config_parse_io_priority(
628 const char *filename,
629 unsigned line,
630 const char *section,
631 const char *lvalue,
632 const char *rvalue,
633 void *data,
634 void *userdata) {
635
636 ExecContext *c = data;
637 int i;
638
639 assert(filename);
640 assert(lvalue);
641 assert(rvalue);
642 assert(data);
643
644 if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) {
645 log_error("[%s:%u] Failed to parse io priority: %s", filename, line, rvalue);
646 return -EBADMSG;
071830ff
LP
647 }
648
94f04347
LP
649 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
650 c->ioprio_set = true;
651
071830ff
LP
652 return 0;
653}
654
94f04347 655int config_parse_cpu_sched_policy(
9eba9da4
LP
656 const char *filename,
657 unsigned line,
658 const char *section,
659 const char *lvalue,
660 const char *rvalue,
661 void *data,
662 void *userdata) {
663
94f04347
LP
664
665 ExecContext *c = data;
666 int x;
667
668 assert(filename);
669 assert(lvalue);
670 assert(rvalue);
671 assert(data);
672
673 if ((x = sched_policy_from_string(rvalue)) < 0)
674
675 /* Second try, let's see if this is a number. */
676 if (safe_atoi(rvalue, &x) < 0 || !sched_policy_to_string(x)) {
677 log_error("[%s:%u] Failed to parse CPU scheduling policy: %s", filename, line, rvalue);
678 return -EBADMSG;
679 }
680
681 c->cpu_sched_policy = x;
682 c->cpu_sched_set = true;
683
684 return 0;
685}
686
687int config_parse_cpu_sched_prio(
688 const char *filename,
689 unsigned line,
690 const char *section,
691 const char *lvalue,
692 const char *rvalue,
693 void *data,
694 void *userdata) {
9eba9da4
LP
695
696 ExecContext *c = data;
697 int i;
698
699 assert(filename);
700 assert(lvalue);
701 assert(rvalue);
702 assert(data);
703
94f04347
LP
704 /* On Linux RR/FIFO have the same range */
705 if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) {
706 log_error("[%s:%u] Failed to parse CPU scheduling priority: %s", filename, line, rvalue);
707 return -EBADMSG;
708 }
9eba9da4 709
94f04347
LP
710 c->cpu_sched_priority = i;
711 c->cpu_sched_set = true;
712
713 return 0;
714}
715
716int config_parse_cpu_affinity(
717 const char *filename,
718 unsigned line,
719 const char *section,
720 const char *lvalue,
721 const char *rvalue,
722 void *data,
723 void *userdata) {
724
725 ExecContext *c = data;
726 char *w;
727 size_t l;
728 char *state;
729
730 assert(filename);
731 assert(lvalue);
732 assert(rvalue);
733 assert(data);
734
735 FOREACH_WORD(w, &l, rvalue, state) {
736 char *t;
737 int r;
738 unsigned cpu;
739
740 if (!(t = strndup(w, l)))
741 return -ENOMEM;
742
743 r = safe_atou(t, &cpu);
744 free(t);
745
746 if (r < 0 || cpu >= CPU_SETSIZE) {
747 log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue);
748 return -EBADMSG;
9eba9da4 749 }
94f04347
LP
750
751 CPU_SET(cpu, &c->cpu_affinity);
9eba9da4
LP
752 }
753
94f04347 754 c->cpu_affinity_set = true;
9eba9da4 755
94f04347
LP
756 return 0;
757}
758
759int config_parse_capabilities(
760 const char *filename,
761 unsigned line,
762 const char *section,
763 const char *lvalue,
764 const char *rvalue,
765 void *data,
766 void *userdata) {
767
768 ExecContext *c = data;
769 cap_t cap;
770
771 assert(filename);
772 assert(lvalue);
773 assert(rvalue);
774 assert(data);
775
776 if (!(cap = cap_from_text(rvalue))) {
777 if (errno == ENOMEM)
778 return -ENOMEM;
779
780 log_error("[%s:%u] Failed to parse capabilities: %s", filename, line, rvalue);
781 return -EBADMSG;
782 }
783
784 if (c->capabilities)
785 cap_free(c->capabilities);
786 c->capabilities = cap;
787
788 return 0;
789}
790
791int config_parse_secure_bits(
792 const char *filename,
793 unsigned line,
794 const char *section,
795 const char *lvalue,
796 const char *rvalue,
797 void *data,
798 void *userdata) {
799
800 ExecContext *c = data;
801 char *w;
802 size_t l;
803 char *state;
804
805 assert(filename);
806 assert(lvalue);
807 assert(rvalue);
808 assert(data);
809
810 FOREACH_WORD(w, &l, rvalue, state) {
811 if (first_word(w, "keep-caps"))
812 c->secure_bits |= SECURE_KEEP_CAPS;
813 else if (first_word(w, "keep-caps-locked"))
814 c->secure_bits |= SECURE_KEEP_CAPS_LOCKED;
815 else if (first_word(w, "no-setuid-fixup"))
816 c->secure_bits |= SECURE_NO_SETUID_FIXUP;
817 else if (first_word(w, "no-setuid-fixup-locked"))
818 c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED;
819 else if (first_word(w, "noroot"))
820 c->secure_bits |= SECURE_NOROOT;
821 else if (first_word(w, "noroot-locked"))
822 c->secure_bits |= SECURE_NOROOT_LOCKED;
9eba9da4 823 else {
94f04347 824 log_error("[%s:%u] Failed to parse secure bits: %s", filename, line, rvalue);
9eba9da4
LP
825 return -EBADMSG;
826 }
827 }
828
94f04347
LP
829 return 0;
830}
831
832int config_parse_bounding_set(
833 const char *filename,
834 unsigned line,
835 const char *section,
836 const char *lvalue,
837 const char *rvalue,
838 void *data,
839 void *userdata) {
840
841 ExecContext *c = data;
842 char *w;
843 size_t l;
844 char *state;
845
846 assert(filename);
847 assert(lvalue);
848 assert(rvalue);
849 assert(data);
850
851 FOREACH_WORD(w, &l, rvalue, state) {
852 char *t;
853 int r;
854 cap_value_t cap;
855
856 if (!(t = strndup(w, l)))
857 return -ENOMEM;
858
859 r = cap_from_name(t, &cap);
860 free(t);
861
862 if (r < 0) {
863 log_error("[%s:%u] Failed to parse capability bounding set: %s", filename, line, rvalue);
864 return -EBADMSG;
865 }
866
867 c->capability_bounding_set_drop |= 1 << cap;
868 }
9eba9da4
LP
869
870 return 0;
871}
872
94f04347 873static int config_parse_timer_slack_ns(
9eba9da4
LP
874 const char *filename,
875 unsigned line,
876 const char *section,
877 const char *lvalue,
878 const char *rvalue,
879 void *data,
880 void *userdata) {
881
882 ExecContext *c = data;
94f04347
LP
883 unsigned long u;
884 int r;
9eba9da4
LP
885
886 assert(filename);
887 assert(lvalue);
888 assert(rvalue);
889 assert(data);
890
94f04347
LP
891 if ((r = safe_atolu(rvalue, &u)) < 0) {
892 log_error("[%s:%u] Failed to parse time slack value: %s", filename, line, rvalue);
893 return r;
9eba9da4
LP
894 }
895
94f04347
LP
896 c->timer_slack_ns = u;
897
898 return 0;
899}
900
901static int config_parse_limit(
902 const char *filename,
903 unsigned line,
904 const char *section,
905 const char *lvalue,
906 const char *rvalue,
907 void *data,
908 void *userdata) {
909
910 struct rlimit **rl = data;
911 unsigned long long u;
912 int r;
913
914 assert(filename);
915 assert(lvalue);
916 assert(rvalue);
917 assert(data);
918
919 if ((r = safe_atollu(rvalue, &u)) < 0) {
920 log_error("[%s:%u] Failed to parse resource value: %s", filename, line, rvalue);
921 return r;
922 }
923
924 if (!*rl)
925 if (!(*rl = new(struct rlimit, 1)))
926 return -ENOMEM;
9eba9da4 927
94f04347 928 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
9eba9da4
LP
929 return 0;
930}
931
071830ff 932#define FOLLOW_MAX 8
87f0e418 933
0301abf4
LP
934static int open_follow(char **filename, FILE **_f, Set *names, char **_id) {
935 unsigned c = 0;
87f0e418
LP
936 int fd, r;
937 FILE *f;
0301abf4 938 char *id = NULL;
87f0e418
LP
939
940 assert(filename);
941 assert(*filename);
942 assert(_f);
943 assert(names);
944
0301abf4
LP
945 /* This will update the filename pointer if the loaded file is
946 * reached by a symlink. The old string will be freed. */
87f0e418 947
0301abf4 948 for (;;) {
87f0e418
LP
949 char *target, *k, *name;
950
0301abf4
LP
951 if (c++ >= FOLLOW_MAX)
952 return -ELOOP;
953
b08d03ff
LP
954 path_kill_slashes(*filename);
955
87f0e418
LP
956 /* Add the file name we are currently looking at to
957 * the names of this unit */
0301abf4
LP
958 name = file_name_from_path(*filename);
959 if (!(id = set_get(names, name))) {
87f0e418 960
0301abf4
LP
961 if (!(id = strdup(name)))
962 return -ENOMEM;
87f0e418 963
0301abf4
LP
964 if ((r = set_put(names, id)) < 0) {
965 free(id);
966 return r;
87f0e418 967 }
87f0e418
LP
968 }
969
0301abf4
LP
970 /* Try to open the file name, but don't if its a symlink */
971 if ((fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0)
87f0e418
LP
972 break;
973
0301abf4
LP
974 if (errno != ELOOP)
975 return -errno;
976
87f0e418 977 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
0301abf4
LP
978 if ((r = readlink_malloc(*filename, &target)) < 0)
979 return r;
87f0e418 980
c25fb0ed 981 k = file_in_same_dir(*filename, target);
87f0e418
LP
982 free(target);
983
0301abf4
LP
984 if (!k)
985 return -ENOMEM;
87f0e418 986
0301abf4
LP
987 free(*filename);
988 *filename = k;
87f0e418
LP
989 }
990
991 if (!(f = fdopen(fd, "r"))) {
992 r = -errno;
993 assert(close_nointr(fd) == 0);
0301abf4 994 return r;
87f0e418
LP
995 }
996
997 *_f = f;
0301abf4
LP
998 *_id = id;
999 return 0;
87f0e418
LP
1000}
1001
0301abf4 1002static int load_from_path(Unit *u, const char *path) {
87f0e418
LP
1003
1004 static const char* const section_table[_UNIT_TYPE_MAX] = {
1005 [UNIT_SERVICE] = "Service",
1006 [UNIT_TIMER] = "Timer",
1007 [UNIT_SOCKET] = "Socket",
1008 [UNIT_TARGET] = "Target",
1009 [UNIT_DEVICE] = "Device",
1010 [UNIT_MOUNT] = "Mount",
1011 [UNIT_AUTOMOUNT] = "Automount",
1012 [UNIT_SNAPSHOT] = "Snapshot"
42f4e3c4
LP
1013 };
1014
034c6ed7 1015#define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \
9eba9da4
LP
1016 { "WorkingDirectory", config_parse_path, &(context).working_directory, section }, \
1017 { "RootDirectory", config_parse_path, &(context).root_directory, section }, \
1c01f82b
LP
1018 { "User", config_parse_string, &(context).user, section }, \
1019 { "Group", config_parse_string, &(context).group, section }, \
1020 { "SupplementaryGroups", config_parse_strv, &(context).supplementary_groups, section }, \
fb33a393
LP
1021 { "Nice", config_parse_nice, &(context), section }, \
1022 { "OOMAdjust", config_parse_oom_adjust, &(context), section }, \
9eba9da4 1023 { "IOSchedulingClass", config_parse_io_class, &(context), section }, \
94f04347
LP
1024 { "IOSchedulingPriority", config_parse_io_priority, &(context), section }, \
1025 { "CPUSchedulingPolicy", config_parse_cpu_sched_policy,&(context), section }, \
1026 { "CPUSchedulingPriority", config_parse_cpu_sched_prio, &(context), section }, \
1027 { "CPUAffinity", config_parse_cpu_affinity, &(context), section }, \
1c01f82b 1028 { "UMask", config_parse_umask, &(context).umask, section }, \
071830ff
LP
1029 { "Environment", config_parse_strv, &(context).environment, section }, \
1030 { "Output", config_parse_output, &(context).output, section }, \
94f04347 1031 { "Input", config_parse_input, &(context).input, section }, \
071830ff
LP
1032 { "SyslogIdentifier", config_parse_string, &(context).syslog_identifier, section }, \
1033 { "SyslogFacility", config_parse_facility, &(context).syslog_priority, section }, \
94f04347
LP
1034 { "SyslogLevel", config_parse_level, &(context).syslog_priority, section }, \
1035 { "Capabilities", config_parse_capabilities, &(context), section }, \
1036 { "SecureBits", config_parse_secure_bits, &(context), section }, \
1037 { "CapabilityBoundingSetDrop", config_parse_bounding_set, &(context), section }, \
1038 { "TimerSlackNS", config_parse_timer_slack_ns, &(context), section }, \
1039 { "LimitCPU", config_parse_limit, &(context).rlimit[RLIMIT_CPU], section }, \
1040 { "LimitFSIZE", config_parse_limit, &(context).rlimit[RLIMIT_FSIZE], section }, \
1041 { "LimitDATA", config_parse_limit, &(context).rlimit[RLIMIT_DATA], section }, \
1042 { "LimitSTACK", config_parse_limit, &(context).rlimit[RLIMIT_STACK], section }, \
1043 { "LimitCORE", config_parse_limit, &(context).rlimit[RLIMIT_CORE], section }, \
1044 { "LimitRSS", config_parse_limit, &(context).rlimit[RLIMIT_RSS], section }, \
1045 { "LimitNOFILE", config_parse_limit, &(context).rlimit[RLIMIT_NOFILE], section }, \
1046 { "LimitAS", config_parse_limit, &(context).rlimit[RLIMIT_AS], section }, \
1047 { "LimitNPROC", config_parse_limit, &(context).rlimit[RLIMIT_NPROC], section }, \
1048 { "LimitMEMLOCK", config_parse_limit, &(context).rlimit[RLIMIT_MEMLOCK], section }, \
1049 { "LimitLOCKS", config_parse_limit, &(context).rlimit[RLIMIT_LOCKS], section }, \
1050 { "LimitSIGPENDING", config_parse_limit, &(context).rlimit[RLIMIT_SIGPENDING], section }, \
1051 { "LimitMSGQUEUE", config_parse_limit, &(context).rlimit[RLIMIT_MSGQUEUE], section }, \
1052 { "LimitNICE", config_parse_limit, &(context).rlimit[RLIMIT_NICE], section }, \
1053 { "LimitRTPRIO", config_parse_limit, &(context).rlimit[RLIMIT_RTPRIO], section }, \
1054 { "LimitRTTIME", config_parse_limit, &(context).rlimit[RLIMIT_RTTIME], section }
034c6ed7 1055
3efd4195 1056 const ConfigItem items[] = {
1c01f82b
LP
1057 { "Names", config_parse_names, u, "Meta" },
1058 { "Description", config_parse_string, &u->meta.description, "Meta" },
1059 { "Requires", config_parse_deps, UINT_TO_PTR(UNIT_REQUIRES), "Meta" },
1060 { "SoftRequires", config_parse_deps, UINT_TO_PTR(UNIT_SOFT_REQUIRES), "Meta" },
1061 { "Wants", config_parse_deps, UINT_TO_PTR(UNIT_WANTS), "Meta" },
1062 { "Requisite", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE), "Meta" },
1063 { "SoftRequisite", config_parse_deps, UINT_TO_PTR(UNIT_SOFT_REQUISITE), "Meta" },
1064 { "Conflicts", config_parse_deps, UINT_TO_PTR(UNIT_CONFLICTS), "Meta" },
1065 { "Before", config_parse_deps, UINT_TO_PTR(UNIT_BEFORE), "Meta" },
1066 { "After", config_parse_deps, UINT_TO_PTR(UNIT_AFTER), "Meta" },
f3bff0eb
LP
1067 { "RecursiveStop", config_parse_bool, &u->meta.recursive_stop, "Meta" },
1068 { "StopWhenUnneeded", config_parse_bool, &u->meta.stop_when_unneeded, "Meta" },
1c01f82b
LP
1069
1070 { "PIDFile", config_parse_path, &u->service.pid_file, "Service" },
1071 { "ExecStartPre", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START_PRE, "Service" },
1072 { "ExecStart", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START, "Service" },
1073 { "ExecStartPost", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START_POST, "Service" },
1074 { "ExecReload", config_parse_exec, u->service.exec_command+SERVICE_EXEC_RELOAD, "Service" },
1075 { "ExecStop", config_parse_exec, u->service.exec_command+SERVICE_EXEC_STOP, "Service" },
1076 { "ExecStopPost", config_parse_exec, u->service.exec_command+SERVICE_EXEC_STOP_POST, "Service" },
1077 { "RestartSec", config_parse_usec, &u->service.restart_usec, "Service" },
1078 { "TimeoutSec", config_parse_usec, &u->service.timeout_usec, "Service" },
1079 { "Type", config_parse_service_type, &u->service, "Service" },
1080 { "Restart", config_parse_service_restart, &u->service, "Service" },
87f0e418
LP
1081 EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
1082
1c01f82b
LP
1083 { "ListenStream", config_parse_listen, &u->socket, "Socket" },
1084 { "ListenDatagram", config_parse_listen, &u->socket, "Socket" },
1085 { "ListenSequentialPacket", config_parse_listen, &u->socket, "Socket" },
1086 { "ListenFIFO", config_parse_listen, &u->socket, "Socket" },
1087 { "BindIPv6Only", config_parse_socket_bind, &u->socket, "Socket" },
1088 { "Backlog", config_parse_unsigned, &u->socket.backlog, "Socket" },
acbb0225 1089 { "BindToDevice", config_parse_bindtodevice, &u->socket, "Socket" },
1c01f82b
LP
1090 { "ExecStartPre", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_START_PRE, "Socket" },
1091 { "ExecStartPost", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_START_POST, "Socket" },
1092 { "ExecStopPre", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_STOP_PRE, "Socket" },
1093 { "ExecStopPost", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_STOP_POST, "Socket" },
87f0e418
LP
1094 EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
1095
1096 EXEC_CONTEXT_CONFIG_ITEMS(u->automount.exec_context, "Automount"),
034c6ed7 1097
3efd4195
LP
1098 { NULL, NULL, NULL, NULL }
1099 };
1100
034c6ed7 1101#undef EXEC_CONTEXT_CONFIG_ITEMS
42f4e3c4 1102
42f4e3c4 1103 const char *sections[3];
0301abf4
LP
1104 char *k;
1105 int r;
87f0e418 1106 Set *symlink_names;
0301abf4
LP
1107 FILE *f;
1108 char *filename, *id;
3efd4195 1109
42f4e3c4 1110 sections[0] = "Meta";
87f0e418 1111 sections[1] = section_table[u->meta.type];
42f4e3c4
LP
1112 sections[2] = NULL;
1113
87f0e418
LP
1114 if (!(symlink_names = set_new(string_hash_func, string_compare_func)))
1115 return -ENOMEM;
3efd4195 1116
0301abf4
LP
1117 /* Instead of opening the path right away, we manually
1118 * follow all symlinks and add their name to our unit
1119 * name set while doing so */
1120 if (!(filename = path_make_absolute(path, unit_path()))) {
1121 r = -ENOMEM;
1122 goto finish;
1123 }
3efd4195 1124
0301abf4
LP
1125 if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) {
1126 if (r == -ENOENT)
1127 r = 0; /* returning 0 means: no suitable config file found */
034c6ed7 1128
0301abf4
LP
1129 goto finish;
1130 }
87f0e418 1131
0301abf4
LP
1132 /* Now, parse the file contents */
1133 r = config_parse(filename, f, sections, items, u);
1134 if (r < 0)
1135 goto finish;
87f0e418 1136
0301abf4
LP
1137 /* Let's try to add in all symlink names we found */
1138 while ((k = set_steal_first(symlink_names))) {
1139 if ((r = unit_add_name(u, k)) < 0)
87f0e418
LP
1140 goto finish;
1141
87f0e418 1142
f50e0a01
LP
1143 if (id == k)
1144 unit_choose_id(u, id);
0301abf4 1145 free(k);
034c6ed7
LP
1146 }
1147
b08d03ff 1148
0301abf4
LP
1149 free(u->meta.load_path);
1150 u->meta.load_path = filename;
1151 filename = NULL;
87f0e418 1152
0301abf4 1153 r = 1; /* returning 1 means: suitable config file found and loaded */
87f0e418
LP
1154
1155finish:
1156 while ((k = set_steal_first(symlink_names)))
1157 free(k);
87f0e418 1158 set_free(symlink_names);
0301abf4
LP
1159 free(filename);
1160
1161 return r;
1162}
1163
1164int unit_load_fragment(Unit *u) {
d46de8a1 1165 int r = 0;
071830ff 1166 ExecContext *c;
0301abf4
LP
1167
1168 assert(u);
1169 assert(u->meta.load_state == UNIT_STUB);
1170
1171 if (u->meta.load_path)
1172 r = load_from_path(u, u->meta.load_path);
1173 else {
1174 Iterator i;
1175 char *t;
1176
1177 /* Try to find a name we can load this with */
1178 SET_FOREACH(t, u->meta.names, i)
1179 if ((r = load_from_path(u, t)) != 0)
1180 return r;
1181 }
87f0e418 1182
071830ff
LP
1183 if (u->meta.type == UNIT_SOCKET)
1184 c = &u->socket.exec_context;
1185 else if (u->meta.type == UNIT_SERVICE)
1186 c = &u->service.exec_context;
1187 else
1188 c = NULL;
1189
1190 if (r >= 0 && c &&
94f04347 1191 (c->output == EXEC_OUTPUT_KERNEL || c->output == EXEC_OUTPUT_SYSLOG)) {
d46de8a1
LP
1192 int k;
1193
071830ff
LP
1194 /* If syslog or kernel logging is requested, make sure
1195 * our own logging daemon is run first. */
1196
f50e0a01 1197 if ((k = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET)) < 0)
d46de8a1 1198 return k;
071830ff 1199
f50e0a01 1200 if ((k = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET)) < 0)
d46de8a1 1201 return k;
071830ff
LP
1202 }
1203
87f0e418 1204 return r;
3efd4195 1205}