]>
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 | 58 | |
f62c0e4f | 59 | FOREACH_WORD(w, l, rvalue, state) { |
3efd4195 LP |
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 | ||
f62c0e4f | 95 | FOREACH_WORD(w, l, rvalue, state) { |
87d1515d LP |
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 | ||
b5a0699f | 288 | static int config_parse_mode( |
034c6ed7 LP |
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) { | |
b5a0699f | 309 | log_error("[%s:%u] Failed to parse mode value: %s", filename, line, rvalue); |
034c6ed7 LP |
310 | return errno ? -errno : -EINVAL; |
311 | } | |
312 | ||
b5a0699f LP |
313 | if (l < 0000 || l > 07777) { |
314 | log_error("[%s:%u] mode value out of range: %s", filename, line, rvalue); | |
034c6ed7 LP |
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 | ||
a6a80b4f | 331 | ExecCommand **e = data, *nce = NULL; |
034c6ed7 LP |
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 | ||
a6a80b4f | 370 | exec_command_append_list(e, nce); |
034c6ed7 LP |
371 | |
372 | return 0; | |
373 | ||
374 | fail: | |
375 | for (; k > 0; k--) | |
376 | free(n[k-1]); | |
377 | free(n); | |
378 | ||
379 | free(nce); | |
380 | ||
381 | return -ENOMEM; | |
382 | } | |
383 | ||
384 | static int config_parse_usec( | |
385 | const char *filename, | |
386 | unsigned line, | |
387 | const char *section, | |
388 | const char *lvalue, | |
389 | const char *rvalue, | |
390 | void *data, | |
391 | void *userdata) { | |
392 | ||
393 | usec_t *usec = data; | |
394 | unsigned long long u; | |
395 | int r; | |
396 | ||
397 | assert(filename); | |
398 | assert(lvalue); | |
399 | assert(rvalue); | |
400 | assert(data); | |
401 | ||
402 | if ((r = safe_atollu(rvalue, &u)) < 0) { | |
403 | log_error("[%s:%u] Failed to parse time value: %s", filename, line, rvalue); | |
404 | return r; | |
405 | } | |
406 | ||
407 | /* We actually assume the user configures seconds. Later on we | |
408 | * might choose to support suffixes for time values, to | |
409 | * configure bigger or smaller units */ | |
410 | ||
411 | *usec = u * USEC_PER_SEC; | |
412 | ||
413 | return 0; | |
414 | } | |
415 | ||
416 | static int config_parse_service_type( | |
417 | const char *filename, | |
418 | unsigned line, | |
419 | const char *section, | |
420 | const char *lvalue, | |
421 | const char *rvalue, | |
422 | void *data, | |
423 | void *userdata) { | |
424 | ||
425 | Service *s = data; | |
94f04347 | 426 | ServiceType x; |
034c6ed7 LP |
427 | |
428 | assert(filename); | |
429 | assert(lvalue); | |
430 | assert(rvalue); | |
431 | assert(data); | |
432 | ||
94f04347 | 433 | if ((x = service_type_from_string(rvalue)) < 0) { |
034c6ed7 LP |
434 | log_error("[%s:%u] Failed to parse service type: %s", filename, line, rvalue); |
435 | return -EBADMSG; | |
436 | } | |
437 | ||
94f04347 LP |
438 | s->type = x; |
439 | ||
034c6ed7 LP |
440 | return 0; |
441 | } | |
442 | ||
443 | static int config_parse_service_restart( | |
444 | const char *filename, | |
445 | unsigned line, | |
446 | const char *section, | |
447 | const char *lvalue, | |
448 | const char *rvalue, | |
449 | void *data, | |
450 | void *userdata) { | |
451 | ||
452 | Service *s = data; | |
94f04347 | 453 | ServiceRestart x; |
034c6ed7 LP |
454 | |
455 | assert(filename); | |
456 | assert(lvalue); | |
457 | assert(rvalue); | |
458 | assert(data); | |
459 | ||
94f04347 LP |
460 | if ((x = service_restart_from_string(rvalue)) < 0) { |
461 | log_error("[%s:%u] Failed to parse service restart specifier: %s", filename, line, rvalue); | |
034c6ed7 LP |
462 | return -EBADMSG; |
463 | } | |
464 | ||
94f04347 LP |
465 | s->restart = x; |
466 | ||
034c6ed7 LP |
467 | return 0; |
468 | } | |
469 | ||
47be870b | 470 | static int config_parse_bindtodevice( |
acbb0225 LP |
471 | const char *filename, |
472 | unsigned line, | |
473 | const char *section, | |
474 | const char *lvalue, | |
475 | const char *rvalue, | |
476 | void *data, | |
477 | void *userdata) { | |
478 | ||
479 | Socket *s = data; | |
480 | char *n; | |
481 | ||
482 | assert(filename); | |
483 | assert(lvalue); | |
484 | assert(rvalue); | |
485 | assert(data); | |
486 | ||
487 | if (rvalue[0] && !streq(rvalue, "*")) { | |
488 | if (!(n = strdup(rvalue))) | |
489 | return -ENOMEM; | |
490 | } else | |
491 | n = NULL; | |
492 | ||
493 | free(s->bind_to_device); | |
494 | s->bind_to_device = n; | |
495 | ||
496 | return 0; | |
497 | } | |
498 | ||
47be870b | 499 | static int config_parse_output( |
071830ff LP |
500 | const char *filename, |
501 | unsigned line, | |
502 | const char *section, | |
503 | const char *lvalue, | |
504 | const char *rvalue, | |
505 | void *data, | |
506 | void *userdata) { | |
507 | ||
94f04347 | 508 | ExecOutput *o = data, x; |
071830ff LP |
509 | |
510 | assert(filename); | |
511 | assert(lvalue); | |
512 | assert(rvalue); | |
513 | assert(data); | |
514 | ||
94f04347 LP |
515 | if ((x = exec_output_from_string(rvalue)) < 0) { |
516 | log_error("[%s:%u] Failed to parse output specifier: %s", filename, line, rvalue); | |
517 | return -EBADMSG; | |
518 | } | |
519 | ||
520 | *o = x; | |
521 | ||
522 | return 0; | |
523 | } | |
524 | ||
47be870b | 525 | static int config_parse_input( |
94f04347 LP |
526 | const char *filename, |
527 | unsigned line, | |
528 | const char *section, | |
529 | const char *lvalue, | |
530 | const char *rvalue, | |
531 | void *data, | |
532 | void *userdata) { | |
533 | ||
534 | ExecInput *i = data, x; | |
535 | ||
536 | assert(filename); | |
537 | assert(lvalue); | |
538 | assert(rvalue); | |
539 | assert(data); | |
540 | ||
541 | if ((x = exec_input_from_string(rvalue)) < 0) { | |
542 | log_error("[%s:%u] Failed to parse input specifier: %s", filename, line, rvalue); | |
071830ff LP |
543 | return -EBADMSG; |
544 | } | |
545 | ||
94f04347 LP |
546 | *i = x; |
547 | ||
071830ff LP |
548 | return 0; |
549 | } | |
87f0e418 | 550 | |
47be870b | 551 | static int config_parse_facility( |
071830ff LP |
552 | const char *filename, |
553 | unsigned line, | |
554 | const char *section, | |
555 | const char *lvalue, | |
556 | const char *rvalue, | |
557 | void *data, | |
558 | void *userdata) { | |
559 | ||
071830ff | 560 | |
94f04347 | 561 | int *o = data, x; |
071830ff LP |
562 | |
563 | assert(filename); | |
564 | assert(lvalue); | |
565 | assert(rvalue); | |
566 | assert(data); | |
567 | ||
94f04347 | 568 | if ((x = log_facility_from_string(rvalue)) < 0) |
071830ff LP |
569 | |
570 | /* Second try, let's see if this is a number. */ | |
94f04347 LP |
571 | if (safe_atoi(rvalue, &x) < 0 || !log_facility_to_string(x)) { |
572 | log_error("[%s:%u] Failed to parse log facility: %s", filename, line, rvalue); | |
071830ff LP |
573 | return -EBADMSG; |
574 | } | |
94f04347 LP |
575 | |
576 | *o = LOG_MAKEPRI(x, LOG_PRI(*o)); | |
071830ff LP |
577 | |
578 | return 0; | |
579 | } | |
580 | ||
47be870b | 581 | static int config_parse_level( |
071830ff LP |
582 | const char *filename, |
583 | unsigned line, | |
584 | const char *section, | |
585 | const char *lvalue, | |
586 | const char *rvalue, | |
587 | void *data, | |
588 | void *userdata) { | |
589 | ||
071830ff | 590 | |
94f04347 | 591 | int *o = data, x; |
071830ff LP |
592 | |
593 | assert(filename); | |
594 | assert(lvalue); | |
595 | assert(rvalue); | |
596 | assert(data); | |
597 | ||
94f04347 LP |
598 | if ((x = log_level_from_string(rvalue)) < 0) |
599 | ||
600 | /* Second try, let's see if this is a number. */ | |
601 | if (safe_atoi(rvalue, &x) < 0 || !log_level_to_string(x)) { | |
602 | log_error("[%s:%u] Failed to parse log level: %s", filename, line, rvalue); | |
603 | return -EBADMSG; | |
071830ff LP |
604 | } |
605 | ||
94f04347 LP |
606 | *o = LOG_MAKEPRI(LOG_FAC(*o), x); |
607 | return 0; | |
608 | } | |
609 | ||
47be870b | 610 | static int config_parse_io_class( |
94f04347 LP |
611 | const char *filename, |
612 | unsigned line, | |
613 | const char *section, | |
614 | const char *lvalue, | |
615 | const char *rvalue, | |
616 | void *data, | |
617 | void *userdata) { | |
618 | ||
619 | ExecContext *c = data; | |
620 | int x; | |
621 | ||
622 | assert(filename); | |
623 | assert(lvalue); | |
624 | assert(rvalue); | |
625 | assert(data); | |
626 | ||
627 | if ((x = ioprio_class_from_string(rvalue)) < 0) | |
071830ff LP |
628 | |
629 | /* Second try, let's see if this is a number. */ | |
94f04347 LP |
630 | if (safe_atoi(rvalue, &x) < 0 || !ioprio_class_to_string(x)) { |
631 | log_error("[%s:%u] Failed to parse IO scheduling class: %s", filename, line, rvalue); | |
071830ff LP |
632 | return -EBADMSG; |
633 | } | |
94f04347 LP |
634 | |
635 | c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio)); | |
636 | c->ioprio_set = true; | |
637 | ||
638 | return 0; | |
639 | } | |
640 | ||
47be870b | 641 | static int config_parse_io_priority( |
94f04347 LP |
642 | const char *filename, |
643 | unsigned line, | |
644 | const char *section, | |
645 | const char *lvalue, | |
646 | const char *rvalue, | |
647 | void *data, | |
648 | void *userdata) { | |
649 | ||
650 | ExecContext *c = data; | |
651 | int i; | |
652 | ||
653 | assert(filename); | |
654 | assert(lvalue); | |
655 | assert(rvalue); | |
656 | assert(data); | |
657 | ||
658 | if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) { | |
659 | log_error("[%s:%u] Failed to parse io priority: %s", filename, line, rvalue); | |
660 | return -EBADMSG; | |
071830ff LP |
661 | } |
662 | ||
94f04347 LP |
663 | c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i); |
664 | c->ioprio_set = true; | |
665 | ||
071830ff LP |
666 | return 0; |
667 | } | |
668 | ||
47be870b | 669 | static int config_parse_cpu_sched_policy( |
9eba9da4 LP |
670 | const char *filename, |
671 | unsigned line, | |
672 | const char *section, | |
673 | const char *lvalue, | |
674 | const char *rvalue, | |
675 | void *data, | |
676 | void *userdata) { | |
677 | ||
94f04347 LP |
678 | |
679 | ExecContext *c = data; | |
680 | int x; | |
681 | ||
682 | assert(filename); | |
683 | assert(lvalue); | |
684 | assert(rvalue); | |
685 | assert(data); | |
686 | ||
687 | if ((x = sched_policy_from_string(rvalue)) < 0) | |
688 | ||
689 | /* Second try, let's see if this is a number. */ | |
690 | if (safe_atoi(rvalue, &x) < 0 || !sched_policy_to_string(x)) { | |
691 | log_error("[%s:%u] Failed to parse CPU scheduling policy: %s", filename, line, rvalue); | |
692 | return -EBADMSG; | |
693 | } | |
694 | ||
695 | c->cpu_sched_policy = x; | |
696 | c->cpu_sched_set = true; | |
697 | ||
698 | return 0; | |
699 | } | |
700 | ||
47be870b | 701 | static int config_parse_cpu_sched_prio( |
94f04347 LP |
702 | const char *filename, |
703 | unsigned line, | |
704 | const char *section, | |
705 | const char *lvalue, | |
706 | const char *rvalue, | |
707 | void *data, | |
708 | void *userdata) { | |
9eba9da4 LP |
709 | |
710 | ExecContext *c = data; | |
711 | int i; | |
712 | ||
713 | assert(filename); | |
714 | assert(lvalue); | |
715 | assert(rvalue); | |
716 | assert(data); | |
717 | ||
94f04347 LP |
718 | /* On Linux RR/FIFO have the same range */ |
719 | if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) { | |
720 | log_error("[%s:%u] Failed to parse CPU scheduling priority: %s", filename, line, rvalue); | |
721 | return -EBADMSG; | |
722 | } | |
9eba9da4 | 723 | |
94f04347 LP |
724 | c->cpu_sched_priority = i; |
725 | c->cpu_sched_set = true; | |
726 | ||
727 | return 0; | |
728 | } | |
729 | ||
47be870b | 730 | static int config_parse_cpu_affinity( |
94f04347 LP |
731 | const char *filename, |
732 | unsigned line, | |
733 | const char *section, | |
734 | const char *lvalue, | |
735 | const char *rvalue, | |
736 | void *data, | |
737 | void *userdata) { | |
738 | ||
739 | ExecContext *c = data; | |
740 | char *w; | |
741 | size_t l; | |
742 | char *state; | |
743 | ||
744 | assert(filename); | |
745 | assert(lvalue); | |
746 | assert(rvalue); | |
747 | assert(data); | |
748 | ||
f62c0e4f | 749 | FOREACH_WORD(w, l, rvalue, state) { |
94f04347 LP |
750 | char *t; |
751 | int r; | |
752 | unsigned cpu; | |
753 | ||
754 | if (!(t = strndup(w, l))) | |
755 | return -ENOMEM; | |
756 | ||
757 | r = safe_atou(t, &cpu); | |
758 | free(t); | |
759 | ||
760 | if (r < 0 || cpu >= CPU_SETSIZE) { | |
761 | log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue); | |
762 | return -EBADMSG; | |
9eba9da4 | 763 | } |
94f04347 LP |
764 | |
765 | CPU_SET(cpu, &c->cpu_affinity); | |
9eba9da4 LP |
766 | } |
767 | ||
94f04347 | 768 | c->cpu_affinity_set = true; |
9eba9da4 | 769 | |
94f04347 LP |
770 | return 0; |
771 | } | |
772 | ||
47be870b | 773 | static int config_parse_capabilities( |
94f04347 LP |
774 | const char *filename, |
775 | unsigned line, | |
776 | const char *section, | |
777 | const char *lvalue, | |
778 | const char *rvalue, | |
779 | void *data, | |
780 | void *userdata) { | |
781 | ||
782 | ExecContext *c = data; | |
783 | cap_t cap; | |
784 | ||
785 | assert(filename); | |
786 | assert(lvalue); | |
787 | assert(rvalue); | |
788 | assert(data); | |
789 | ||
790 | if (!(cap = cap_from_text(rvalue))) { | |
791 | if (errno == ENOMEM) | |
792 | return -ENOMEM; | |
793 | ||
794 | log_error("[%s:%u] Failed to parse capabilities: %s", filename, line, rvalue); | |
795 | return -EBADMSG; | |
796 | } | |
797 | ||
798 | if (c->capabilities) | |
799 | cap_free(c->capabilities); | |
800 | c->capabilities = cap; | |
801 | ||
802 | return 0; | |
803 | } | |
804 | ||
47be870b | 805 | static int config_parse_secure_bits( |
94f04347 LP |
806 | const char *filename, |
807 | unsigned line, | |
808 | const char *section, | |
809 | const char *lvalue, | |
810 | const char *rvalue, | |
811 | void *data, | |
812 | void *userdata) { | |
813 | ||
814 | ExecContext *c = data; | |
815 | char *w; | |
816 | size_t l; | |
817 | char *state; | |
818 | ||
819 | assert(filename); | |
820 | assert(lvalue); | |
821 | assert(rvalue); | |
822 | assert(data); | |
823 | ||
f62c0e4f | 824 | FOREACH_WORD(w, l, rvalue, state) { |
94f04347 LP |
825 | if (first_word(w, "keep-caps")) |
826 | c->secure_bits |= SECURE_KEEP_CAPS; | |
827 | else if (first_word(w, "keep-caps-locked")) | |
828 | c->secure_bits |= SECURE_KEEP_CAPS_LOCKED; | |
829 | else if (first_word(w, "no-setuid-fixup")) | |
830 | c->secure_bits |= SECURE_NO_SETUID_FIXUP; | |
831 | else if (first_word(w, "no-setuid-fixup-locked")) | |
832 | c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED; | |
833 | else if (first_word(w, "noroot")) | |
834 | c->secure_bits |= SECURE_NOROOT; | |
835 | else if (first_word(w, "noroot-locked")) | |
836 | c->secure_bits |= SECURE_NOROOT_LOCKED; | |
9eba9da4 | 837 | else { |
94f04347 | 838 | log_error("[%s:%u] Failed to parse secure bits: %s", filename, line, rvalue); |
9eba9da4 LP |
839 | return -EBADMSG; |
840 | } | |
841 | } | |
842 | ||
94f04347 LP |
843 | return 0; |
844 | } | |
845 | ||
47be870b | 846 | static int config_parse_bounding_set( |
94f04347 LP |
847 | const char *filename, |
848 | unsigned line, | |
849 | const char *section, | |
850 | const char *lvalue, | |
851 | const char *rvalue, | |
852 | void *data, | |
853 | void *userdata) { | |
854 | ||
855 | ExecContext *c = data; | |
856 | char *w; | |
857 | size_t l; | |
858 | char *state; | |
859 | ||
860 | assert(filename); | |
861 | assert(lvalue); | |
862 | assert(rvalue); | |
863 | assert(data); | |
864 | ||
f62c0e4f | 865 | FOREACH_WORD(w, l, rvalue, state) { |
94f04347 LP |
866 | char *t; |
867 | int r; | |
868 | cap_value_t cap; | |
869 | ||
870 | if (!(t = strndup(w, l))) | |
871 | return -ENOMEM; | |
872 | ||
873 | r = cap_from_name(t, &cap); | |
874 | free(t); | |
875 | ||
876 | if (r < 0) { | |
877 | log_error("[%s:%u] Failed to parse capability bounding set: %s", filename, line, rvalue); | |
878 | return -EBADMSG; | |
879 | } | |
880 | ||
881 | c->capability_bounding_set_drop |= 1 << cap; | |
882 | } | |
9eba9da4 LP |
883 | |
884 | return 0; | |
885 | } | |
886 | ||
94f04347 | 887 | static int config_parse_timer_slack_ns( |
9eba9da4 LP |
888 | const char *filename, |
889 | unsigned line, | |
890 | const char *section, | |
891 | const char *lvalue, | |
892 | const char *rvalue, | |
893 | void *data, | |
894 | void *userdata) { | |
895 | ||
896 | ExecContext *c = data; | |
94f04347 LP |
897 | unsigned long u; |
898 | int r; | |
9eba9da4 LP |
899 | |
900 | assert(filename); | |
901 | assert(lvalue); | |
902 | assert(rvalue); | |
903 | assert(data); | |
904 | ||
94f04347 LP |
905 | if ((r = safe_atolu(rvalue, &u)) < 0) { |
906 | log_error("[%s:%u] Failed to parse time slack value: %s", filename, line, rvalue); | |
907 | return r; | |
9eba9da4 LP |
908 | } |
909 | ||
94f04347 LP |
910 | c->timer_slack_ns = u; |
911 | ||
912 | return 0; | |
913 | } | |
914 | ||
915 | static int config_parse_limit( | |
916 | const char *filename, | |
917 | unsigned line, | |
918 | const char *section, | |
919 | const char *lvalue, | |
920 | const char *rvalue, | |
921 | void *data, | |
922 | void *userdata) { | |
923 | ||
924 | struct rlimit **rl = data; | |
925 | unsigned long long u; | |
926 | int r; | |
927 | ||
928 | assert(filename); | |
929 | assert(lvalue); | |
930 | assert(rvalue); | |
931 | assert(data); | |
932 | ||
933 | if ((r = safe_atollu(rvalue, &u)) < 0) { | |
934 | log_error("[%s:%u] Failed to parse resource value: %s", filename, line, rvalue); | |
935 | return r; | |
936 | } | |
937 | ||
938 | if (!*rl) | |
939 | if (!(*rl = new(struct rlimit, 1))) | |
940 | return -ENOMEM; | |
9eba9da4 | 941 | |
94f04347 | 942 | (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u; |
9eba9da4 LP |
943 | return 0; |
944 | } | |
945 | ||
8e274523 LP |
946 | static int config_parse_cgroup( |
947 | const char *filename, | |
948 | unsigned line, | |
949 | const char *section, | |
950 | const char *lvalue, | |
951 | const char *rvalue, | |
952 | void *data, | |
953 | void *userdata) { | |
954 | ||
955 | Unit *u = userdata; | |
956 | char *w; | |
957 | size_t l; | |
958 | char *state; | |
959 | ||
960 | FOREACH_WORD(w, l, rvalue, state) { | |
961 | char *t; | |
962 | int r; | |
963 | ||
964 | if (!(t = strndup(w, l))) | |
965 | return -ENOMEM; | |
966 | ||
967 | r = unit_add_cgroup_from_text(u, t); | |
968 | free(t); | |
969 | ||
970 | if (r < 0) | |
971 | return r; | |
972 | } | |
973 | ||
974 | return 0; | |
975 | } | |
976 | ||
071830ff | 977 | #define FOLLOW_MAX 8 |
87f0e418 | 978 | |
0301abf4 LP |
979 | static int open_follow(char **filename, FILE **_f, Set *names, char **_id) { |
980 | unsigned c = 0; | |
87f0e418 LP |
981 | int fd, r; |
982 | FILE *f; | |
0301abf4 | 983 | char *id = NULL; |
87f0e418 LP |
984 | |
985 | assert(filename); | |
986 | assert(*filename); | |
987 | assert(_f); | |
988 | assert(names); | |
989 | ||
0301abf4 LP |
990 | /* This will update the filename pointer if the loaded file is |
991 | * reached by a symlink. The old string will be freed. */ | |
87f0e418 | 992 | |
0301abf4 | 993 | for (;;) { |
87f0e418 LP |
994 | char *target, *k, *name; |
995 | ||
0301abf4 LP |
996 | if (c++ >= FOLLOW_MAX) |
997 | return -ELOOP; | |
998 | ||
b08d03ff LP |
999 | path_kill_slashes(*filename); |
1000 | ||
87f0e418 LP |
1001 | /* Add the file name we are currently looking at to |
1002 | * the names of this unit */ | |
0301abf4 LP |
1003 | name = file_name_from_path(*filename); |
1004 | if (!(id = set_get(names, name))) { | |
87f0e418 | 1005 | |
0301abf4 LP |
1006 | if (!(id = strdup(name))) |
1007 | return -ENOMEM; | |
87f0e418 | 1008 | |
0301abf4 LP |
1009 | if ((r = set_put(names, id)) < 0) { |
1010 | free(id); | |
1011 | return r; | |
87f0e418 | 1012 | } |
87f0e418 LP |
1013 | } |
1014 | ||
0301abf4 LP |
1015 | /* Try to open the file name, but don't if its a symlink */ |
1016 | if ((fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0) | |
87f0e418 LP |
1017 | break; |
1018 | ||
0301abf4 LP |
1019 | if (errno != ELOOP) |
1020 | return -errno; | |
1021 | ||
87f0e418 | 1022 | /* Hmm, so this is a symlink. Let's read the name, and follow it manually */ |
0301abf4 LP |
1023 | if ((r = readlink_malloc(*filename, &target)) < 0) |
1024 | return r; | |
87f0e418 | 1025 | |
c25fb0ed | 1026 | k = file_in_same_dir(*filename, target); |
87f0e418 LP |
1027 | free(target); |
1028 | ||
0301abf4 LP |
1029 | if (!k) |
1030 | return -ENOMEM; | |
87f0e418 | 1031 | |
0301abf4 LP |
1032 | free(*filename); |
1033 | *filename = k; | |
87f0e418 LP |
1034 | } |
1035 | ||
1036 | if (!(f = fdopen(fd, "r"))) { | |
1037 | r = -errno; | |
1038 | assert(close_nointr(fd) == 0); | |
0301abf4 | 1039 | return r; |
87f0e418 LP |
1040 | } |
1041 | ||
1042 | *_f = f; | |
0301abf4 LP |
1043 | *_id = id; |
1044 | return 0; | |
87f0e418 LP |
1045 | } |
1046 | ||
0301abf4 | 1047 | static int load_from_path(Unit *u, const char *path) { |
87f0e418 LP |
1048 | |
1049 | static const char* const section_table[_UNIT_TYPE_MAX] = { | |
1050 | [UNIT_SERVICE] = "Service", | |
1051 | [UNIT_TIMER] = "Timer", | |
1052 | [UNIT_SOCKET] = "Socket", | |
1053 | [UNIT_TARGET] = "Target", | |
1054 | [UNIT_DEVICE] = "Device", | |
1055 | [UNIT_MOUNT] = "Mount", | |
1056 | [UNIT_AUTOMOUNT] = "Automount", | |
1057 | [UNIT_SNAPSHOT] = "Snapshot" | |
42f4e3c4 LP |
1058 | }; |
1059 | ||
034c6ed7 | 1060 | #define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \ |
9eba9da4 LP |
1061 | { "WorkingDirectory", config_parse_path, &(context).working_directory, section }, \ |
1062 | { "RootDirectory", config_parse_path, &(context).root_directory, section }, \ | |
1c01f82b LP |
1063 | { "User", config_parse_string, &(context).user, section }, \ |
1064 | { "Group", config_parse_string, &(context).group, section }, \ | |
1065 | { "SupplementaryGroups", config_parse_strv, &(context).supplementary_groups, section }, \ | |
fb33a393 LP |
1066 | { "Nice", config_parse_nice, &(context), section }, \ |
1067 | { "OOMAdjust", config_parse_oom_adjust, &(context), section }, \ | |
9eba9da4 | 1068 | { "IOSchedulingClass", config_parse_io_class, &(context), section }, \ |
94f04347 LP |
1069 | { "IOSchedulingPriority", config_parse_io_priority, &(context), section }, \ |
1070 | { "CPUSchedulingPolicy", config_parse_cpu_sched_policy,&(context), section }, \ | |
1071 | { "CPUSchedulingPriority", config_parse_cpu_sched_prio, &(context), section }, \ | |
38b48754 | 1072 | { "CPUSchedulingResetOnFork", config_parse_bool, &(context).cpu_sched_reset_on_fork, section }, \ |
94f04347 | 1073 | { "CPUAffinity", config_parse_cpu_affinity, &(context), section }, \ |
b5a0699f | 1074 | { "UMask", config_parse_mode, &(context).umask, section }, \ |
071830ff LP |
1075 | { "Environment", config_parse_strv, &(context).environment, section }, \ |
1076 | { "Output", config_parse_output, &(context).output, section }, \ | |
94f04347 | 1077 | { "Input", config_parse_input, &(context).input, section }, \ |
071830ff LP |
1078 | { "SyslogIdentifier", config_parse_string, &(context).syslog_identifier, section }, \ |
1079 | { "SyslogFacility", config_parse_facility, &(context).syslog_priority, section }, \ | |
94f04347 LP |
1080 | { "SyslogLevel", config_parse_level, &(context).syslog_priority, section }, \ |
1081 | { "Capabilities", config_parse_capabilities, &(context), section }, \ | |
1082 | { "SecureBits", config_parse_secure_bits, &(context), section }, \ | |
1083 | { "CapabilityBoundingSetDrop", config_parse_bounding_set, &(context), section }, \ | |
1084 | { "TimerSlackNS", config_parse_timer_slack_ns, &(context), section }, \ | |
1085 | { "LimitCPU", config_parse_limit, &(context).rlimit[RLIMIT_CPU], section }, \ | |
1086 | { "LimitFSIZE", config_parse_limit, &(context).rlimit[RLIMIT_FSIZE], section }, \ | |
1087 | { "LimitDATA", config_parse_limit, &(context).rlimit[RLIMIT_DATA], section }, \ | |
1088 | { "LimitSTACK", config_parse_limit, &(context).rlimit[RLIMIT_STACK], section }, \ | |
1089 | { "LimitCORE", config_parse_limit, &(context).rlimit[RLIMIT_CORE], section }, \ | |
1090 | { "LimitRSS", config_parse_limit, &(context).rlimit[RLIMIT_RSS], section }, \ | |
1091 | { "LimitNOFILE", config_parse_limit, &(context).rlimit[RLIMIT_NOFILE], section }, \ | |
1092 | { "LimitAS", config_parse_limit, &(context).rlimit[RLIMIT_AS], section }, \ | |
1093 | { "LimitNPROC", config_parse_limit, &(context).rlimit[RLIMIT_NPROC], section }, \ | |
1094 | { "LimitMEMLOCK", config_parse_limit, &(context).rlimit[RLIMIT_MEMLOCK], section }, \ | |
1095 | { "LimitLOCKS", config_parse_limit, &(context).rlimit[RLIMIT_LOCKS], section }, \ | |
1096 | { "LimitSIGPENDING", config_parse_limit, &(context).rlimit[RLIMIT_SIGPENDING], section }, \ | |
1097 | { "LimitMSGQUEUE", config_parse_limit, &(context).rlimit[RLIMIT_MSGQUEUE], section }, \ | |
1098 | { "LimitNICE", config_parse_limit, &(context).rlimit[RLIMIT_NICE], section }, \ | |
1099 | { "LimitRTPRIO", config_parse_limit, &(context).rlimit[RLIMIT_RTPRIO], section }, \ | |
451a074f | 1100 | { "LimitRTTIME", config_parse_limit, &(context).rlimit[RLIMIT_RTTIME], section }, \ |
8e274523 LP |
1101 | { "NonBlocking", config_parse_bool, &(context).non_blocking, section }, \ |
1102 | { "ControlGroup", config_parse_cgroup, u, section } \ | |
034c6ed7 | 1103 | |
3efd4195 | 1104 | const ConfigItem items[] = { |
1c01f82b LP |
1105 | { "Names", config_parse_names, u, "Meta" }, |
1106 | { "Description", config_parse_string, &u->meta.description, "Meta" }, | |
1107 | { "Requires", config_parse_deps, UINT_TO_PTR(UNIT_REQUIRES), "Meta" }, | |
1108 | { "SoftRequires", config_parse_deps, UINT_TO_PTR(UNIT_SOFT_REQUIRES), "Meta" }, | |
1109 | { "Wants", config_parse_deps, UINT_TO_PTR(UNIT_WANTS), "Meta" }, | |
1110 | { "Requisite", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE), "Meta" }, | |
1111 | { "SoftRequisite", config_parse_deps, UINT_TO_PTR(UNIT_SOFT_REQUISITE), "Meta" }, | |
1112 | { "Conflicts", config_parse_deps, UINT_TO_PTR(UNIT_CONFLICTS), "Meta" }, | |
1113 | { "Before", config_parse_deps, UINT_TO_PTR(UNIT_BEFORE), "Meta" }, | |
1114 | { "After", config_parse_deps, UINT_TO_PTR(UNIT_AFTER), "Meta" }, | |
f3bff0eb LP |
1115 | { "RecursiveStop", config_parse_bool, &u->meta.recursive_stop, "Meta" }, |
1116 | { "StopWhenUnneeded", config_parse_bool, &u->meta.stop_when_unneeded, "Meta" }, | |
1c01f82b LP |
1117 | |
1118 | { "PIDFile", config_parse_path, &u->service.pid_file, "Service" }, | |
1119 | { "ExecStartPre", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START_PRE, "Service" }, | |
1120 | { "ExecStart", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START, "Service" }, | |
1121 | { "ExecStartPost", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START_POST, "Service" }, | |
1122 | { "ExecReload", config_parse_exec, u->service.exec_command+SERVICE_EXEC_RELOAD, "Service" }, | |
1123 | { "ExecStop", config_parse_exec, u->service.exec_command+SERVICE_EXEC_STOP, "Service" }, | |
1124 | { "ExecStopPost", config_parse_exec, u->service.exec_command+SERVICE_EXEC_STOP_POST, "Service" }, | |
1125 | { "RestartSec", config_parse_usec, &u->service.restart_usec, "Service" }, | |
1126 | { "TimeoutSec", config_parse_usec, &u->service.timeout_usec, "Service" }, | |
1127 | { "Type", config_parse_service_type, &u->service, "Service" }, | |
1128 | { "Restart", config_parse_service_restart, &u->service, "Service" }, | |
81a2b7ce LP |
1129 | { "PermissionsStartOnly", config_parse_bool, &u->service.permissions_start_only, "Service" }, |
1130 | { "RootDirectoryStartOnly", config_parse_bool, &u->service.root_directory_start_only, "Service" }, | |
8e274523 | 1131 | { "ValidNoProcess", config_parse_bool, &u->service.valid_no_process, "Service" }, |
87f0e418 LP |
1132 | EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"), |
1133 | ||
1c01f82b LP |
1134 | { "ListenStream", config_parse_listen, &u->socket, "Socket" }, |
1135 | { "ListenDatagram", config_parse_listen, &u->socket, "Socket" }, | |
1136 | { "ListenSequentialPacket", config_parse_listen, &u->socket, "Socket" }, | |
1137 | { "ListenFIFO", config_parse_listen, &u->socket, "Socket" }, | |
1138 | { "BindIPv6Only", config_parse_socket_bind, &u->socket, "Socket" }, | |
1139 | { "Backlog", config_parse_unsigned, &u->socket.backlog, "Socket" }, | |
acbb0225 | 1140 | { "BindToDevice", config_parse_bindtodevice, &u->socket, "Socket" }, |
1c01f82b LP |
1141 | { "ExecStartPre", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_START_PRE, "Socket" }, |
1142 | { "ExecStartPost", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_START_POST, "Socket" }, | |
1143 | { "ExecStopPre", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_STOP_PRE, "Socket" }, | |
1144 | { "ExecStopPost", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_STOP_POST, "Socket" }, | |
b5a0699f LP |
1145 | { "DirectoryMode", config_parse_mode, &u->socket.directory_mode, "Socket" }, |
1146 | { "SocketMode", config_parse_mode, &u->socket.socket_mode, "Socket" }, | |
87f0e418 LP |
1147 | EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"), |
1148 | ||
1149 | EXEC_CONTEXT_CONFIG_ITEMS(u->automount.exec_context, "Automount"), | |
034c6ed7 | 1150 | |
3efd4195 LP |
1151 | { NULL, NULL, NULL, NULL } |
1152 | }; | |
1153 | ||
034c6ed7 | 1154 | #undef EXEC_CONTEXT_CONFIG_ITEMS |
42f4e3c4 | 1155 | |
42f4e3c4 | 1156 | const char *sections[3]; |
0301abf4 LP |
1157 | char *k; |
1158 | int r; | |
87f0e418 | 1159 | Set *symlink_names; |
0301abf4 | 1160 | FILE *f; |
036643a2 | 1161 | char *filename = NULL, *id; |
3efd4195 | 1162 | |
42f4e3c4 | 1163 | sections[0] = "Meta"; |
87f0e418 | 1164 | sections[1] = section_table[u->meta.type]; |
42f4e3c4 LP |
1165 | sections[2] = NULL; |
1166 | ||
87f0e418 LP |
1167 | if (!(symlink_names = set_new(string_hash_func, string_compare_func))) |
1168 | return -ENOMEM; | |
3efd4195 | 1169 | |
036643a2 LP |
1170 | if (path_is_absolute(path)) { |
1171 | ||
1172 | if (!(filename = strdup(path))) { | |
1173 | r = -ENOMEM; | |
1174 | goto finish; | |
1175 | } | |
1176 | ||
1177 | if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) { | |
1178 | free(filename); | |
1179 | filename = NULL; | |
1180 | ||
1181 | if (r != -ENOENT) | |
1182 | goto finish; | |
1183 | } | |
1184 | ||
1185 | } else { | |
1186 | char **p; | |
1187 | ||
1188 | STRV_FOREACH(p, u->meta.manager->unit_path) { | |
1189 | ||
1190 | /* Instead of opening the path right away, we manually | |
1191 | * follow all symlinks and add their name to our unit | |
1192 | * name set while doing so */ | |
1193 | if (!(filename = path_make_absolute(path, *p))) { | |
1194 | r = -ENOMEM; | |
1195 | goto finish; | |
1196 | } | |
1197 | ||
1198 | if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) { | |
1199 | char *sn; | |
1200 | ||
1201 | free(filename); | |
1202 | filename = NULL; | |
1203 | ||
1204 | if (r != -ENOENT) | |
1205 | goto finish; | |
1206 | ||
1207 | /* Empty the symlink names for the next run */ | |
1208 | while ((sn = set_steal_first(symlink_names))) | |
1209 | free(sn); | |
3efd4195 | 1210 | |
036643a2 LP |
1211 | continue; |
1212 | } | |
1213 | ||
1214 | break; | |
1215 | } | |
1216 | } | |
034c6ed7 | 1217 | |
036643a2 LP |
1218 | if (!filename) { |
1219 | r = 0; /* returning 0 means: no suitable config file found */ | |
0301abf4 LP |
1220 | goto finish; |
1221 | } | |
87f0e418 | 1222 | |
0301abf4 LP |
1223 | /* Now, parse the file contents */ |
1224 | r = config_parse(filename, f, sections, items, u); | |
1225 | if (r < 0) | |
1226 | goto finish; | |
87f0e418 | 1227 | |
0301abf4 LP |
1228 | /* Let's try to add in all symlink names we found */ |
1229 | while ((k = set_steal_first(symlink_names))) { | |
1230 | if ((r = unit_add_name(u, k)) < 0) | |
87f0e418 LP |
1231 | goto finish; |
1232 | ||
87f0e418 | 1233 | |
f50e0a01 LP |
1234 | if (id == k) |
1235 | unit_choose_id(u, id); | |
0301abf4 | 1236 | free(k); |
034c6ed7 LP |
1237 | } |
1238 | ||
b08d03ff | 1239 | |
6be1e7d5 LP |
1240 | free(u->meta.fragment_path); |
1241 | u->meta.fragment_path = filename; | |
0301abf4 | 1242 | filename = NULL; |
87f0e418 | 1243 | |
0301abf4 | 1244 | r = 1; /* returning 1 means: suitable config file found and loaded */ |
87f0e418 LP |
1245 | |
1246 | finish: | |
1247 | while ((k = set_steal_first(symlink_names))) | |
1248 | free(k); | |
87f0e418 | 1249 | set_free(symlink_names); |
0301abf4 LP |
1250 | free(filename); |
1251 | ||
1252 | return r; | |
1253 | } | |
1254 | ||
1255 | int unit_load_fragment(Unit *u) { | |
d46de8a1 | 1256 | int r = 0; |
0301abf4 LP |
1257 | |
1258 | assert(u); | |
1259 | assert(u->meta.load_state == UNIT_STUB); | |
1260 | ||
6be1e7d5 LP |
1261 | if (u->meta.fragment_path) |
1262 | r = load_from_path(u, u->meta.fragment_path); | |
0301abf4 LP |
1263 | else { |
1264 | Iterator i; | |
890f434c | 1265 | const char *t; |
0301abf4 | 1266 | |
890f434c LP |
1267 | /* Try to find the unit under its id */ |
1268 | if ((t = unit_id(u))) | |
1269 | r = load_from_path(u, t); | |
1270 | ||
1271 | /* Try to find an alias we can load this with */ | |
1272 | if (r == 0) | |
1273 | SET_FOREACH(t, u->meta.names, i) | |
1274 | if ((r = load_from_path(u, t)) != 0) | |
1275 | break; | |
0301abf4 | 1276 | } |
87f0e418 | 1277 | |
890f434c LP |
1278 | if (r >= 0) { |
1279 | ExecContext *c; | |
071830ff | 1280 | |
890f434c LP |
1281 | if (u->meta.type == UNIT_SOCKET) |
1282 | c = &u->socket.exec_context; | |
1283 | else if (u->meta.type == UNIT_SERVICE) | |
1284 | c = &u->service.exec_context; | |
1285 | else | |
1286 | c = NULL; | |
d46de8a1 | 1287 | |
81a2b7ce | 1288 | if (c && |
c9dae904 | 1289 | (c->output == EXEC_OUTPUT_KERNEL || c->output == EXEC_OUTPUT_SYSLOG)) { |
890f434c | 1290 | int k; |
071830ff | 1291 | |
890f434c LP |
1292 | /* If syslog or kernel logging is requested, make sure |
1293 | * our own logging daemon is run first. */ | |
071830ff | 1294 | |
890f434c LP |
1295 | if ((k = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_LOGGER_SOCKET)) < 0) |
1296 | return k; | |
1297 | ||
c9dae904 LP |
1298 | if (u->meta.manager->running_as != MANAGER_SESSION) |
1299 | if ((k = unit_add_dependency_by_name(u, UNIT_REQUIRES, SPECIAL_LOGGER_SOCKET)) < 0) | |
1300 | return k; | |
890f434c | 1301 | } |
071830ff LP |
1302 | } |
1303 | ||
87f0e418 | 1304 | return r; |
3efd4195 | 1305 | } |