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