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