]>
Commit | Line | Data |
---|---|---|
d6c9574f | 1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
3efd4195 | 2 | |
a7334b09 LP |
3 | /*** |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2010 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
87f0e418 | 22 | #include <linux/oom.h> |
3efd4195 LP |
23 | #include <assert.h> |
24 | #include <errno.h> | |
25 | #include <string.h> | |
87f0e418 LP |
26 | #include <unistd.h> |
27 | #include <fcntl.h> | |
94f04347 LP |
28 | #include <sched.h> |
29 | #include <sys/prctl.h> | |
15ae422b | 30 | #include <sys/mount.h> |
25e870b5 | 31 | #include <linux/fs.h> |
45fb0699 | 32 | #include <sys/stat.h> |
3efd4195 | 33 | |
87f0e418 | 34 | #include "unit.h" |
3efd4195 LP |
35 | #include "strv.h" |
36 | #include "conf-parser.h" | |
37 | #include "load-fragment.h" | |
16354eff | 38 | #include "log.h" |
9eba9da4 | 39 | #include "ioprio.h" |
94f04347 LP |
40 | #include "securebits.h" |
41 | #include "missing.h" | |
9e2f7c11 | 42 | #include "unit-name.h" |
398ef8ba | 43 | #include "bus-errors.h" |
3efd4195 | 44 | |
ddb26e18 | 45 | #define COMMENTS "#;\n" |
ddb26e18 | 46 | |
42f4e3c4 | 47 | static int config_parse_deps( |
3efd4195 LP |
48 | const char *filename, |
49 | unsigned line, | |
50 | const char *section, | |
51 | const char *lvalue, | |
52 | const char *rvalue, | |
53 | void *data, | |
54 | void *userdata) { | |
55 | ||
87f0e418 LP |
56 | UnitDependency d = PTR_TO_UINT(data); |
57 | Unit *u = userdata; | |
3efd4195 LP |
58 | char *w; |
59 | size_t l; | |
60 | char *state; | |
61 | ||
62 | assert(filename); | |
63 | assert(lvalue); | |
64 | assert(rvalue); | |
3efd4195 | 65 | |
f60f22df | 66 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { |
9e2f7c11 | 67 | char *t, *k; |
3efd4195 | 68 | int r; |
3efd4195 LP |
69 | |
70 | if (!(t = strndup(w, l))) | |
71 | return -ENOMEM; | |
72 | ||
9e2f7c11 | 73 | k = unit_name_printf(u, t); |
3efd4195 LP |
74 | free(t); |
75 | ||
9e2f7c11 LP |
76 | if (!k) |
77 | return -ENOMEM; | |
78 | ||
701cc384 | 79 | r = unit_add_dependency_by_name(u, d, k, NULL, true); |
9e2f7c11 | 80 | |
c0b34696 LP |
81 | if (r < 0) { |
82 | log_error("Failed to add dependency on %s, ignoring: %s", k, strerror(-r)); | |
83 | free(k); | |
84 | return 0; | |
85 | } | |
86 | ||
87 | free(k); | |
3efd4195 LP |
88 | } |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
42f4e3c4 | 93 | static int config_parse_names( |
87d1515d LP |
94 | const char *filename, |
95 | unsigned line, | |
96 | const char *section, | |
97 | const char *lvalue, | |
98 | const char *rvalue, | |
99 | void *data, | |
100 | void *userdata) { | |
101 | ||
87f0e418 | 102 | Unit *u = userdata; |
87d1515d LP |
103 | char *w; |
104 | size_t l; | |
105 | char *state; | |
106 | ||
107 | assert(filename); | |
108 | assert(lvalue); | |
109 | assert(rvalue); | |
110 | assert(data); | |
111 | ||
f60f22df | 112 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { |
9e2f7c11 | 113 | char *t, *k; |
87d1515d | 114 | int r; |
87d1515d LP |
115 | |
116 | if (!(t = strndup(w, l))) | |
117 | return -ENOMEM; | |
118 | ||
9e2f7c11 | 119 | k = unit_name_printf(u, t); |
87d1515d | 120 | free(t); |
23a177ef | 121 | |
9e2f7c11 LP |
122 | if (!k) |
123 | return -ENOMEM; | |
124 | ||
125 | r = unit_merge_by_name(u, k); | |
9e2f7c11 | 126 | |
c0b34696 LP |
127 | if (r < 0) { |
128 | log_error("Failed to add name %s, ignoring: %s", k, strerror(-r)); | |
129 | free(k); | |
130 | return 0; | |
131 | } | |
132 | ||
133 | free(k); | |
87d1515d LP |
134 | } |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
f2d3769a | 139 | static int config_parse_string_printf( |
932921b5 LP |
140 | const char *filename, |
141 | unsigned line, | |
142 | const char *section, | |
143 | const char *lvalue, | |
144 | const char *rvalue, | |
145 | void *data, | |
146 | void *userdata) { | |
147 | ||
148 | Unit *u = userdata; | |
f2d3769a | 149 | char **s = data; |
932921b5 LP |
150 | char *k; |
151 | ||
152 | assert(filename); | |
153 | assert(lvalue); | |
154 | assert(rvalue); | |
f2d3769a LP |
155 | assert(s); |
156 | assert(u); | |
932921b5 LP |
157 | |
158 | if (!(k = unit_full_printf(u, rvalue))) | |
159 | return -ENOMEM; | |
160 | ||
f2d3769a | 161 | free(*s); |
932921b5 | 162 | if (*k) |
f2d3769a | 163 | *s = k; |
932921b5 LP |
164 | else { |
165 | free(k); | |
f2d3769a | 166 | *s = NULL; |
932921b5 LP |
167 | } |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
42f4e3c4 LP |
172 | static int config_parse_listen( |
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 | ||
16354eff | 181 | int r; |
542563ba LP |
182 | SocketPort *p; |
183 | Socket *s; | |
16354eff | 184 | |
42f4e3c4 LP |
185 | assert(filename); |
186 | assert(lvalue); | |
187 | assert(rvalue); | |
188 | assert(data); | |
189 | ||
542563ba LP |
190 | s = (Socket*) data; |
191 | ||
192 | if (!(p = new0(SocketPort, 1))) | |
193 | return -ENOMEM; | |
194 | ||
195 | if (streq(lvalue, "ListenFIFO")) { | |
196 | p->type = SOCKET_FIFO; | |
197 | ||
198 | if (!(p->path = strdup(rvalue))) { | |
199 | free(p); | |
200 | return -ENOMEM; | |
201 | } | |
01f78473 LP |
202 | |
203 | path_kill_slashes(p->path); | |
542563ba LP |
204 | } else { |
205 | p->type = SOCKET_SOCKET; | |
206 | ||
207 | if ((r = socket_address_parse(&p->address, rvalue)) < 0) { | |
c0b34696 | 208 | log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue); |
542563ba | 209 | free(p); |
c0b34696 | 210 | return 0; |
542563ba LP |
211 | } |
212 | ||
213 | if (streq(lvalue, "ListenStream")) | |
214 | p->address.type = SOCK_STREAM; | |
215 | else if (streq(lvalue, "ListenDatagram")) | |
216 | p->address.type = SOCK_DGRAM; | |
217 | else { | |
218 | assert(streq(lvalue, "ListenSequentialPacket")); | |
219 | p->address.type = SOCK_SEQPACKET; | |
220 | } | |
221 | ||
222 | if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) { | |
c0b34696 | 223 | log_error("[%s:%u] Address family not supported, ignoring: %s", filename, line, rvalue); |
542563ba | 224 | free(p); |
c0b34696 | 225 | return 0; |
542563ba | 226 | } |
16354eff LP |
227 | } |
228 | ||
542563ba | 229 | p->fd = -1; |
034c6ed7 | 230 | LIST_PREPEND(SocketPort, port, s->ports, p); |
542563ba | 231 | |
16354eff | 232 | return 0; |
42f4e3c4 LP |
233 | } |
234 | ||
034c6ed7 | 235 | static int config_parse_socket_bind( |
42f4e3c4 LP |
236 | const char *filename, |
237 | unsigned line, | |
238 | const char *section, | |
239 | const char *lvalue, | |
240 | const char *rvalue, | |
241 | void *data, | |
242 | void *userdata) { | |
243 | ||
542563ba | 244 | Socket *s; |
c0120d99 | 245 | SocketAddressBindIPv6Only b; |
42f4e3c4 LP |
246 | |
247 | assert(filename); | |
248 | assert(lvalue); | |
249 | assert(rvalue); | |
250 | assert(data); | |
251 | ||
542563ba LP |
252 | s = (Socket*) data; |
253 | ||
c0120d99 LP |
254 | if ((b = socket_address_bind_ipv6_only_from_string(rvalue)) < 0) { |
255 | int r; | |
256 | ||
257 | if ((r = parse_boolean(rvalue)) < 0) { | |
c0b34696 LP |
258 | log_error("[%s:%u] Failed to parse bind IPv6 only value, ignoring: %s", filename, line, rvalue); |
259 | return 0; | |
c0120d99 | 260 | } |
42f4e3c4 | 261 | |
c0120d99 LP |
262 | s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH; |
263 | } else | |
264 | s->bind_ipv6_only = b; | |
542563ba | 265 | |
42f4e3c4 LP |
266 | return 0; |
267 | } | |
268 | ||
034c6ed7 LP |
269 | static int config_parse_nice( |
270 | const char *filename, | |
271 | unsigned line, | |
272 | const char *section, | |
273 | const char *lvalue, | |
274 | const char *rvalue, | |
275 | void *data, | |
276 | void *userdata) { | |
277 | ||
fb33a393 LP |
278 | ExecContext *c = data; |
279 | int priority, r; | |
034c6ed7 LP |
280 | |
281 | assert(filename); | |
282 | assert(lvalue); | |
283 | assert(rvalue); | |
284 | assert(data); | |
285 | ||
286 | if ((r = safe_atoi(rvalue, &priority)) < 0) { | |
c0b34696 LP |
287 | log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue); |
288 | return 0; | |
034c6ed7 LP |
289 | } |
290 | ||
291 | if (priority < PRIO_MIN || priority >= PRIO_MAX) { | |
c0b34696 LP |
292 | log_error("[%s:%u] Nice priority out of range, ignoring: %s", filename, line, rvalue); |
293 | return 0; | |
034c6ed7 LP |
294 | } |
295 | ||
fb33a393 LP |
296 | c->nice = priority; |
297 | c->nice_set = false; | |
298 | ||
034c6ed7 LP |
299 | return 0; |
300 | } | |
301 | ||
dd6c17b1 | 302 | static int config_parse_oom_score_adjust( |
034c6ed7 LP |
303 | const char *filename, |
304 | unsigned line, | |
305 | const char *section, | |
306 | const char *lvalue, | |
307 | const char *rvalue, | |
308 | void *data, | |
309 | void *userdata) { | |
310 | ||
fb33a393 LP |
311 | ExecContext *c = data; |
312 | int oa, r; | |
034c6ed7 LP |
313 | |
314 | assert(filename); | |
315 | assert(lvalue); | |
316 | assert(rvalue); | |
317 | assert(data); | |
318 | ||
319 | if ((r = safe_atoi(rvalue, &oa)) < 0) { | |
dd6c17b1 | 320 | log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue); |
c0b34696 | 321 | return 0; |
034c6ed7 LP |
322 | } |
323 | ||
dd6c17b1 LP |
324 | if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) { |
325 | log_error("[%s:%u] OOM score adjust value out of range, ignoring: %s", filename, line, rvalue); | |
c0b34696 | 326 | return 0; |
034c6ed7 LP |
327 | } |
328 | ||
dd6c17b1 LP |
329 | c->oom_score_adjust = oa; |
330 | c->oom_score_adjust_set = true; | |
fb33a393 | 331 | |
034c6ed7 LP |
332 | return 0; |
333 | } | |
334 | ||
b5a0699f | 335 | static int config_parse_mode( |
034c6ed7 LP |
336 | const char *filename, |
337 | unsigned line, | |
338 | const char *section, | |
339 | const char *lvalue, | |
340 | const char *rvalue, | |
341 | void *data, | |
342 | void *userdata) { | |
343 | ||
344 | mode_t *m = data; | |
345 | long l; | |
346 | char *x = NULL; | |
347 | ||
348 | assert(filename); | |
349 | assert(lvalue); | |
350 | assert(rvalue); | |
351 | assert(data); | |
352 | ||
353 | errno = 0; | |
354 | l = strtol(rvalue, &x, 8); | |
355 | if (!x || *x || errno) { | |
c0b34696 LP |
356 | log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue); |
357 | return 0; | |
034c6ed7 LP |
358 | } |
359 | ||
b5a0699f | 360 | if (l < 0000 || l > 07777) { |
c0b34696 LP |
361 | log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue); |
362 | return 0; | |
034c6ed7 LP |
363 | } |
364 | ||
365 | *m = (mode_t) l; | |
366 | return 0; | |
367 | } | |
368 | ||
369 | static int config_parse_exec( | |
370 | const char *filename, | |
371 | unsigned line, | |
372 | const char *section, | |
373 | const char *lvalue, | |
374 | const char *rvalue, | |
375 | void *data, | |
376 | void *userdata) { | |
377 | ||
61e5d8ed LP |
378 | ExecCommand **e = data, *nce; |
379 | char *path, **n; | |
034c6ed7 | 380 | unsigned k; |
034c6ed7 LP |
381 | |
382 | assert(filename); | |
383 | assert(lvalue); | |
384 | assert(rvalue); | |
61e5d8ed | 385 | assert(e); |
034c6ed7 | 386 | |
6c666e26 LP |
387 | /* We accept an absolute path as first argument, or |
388 | * alternatively an absolute prefixed with @ to allow | |
389 | * overriding of argv[0]. */ | |
390 | ||
61e5d8ed LP |
391 | for (;;) { |
392 | char *w; | |
393 | size_t l; | |
394 | char *state; | |
b708e7ce | 395 | bool honour_argv0 = false, ignore = false; |
6c666e26 | 396 | |
61e5d8ed LP |
397 | path = NULL; |
398 | nce = NULL; | |
399 | n = NULL; | |
6c666e26 | 400 | |
61e5d8ed | 401 | rvalue += strspn(rvalue, WHITESPACE); |
034c6ed7 | 402 | |
61e5d8ed LP |
403 | if (rvalue[0] == 0) |
404 | break; | |
034c6ed7 | 405 | |
b708e7ce LP |
406 | if (rvalue[0] == '-') { |
407 | ignore = true; | |
408 | rvalue ++; | |
409 | } | |
410 | ||
411 | if (rvalue[0] == '@') { | |
412 | honour_argv0 = true; | |
413 | rvalue ++; | |
414 | } | |
61e5d8ed | 415 | |
b708e7ce | 416 | if (*rvalue != '/') { |
c0b34696 LP |
417 | log_error("[%s:%u] Invalid executable path in command line, ignoring: %s", filename, line, rvalue); |
418 | return 0; | |
6c666e26 | 419 | } |
034c6ed7 | 420 | |
61e5d8ed LP |
421 | k = 0; |
422 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { | |
423 | if (strncmp(w, ";", l) == 0) | |
424 | break; | |
034c6ed7 | 425 | |
61e5d8ed LP |
426 | k++; |
427 | } | |
034c6ed7 | 428 | |
b708e7ce | 429 | if (!(n = new(char*, k + !honour_argv0))) |
61e5d8ed LP |
430 | return -ENOMEM; |
431 | ||
432 | k = 0; | |
61e5d8ed LP |
433 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { |
434 | if (strncmp(w, ";", l) == 0) | |
435 | break; | |
436 | ||
b708e7ce LP |
437 | if (honour_argv0 && w == rvalue) { |
438 | assert(!path); | |
439 | if (!(path = cunescape_length(w, l))) | |
61e5d8ed | 440 | goto fail; |
61e5d8ed LP |
441 | } else { |
442 | if (!(n[k++] = cunescape_length(w, l))) | |
443 | goto fail; | |
444 | } | |
445 | } | |
446 | ||
447 | n[k] = NULL; | |
448 | ||
449 | if (!n[0]) { | |
c0b34696 | 450 | log_error("[%s:%u] Invalid command line, ignoring: %s", filename, line, rvalue); |
61e5d8ed | 451 | strv_free(n); |
c0b34696 | 452 | return 0; |
61e5d8ed LP |
453 | } |
454 | ||
455 | if (!path) | |
456 | if (!(path = strdup(n[0]))) | |
457 | goto fail; | |
6c666e26 | 458 | |
61e5d8ed | 459 | assert(path_is_absolute(path)); |
6c666e26 | 460 | |
61e5d8ed LP |
461 | if (!(nce = new0(ExecCommand, 1))) |
462 | goto fail; | |
463 | ||
464 | nce->argv = n; | |
465 | nce->path = path; | |
b708e7ce | 466 | nce->ignore = ignore; |
034c6ed7 | 467 | |
61e5d8ed | 468 | path_kill_slashes(nce->path); |
034c6ed7 | 469 | |
61e5d8ed | 470 | exec_command_append_list(e, nce); |
01f78473 | 471 | |
61e5d8ed LP |
472 | rvalue = state; |
473 | } | |
034c6ed7 LP |
474 | |
475 | return 0; | |
476 | ||
477 | fail: | |
6c666e26 LP |
478 | n[k] = NULL; |
479 | strv_free(n); | |
480 | free(path); | |
034c6ed7 LP |
481 | free(nce); |
482 | ||
483 | return -ENOMEM; | |
484 | } | |
485 | ||
486 | static int config_parse_usec( | |
487 | const char *filename, | |
488 | unsigned line, | |
489 | const char *section, | |
490 | const char *lvalue, | |
491 | const char *rvalue, | |
492 | void *data, | |
493 | void *userdata) { | |
494 | ||
495 | usec_t *usec = data; | |
034c6ed7 LP |
496 | int r; |
497 | ||
498 | assert(filename); | |
499 | assert(lvalue); | |
500 | assert(rvalue); | |
501 | assert(data); | |
502 | ||
24a6e4a4 | 503 | if ((r = parse_usec(rvalue, usec)) < 0) { |
c0b34696 LP |
504 | log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue); |
505 | return 0; | |
034c6ed7 LP |
506 | } |
507 | ||
034c6ed7 LP |
508 | return 0; |
509 | } | |
510 | ||
487393e9 LP |
511 | static DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); |
512 | static DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); | |
034c6ed7 | 513 | |
47be870b | 514 | static int config_parse_bindtodevice( |
acbb0225 LP |
515 | const char *filename, |
516 | unsigned line, | |
517 | const char *section, | |
518 | const char *lvalue, | |
519 | const char *rvalue, | |
520 | void *data, | |
521 | void *userdata) { | |
522 | ||
523 | Socket *s = data; | |
524 | char *n; | |
525 | ||
526 | assert(filename); | |
527 | assert(lvalue); | |
528 | assert(rvalue); | |
529 | assert(data); | |
530 | ||
531 | if (rvalue[0] && !streq(rvalue, "*")) { | |
532 | if (!(n = strdup(rvalue))) | |
533 | return -ENOMEM; | |
534 | } else | |
535 | n = NULL; | |
536 | ||
537 | free(s->bind_to_device); | |
538 | s->bind_to_device = n; | |
539 | ||
540 | return 0; | |
541 | } | |
542 | ||
487393e9 LP |
543 | static DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier"); |
544 | static DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier"); | |
87f0e418 | 545 | |
47be870b | 546 | static int config_parse_facility( |
071830ff LP |
547 | const char *filename, |
548 | unsigned line, | |
549 | const char *section, | |
550 | const char *lvalue, | |
551 | const char *rvalue, | |
552 | void *data, | |
553 | void *userdata) { | |
554 | ||
071830ff | 555 | |
94f04347 | 556 | int *o = data, x; |
071830ff LP |
557 | |
558 | assert(filename); | |
559 | assert(lvalue); | |
560 | assert(rvalue); | |
561 | assert(data); | |
562 | ||
0d87eb42 | 563 | if ((x = log_facility_from_string(rvalue)) < 0) { |
c0b34696 LP |
564 | log_error("[%s:%u] Failed to parse log facility, ignoring: %s", filename, line, rvalue); |
565 | return 0; | |
0d87eb42 | 566 | } |
94f04347 LP |
567 | |
568 | *o = LOG_MAKEPRI(x, LOG_PRI(*o)); | |
071830ff LP |
569 | |
570 | return 0; | |
571 | } | |
572 | ||
47be870b | 573 | static int config_parse_level( |
071830ff LP |
574 | const char *filename, |
575 | unsigned line, | |
576 | const char *section, | |
577 | const char *lvalue, | |
578 | const char *rvalue, | |
579 | void *data, | |
580 | void *userdata) { | |
581 | ||
071830ff | 582 | |
94f04347 | 583 | int *o = data, x; |
071830ff LP |
584 | |
585 | assert(filename); | |
586 | assert(lvalue); | |
587 | assert(rvalue); | |
588 | assert(data); | |
589 | ||
0d87eb42 | 590 | if ((x = log_level_from_string(rvalue)) < 0) { |
c0b34696 LP |
591 | log_error("[%s:%u] Failed to parse log level, ignoring: %s", filename, line, rvalue); |
592 | return 0; | |
0d87eb42 | 593 | } |
071830ff | 594 | |
94f04347 LP |
595 | *o = LOG_MAKEPRI(LOG_FAC(*o), x); |
596 | return 0; | |
597 | } | |
598 | ||
47be870b | 599 | static int config_parse_io_class( |
94f04347 LP |
600 | const char *filename, |
601 | unsigned line, | |
602 | const char *section, | |
603 | const char *lvalue, | |
604 | const char *rvalue, | |
605 | void *data, | |
606 | void *userdata) { | |
607 | ||
608 | ExecContext *c = data; | |
609 | int x; | |
610 | ||
611 | assert(filename); | |
612 | assert(lvalue); | |
613 | assert(rvalue); | |
614 | assert(data); | |
615 | ||
0d87eb42 | 616 | if ((x = ioprio_class_from_string(rvalue)) < 0) { |
c0b34696 LP |
617 | log_error("[%s:%u] Failed to parse IO scheduling class, ignoring: %s", filename, line, rvalue); |
618 | return 0; | |
0d87eb42 | 619 | } |
94f04347 LP |
620 | |
621 | c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio)); | |
622 | c->ioprio_set = true; | |
623 | ||
624 | return 0; | |
625 | } | |
626 | ||
47be870b | 627 | static int config_parse_io_priority( |
94f04347 LP |
628 | const char *filename, |
629 | unsigned line, | |
630 | const char *section, | |
631 | const char *lvalue, | |
632 | const char *rvalue, | |
633 | void *data, | |
634 | void *userdata) { | |
635 | ||
636 | ExecContext *c = data; | |
637 | int i; | |
638 | ||
639 | assert(filename); | |
640 | assert(lvalue); | |
641 | assert(rvalue); | |
642 | assert(data); | |
643 | ||
644 | if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) { | |
c0b34696 LP |
645 | log_error("[%s:%u] Failed to parse io priority, ignoring: %s", filename, line, rvalue); |
646 | return 0; | |
071830ff LP |
647 | } |
648 | ||
94f04347 LP |
649 | c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i); |
650 | c->ioprio_set = true; | |
651 | ||
071830ff LP |
652 | return 0; |
653 | } | |
654 | ||
47be870b | 655 | static int config_parse_cpu_sched_policy( |
9eba9da4 LP |
656 | const char *filename, |
657 | unsigned line, | |
658 | const char *section, | |
659 | const char *lvalue, | |
660 | const char *rvalue, | |
661 | void *data, | |
662 | void *userdata) { | |
663 | ||
94f04347 LP |
664 | |
665 | ExecContext *c = data; | |
666 | int x; | |
667 | ||
668 | assert(filename); | |
669 | assert(lvalue); | |
670 | assert(rvalue); | |
671 | assert(data); | |
672 | ||
0d87eb42 | 673 | if ((x = sched_policy_from_string(rvalue)) < 0) { |
c0b34696 LP |
674 | log_error("[%s:%u] Failed to parse CPU scheduling policy, ignoring: %s", filename, line, rvalue); |
675 | return 0; | |
0d87eb42 | 676 | } |
94f04347 LP |
677 | |
678 | c->cpu_sched_policy = x; | |
679 | c->cpu_sched_set = true; | |
680 | ||
681 | return 0; | |
682 | } | |
683 | ||
47be870b | 684 | static int config_parse_cpu_sched_prio( |
94f04347 LP |
685 | const char *filename, |
686 | unsigned line, | |
687 | const char *section, | |
688 | const char *lvalue, | |
689 | const char *rvalue, | |
690 | void *data, | |
691 | void *userdata) { | |
9eba9da4 LP |
692 | |
693 | ExecContext *c = data; | |
694 | int i; | |
695 | ||
696 | assert(filename); | |
697 | assert(lvalue); | |
698 | assert(rvalue); | |
699 | assert(data); | |
700 | ||
94f04347 LP |
701 | /* On Linux RR/FIFO have the same range */ |
702 | if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) { | |
c0b34696 LP |
703 | log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, rvalue); |
704 | return 0; | |
94f04347 | 705 | } |
9eba9da4 | 706 | |
94f04347 LP |
707 | c->cpu_sched_priority = i; |
708 | c->cpu_sched_set = true; | |
709 | ||
710 | return 0; | |
711 | } | |
712 | ||
47be870b | 713 | static int config_parse_cpu_affinity( |
94f04347 LP |
714 | const char *filename, |
715 | unsigned line, | |
716 | const char *section, | |
717 | const char *lvalue, | |
718 | const char *rvalue, | |
719 | void *data, | |
720 | void *userdata) { | |
721 | ||
722 | ExecContext *c = data; | |
723 | char *w; | |
724 | size_t l; | |
725 | char *state; | |
726 | ||
727 | assert(filename); | |
728 | assert(lvalue); | |
729 | assert(rvalue); | |
730 | assert(data); | |
731 | ||
f60f22df | 732 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { |
94f04347 LP |
733 | char *t; |
734 | int r; | |
735 | unsigned cpu; | |
736 | ||
737 | if (!(t = strndup(w, l))) | |
738 | return -ENOMEM; | |
739 | ||
487393e9 LP |
740 | r = safe_atou(t, &cpu); |
741 | free(t); | |
742 | ||
82c121a4 LP |
743 | if (!(c->cpuset)) |
744 | if (!(c->cpuset = cpu_set_malloc(&c->cpuset_ncpus))) | |
745 | return -ENOMEM; | |
746 | ||
82c121a4 | 747 | if (r < 0 || cpu >= c->cpuset_ncpus) { |
c0b34696 LP |
748 | log_error("[%s:%u] Failed to parse CPU affinity, ignoring: %s", filename, line, rvalue); |
749 | return 0; | |
9eba9da4 | 750 | } |
94f04347 | 751 | |
82c121a4 | 752 | CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset); |
9eba9da4 LP |
753 | } |
754 | ||
94f04347 LP |
755 | return 0; |
756 | } | |
757 | ||
47be870b | 758 | static int config_parse_capabilities( |
94f04347 LP |
759 | const char *filename, |
760 | unsigned line, | |
761 | const char *section, | |
762 | const char *lvalue, | |
763 | const char *rvalue, | |
764 | void *data, | |
765 | void *userdata) { | |
766 | ||
767 | ExecContext *c = data; | |
768 | cap_t cap; | |
769 | ||
770 | assert(filename); | |
771 | assert(lvalue); | |
772 | assert(rvalue); | |
773 | assert(data); | |
774 | ||
775 | if (!(cap = cap_from_text(rvalue))) { | |
776 | if (errno == ENOMEM) | |
777 | return -ENOMEM; | |
778 | ||
c0b34696 LP |
779 | log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue); |
780 | return 0; | |
94f04347 LP |
781 | } |
782 | ||
783 | if (c->capabilities) | |
784 | cap_free(c->capabilities); | |
785 | c->capabilities = cap; | |
786 | ||
787 | return 0; | |
788 | } | |
789 | ||
47be870b | 790 | static int config_parse_secure_bits( |
94f04347 LP |
791 | const char *filename, |
792 | unsigned line, | |
793 | const char *section, | |
794 | const char *lvalue, | |
795 | const char *rvalue, | |
796 | void *data, | |
797 | void *userdata) { | |
798 | ||
799 | ExecContext *c = data; | |
800 | char *w; | |
801 | size_t l; | |
802 | char *state; | |
803 | ||
804 | assert(filename); | |
805 | assert(lvalue); | |
806 | assert(rvalue); | |
807 | assert(data); | |
808 | ||
f60f22df | 809 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { |
94f04347 LP |
810 | if (first_word(w, "keep-caps")) |
811 | c->secure_bits |= SECURE_KEEP_CAPS; | |
812 | else if (first_word(w, "keep-caps-locked")) | |
813 | c->secure_bits |= SECURE_KEEP_CAPS_LOCKED; | |
814 | else if (first_word(w, "no-setuid-fixup")) | |
815 | c->secure_bits |= SECURE_NO_SETUID_FIXUP; | |
816 | else if (first_word(w, "no-setuid-fixup-locked")) | |
817 | c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED; | |
818 | else if (first_word(w, "noroot")) | |
819 | c->secure_bits |= SECURE_NOROOT; | |
820 | else if (first_word(w, "noroot-locked")) | |
821 | c->secure_bits |= SECURE_NOROOT_LOCKED; | |
9eba9da4 | 822 | else { |
c0b34696 LP |
823 | log_error("[%s:%u] Failed to parse secure bits, ignoring: %s", filename, line, rvalue); |
824 | return 0; | |
9eba9da4 LP |
825 | } |
826 | } | |
827 | ||
94f04347 LP |
828 | return 0; |
829 | } | |
830 | ||
47be870b | 831 | static int config_parse_bounding_set( |
94f04347 LP |
832 | const char *filename, |
833 | unsigned line, | |
834 | const char *section, | |
835 | const char *lvalue, | |
836 | const char *rvalue, | |
837 | void *data, | |
838 | void *userdata) { | |
839 | ||
840 | ExecContext *c = data; | |
841 | char *w; | |
842 | size_t l; | |
843 | char *state; | |
844 | ||
845 | assert(filename); | |
846 | assert(lvalue); | |
847 | assert(rvalue); | |
848 | assert(data); | |
849 | ||
f60f22df | 850 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { |
94f04347 LP |
851 | char *t; |
852 | int r; | |
853 | cap_value_t cap; | |
854 | ||
855 | if (!(t = strndup(w, l))) | |
856 | return -ENOMEM; | |
857 | ||
858 | r = cap_from_name(t, &cap); | |
859 | free(t); | |
860 | ||
861 | if (r < 0) { | |
c0b34696 LP |
862 | log_error("[%s:%u] Failed to parse capability bounding set, ignoring: %s", filename, line, rvalue); |
863 | return 0; | |
94f04347 LP |
864 | } |
865 | ||
866 | c->capability_bounding_set_drop |= 1 << cap; | |
867 | } | |
9eba9da4 LP |
868 | |
869 | return 0; | |
870 | } | |
871 | ||
03fae018 | 872 | static int config_parse_timer_slack_nsec( |
9eba9da4 LP |
873 | const char *filename, |
874 | unsigned line, | |
875 | const char *section, | |
876 | const char *lvalue, | |
877 | const char *rvalue, | |
878 | void *data, | |
879 | void *userdata) { | |
880 | ||
881 | ExecContext *c = data; | |
94f04347 LP |
882 | unsigned long u; |
883 | int r; | |
9eba9da4 LP |
884 | |
885 | assert(filename); | |
886 | assert(lvalue); | |
887 | assert(rvalue); | |
888 | assert(data); | |
889 | ||
94f04347 | 890 | if ((r = safe_atolu(rvalue, &u)) < 0) { |
c0b34696 LP |
891 | log_error("[%s:%u] Failed to parse time slack value, ignoring: %s", filename, line, rvalue); |
892 | return 0; | |
9eba9da4 LP |
893 | } |
894 | ||
03fae018 | 895 | c->timer_slack_nsec = u; |
94f04347 LP |
896 | |
897 | return 0; | |
898 | } | |
899 | ||
900 | static int config_parse_limit( | |
901 | const char *filename, | |
902 | unsigned line, | |
903 | const char *section, | |
904 | const char *lvalue, | |
905 | const char *rvalue, | |
906 | void *data, | |
907 | void *userdata) { | |
908 | ||
909 | struct rlimit **rl = data; | |
910 | unsigned long long u; | |
911 | int r; | |
912 | ||
913 | assert(filename); | |
914 | assert(lvalue); | |
915 | assert(rvalue); | |
916 | assert(data); | |
917 | ||
918 | if ((r = safe_atollu(rvalue, &u)) < 0) { | |
c0b34696 LP |
919 | log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue); |
920 | return 0; | |
94f04347 LP |
921 | } |
922 | ||
923 | if (!*rl) | |
924 | if (!(*rl = new(struct rlimit, 1))) | |
925 | return -ENOMEM; | |
9eba9da4 | 926 | |
94f04347 | 927 | (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u; |
9eba9da4 LP |
928 | return 0; |
929 | } | |
930 | ||
8e274523 LP |
931 | static int config_parse_cgroup( |
932 | const char *filename, | |
933 | unsigned line, | |
934 | const char *section, | |
935 | const char *lvalue, | |
936 | const char *rvalue, | |
937 | void *data, | |
938 | void *userdata) { | |
939 | ||
940 | Unit *u = userdata; | |
941 | char *w; | |
942 | size_t l; | |
943 | char *state; | |
944 | ||
f60f22df | 945 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { |
8e274523 LP |
946 | char *t; |
947 | int r; | |
948 | ||
f60f22df | 949 | if (!(t = cunescape_length(w, l))) |
8e274523 LP |
950 | return -ENOMEM; |
951 | ||
952 | r = unit_add_cgroup_from_text(u, t); | |
953 | free(t); | |
954 | ||
c0b34696 LP |
955 | if (r < 0) { |
956 | log_error("[%s:%u] Failed to parse cgroup value, ignoring: %s", filename, line, rvalue); | |
957 | return 0; | |
958 | } | |
8e274523 LP |
959 | } |
960 | ||
961 | return 0; | |
962 | } | |
963 | ||
a9a1e00a LP |
964 | static int config_parse_sysv_priority( |
965 | const char *filename, | |
966 | unsigned line, | |
967 | const char *section, | |
968 | const char *lvalue, | |
969 | const char *rvalue, | |
970 | void *data, | |
971 | void *userdata) { | |
972 | ||
973 | int *priority = data; | |
974 | int r, i; | |
975 | ||
976 | assert(filename); | |
977 | assert(lvalue); | |
978 | assert(rvalue); | |
979 | assert(data); | |
980 | ||
981 | if ((r = safe_atoi(rvalue, &i)) < 0 || i < 0) { | |
c0b34696 LP |
982 | log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue); |
983 | return 0; | |
a9a1e00a LP |
984 | } |
985 | ||
986 | *priority = (int) i; | |
987 | return 0; | |
988 | } | |
989 | ||
487393e9 | 990 | static DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); |
50159e6a | 991 | |
2e22afe9 LP |
992 | static int config_parse_kill_signal( |
993 | const char *filename, | |
994 | unsigned line, | |
995 | const char *section, | |
996 | const char *lvalue, | |
997 | const char *rvalue, | |
998 | void *data, | |
999 | void *userdata) { | |
1000 | ||
1001 | int *sig = data; | |
1002 | int r; | |
1003 | ||
1004 | assert(filename); | |
1005 | assert(lvalue); | |
1006 | assert(rvalue); | |
1007 | assert(sig); | |
1008 | ||
1009 | if ((r = signal_from_string(rvalue)) <= 0) | |
1010 | if (startswith(rvalue, "SIG")) | |
1011 | r = signal_from_string(rvalue+3); | |
1012 | ||
1013 | if (r <= 0) { | |
c0b34696 LP |
1014 | log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue); |
1015 | return 0; | |
2e22afe9 LP |
1016 | } |
1017 | ||
1018 | *sig = r; | |
1019 | return 0; | |
1020 | } | |
1021 | ||
15ae422b LP |
1022 | static int config_parse_mount_flags( |
1023 | const char *filename, | |
1024 | unsigned line, | |
1025 | const char *section, | |
1026 | const char *lvalue, | |
1027 | const char *rvalue, | |
1028 | void *data, | |
1029 | void *userdata) { | |
1030 | ||
1031 | ExecContext *c = data; | |
1032 | char *w; | |
1033 | size_t l; | |
1034 | char *state; | |
1035 | unsigned long flags = 0; | |
1036 | ||
1037 | assert(filename); | |
1038 | assert(lvalue); | |
1039 | assert(rvalue); | |
1040 | assert(data); | |
1041 | ||
f60f22df | 1042 | FOREACH_WORD_QUOTED(w, l, rvalue, state) { |
15ae422b LP |
1043 | if (strncmp(w, "shared", l) == 0) |
1044 | flags |= MS_SHARED; | |
1045 | else if (strncmp(w, "slave", l) == 0) | |
1046 | flags |= MS_SLAVE; | |
1047 | else if (strncmp(w, "private", l) == 0) | |
1048 | flags |= MS_PRIVATE; | |
1049 | else { | |
c0b34696 LP |
1050 | log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue); |
1051 | return 0; | |
15ae422b LP |
1052 | } |
1053 | } | |
1054 | ||
1055 | c->mount_flags = flags; | |
1056 | return 0; | |
1057 | } | |
1058 | ||
871d7de4 LP |
1059 | static int config_parse_timer( |
1060 | const char *filename, | |
1061 | unsigned line, | |
1062 | const char *section, | |
1063 | const char *lvalue, | |
1064 | const char *rvalue, | |
1065 | void *data, | |
1066 | void *userdata) { | |
1067 | ||
1068 | Timer *t = data; | |
1069 | usec_t u; | |
1070 | int r; | |
1071 | TimerValue *v; | |
1072 | TimerBase b; | |
1073 | ||
1074 | assert(filename); | |
1075 | assert(lvalue); | |
1076 | assert(rvalue); | |
1077 | assert(data); | |
1078 | ||
1079 | if ((b = timer_base_from_string(lvalue)) < 0) { | |
c0b34696 LP |
1080 | log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue); |
1081 | return 0; | |
871d7de4 LP |
1082 | } |
1083 | ||
1084 | if ((r = parse_usec(rvalue, &u)) < 0) { | |
c0b34696 LP |
1085 | log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue); |
1086 | return 0; | |
871d7de4 LP |
1087 | } |
1088 | ||
1089 | if (!(v = new0(TimerValue, 1))) | |
1090 | return -ENOMEM; | |
1091 | ||
1092 | v->base = b; | |
1093 | v->value = u; | |
1094 | ||
1095 | LIST_PREPEND(TimerValue, value, t->values, v); | |
1096 | ||
1097 | return 0; | |
1098 | } | |
1099 | ||
1100 | static int config_parse_timer_unit( | |
1101 | const char *filename, | |
1102 | unsigned line, | |
1103 | const char *section, | |
1104 | const char *lvalue, | |
1105 | const char *rvalue, | |
1106 | void *data, | |
1107 | void *userdata) { | |
1108 | ||
1109 | Timer *t = data; | |
1110 | int r; | |
398ef8ba LP |
1111 | DBusError error; |
1112 | ||
1113 | assert(filename); | |
1114 | assert(lvalue); | |
1115 | assert(rvalue); | |
1116 | assert(data); | |
1117 | ||
1118 | dbus_error_init(&error); | |
871d7de4 LP |
1119 | |
1120 | if (endswith(rvalue, ".timer")) { | |
c0b34696 LP |
1121 | log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue); |
1122 | return 0; | |
871d7de4 LP |
1123 | } |
1124 | ||
398ef8ba | 1125 | if ((r = manager_load_unit(t->meta.manager, rvalue, NULL, NULL, &t->unit)) < 0) { |
c0b34696 | 1126 | log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); |
398ef8ba | 1127 | dbus_error_free(&error); |
c0b34696 | 1128 | return 0; |
871d7de4 LP |
1129 | } |
1130 | ||
1131 | return 0; | |
1132 | } | |
1133 | ||
01f78473 LP |
1134 | static int config_parse_path_spec( |
1135 | const char *filename, | |
1136 | unsigned line, | |
1137 | const char *section, | |
1138 | const char *lvalue, | |
1139 | const char *rvalue, | |
1140 | void *data, | |
1141 | void *userdata) { | |
1142 | ||
1143 | Path *p = data; | |
1144 | PathSpec *s; | |
1145 | PathType b; | |
1146 | ||
1147 | assert(filename); | |
1148 | assert(lvalue); | |
1149 | assert(rvalue); | |
1150 | assert(data); | |
1151 | ||
1152 | if ((b = path_type_from_string(lvalue)) < 0) { | |
c0b34696 LP |
1153 | log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue); |
1154 | return 0; | |
01f78473 LP |
1155 | } |
1156 | ||
1157 | if (!path_is_absolute(rvalue)) { | |
c0b34696 LP |
1158 | log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, rvalue); |
1159 | return 0; | |
01f78473 LP |
1160 | } |
1161 | ||
1162 | if (!(s = new0(PathSpec, 1))) | |
1163 | return -ENOMEM; | |
1164 | ||
1165 | if (!(s->path = strdup(rvalue))) { | |
1166 | free(s); | |
1167 | return -ENOMEM; | |
1168 | } | |
1169 | ||
1170 | path_kill_slashes(s->path); | |
1171 | ||
1172 | s->type = b; | |
1173 | s->inotify_fd = -1; | |
1174 | ||
1175 | LIST_PREPEND(PathSpec, spec, p->specs, s); | |
1176 | ||
1177 | return 0; | |
1178 | } | |
1179 | ||
1180 | static int config_parse_path_unit( | |
1181 | const char *filename, | |
1182 | unsigned line, | |
1183 | const char *section, | |
1184 | const char *lvalue, | |
1185 | const char *rvalue, | |
1186 | void *data, | |
1187 | void *userdata) { | |
1188 | ||
1189 | Path *t = data; | |
1190 | int r; | |
398ef8ba LP |
1191 | DBusError error; |
1192 | ||
1193 | assert(filename); | |
1194 | assert(lvalue); | |
1195 | assert(rvalue); | |
1196 | assert(data); | |
1197 | ||
1198 | dbus_error_init(&error); | |
01f78473 LP |
1199 | |
1200 | if (endswith(rvalue, ".path")) { | |
c0b34696 LP |
1201 | log_error("[%s:%u] Unit cannot be of type path, ignoring: %s", filename, line, rvalue); |
1202 | return 0; | |
01f78473 LP |
1203 | } |
1204 | ||
398ef8ba | 1205 | if ((r = manager_load_unit(t->meta.manager, rvalue, NULL, &error, &t->unit)) < 0) { |
c0b34696 | 1206 | log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r)); |
398ef8ba | 1207 | dbus_error_free(&error); |
c0b34696 | 1208 | return 0; |
01f78473 LP |
1209 | } |
1210 | ||
1211 | return 0; | |
1212 | } | |
1213 | ||
ddb26e18 LP |
1214 | static int config_parse_env_file( |
1215 | const char *filename, | |
1216 | unsigned line, | |
1217 | const char *section, | |
1218 | const char *lvalue, | |
1219 | const char *rvalue, | |
1220 | void *data, | |
1221 | void *userdata) { | |
1222 | ||
1223 | FILE *f; | |
1224 | int r; | |
1225 | char ***env = data; | |
1226 | ||
1227 | assert(filename); | |
1228 | assert(lvalue); | |
1229 | assert(rvalue); | |
1230 | assert(data); | |
1231 | ||
1232 | if (!(f = fopen(rvalue, "re"))) { | |
c0b34696 LP |
1233 | log_error("[%s:%u] Failed to open environment file '%s', ignoring: %m", filename, line, rvalue); |
1234 | return 0; | |
ddb26e18 LP |
1235 | } |
1236 | ||
1237 | while (!feof(f)) { | |
1238 | char l[LINE_MAX], *p; | |
1239 | char **t; | |
1240 | ||
1241 | if (!fgets(l, sizeof(l), f)) { | |
1242 | if (feof(f)) | |
1243 | break; | |
1244 | ||
1245 | r = -errno; | |
c0b34696 LP |
1246 | log_error("[%s:%u] Failed to read environment file '%s', ignoring: %m", filename, line, rvalue); |
1247 | r = 0; | |
ddb26e18 LP |
1248 | goto finish; |
1249 | } | |
1250 | ||
1251 | p = strstrip(l); | |
1252 | ||
1253 | if (!*p) | |
1254 | continue; | |
1255 | ||
1256 | if (strchr(COMMENTS, *p)) | |
1257 | continue; | |
1258 | ||
1259 | t = strv_env_set(*env, p); | |
1260 | strv_free(*env); | |
1261 | *env = t; | |
1262 | } | |
1263 | ||
1264 | r = 0; | |
1265 | ||
1266 | finish: | |
1267 | if (f) | |
1268 | fclose(f); | |
1269 | ||
1270 | return r; | |
1271 | } | |
1272 | ||
4fd5948e LP |
1273 | static int config_parse_ip_tos( |
1274 | const char *filename, | |
1275 | unsigned line, | |
1276 | const char *section, | |
1277 | const char *lvalue, | |
1278 | const char *rvalue, | |
1279 | void *data, | |
1280 | void *userdata) { | |
1281 | ||
1282 | int *ip_tos = data, x; | |
1283 | int r; | |
1284 | ||
1285 | assert(filename); | |
1286 | assert(lvalue); | |
1287 | assert(rvalue); | |
1288 | assert(data); | |
1289 | ||
1290 | if ((x = ip_tos_from_string(rvalue)) < 0) | |
1291 | if ((r = safe_atoi(rvalue, &x)) < 0) { | |
c0b34696 LP |
1292 | log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue); |
1293 | return 0; | |
4fd5948e LP |
1294 | } |
1295 | ||
1296 | *ip_tos = x; | |
1297 | return 0; | |
1298 | } | |
1299 | ||
487393e9 | 1300 | static DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); |
c952c6ec | 1301 | |
071830ff | 1302 | #define FOLLOW_MAX 8 |
87f0e418 | 1303 | |
9e2f7c11 | 1304 | static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { |
0301abf4 | 1305 | unsigned c = 0; |
87f0e418 LP |
1306 | int fd, r; |
1307 | FILE *f; | |
0301abf4 | 1308 | char *id = NULL; |
87f0e418 LP |
1309 | |
1310 | assert(filename); | |
1311 | assert(*filename); | |
1312 | assert(_f); | |
1313 | assert(names); | |
1314 | ||
0301abf4 LP |
1315 | /* This will update the filename pointer if the loaded file is |
1316 | * reached by a symlink. The old string will be freed. */ | |
87f0e418 | 1317 | |
0301abf4 | 1318 | for (;;) { |
2c7108c4 | 1319 | char *target, *name; |
87f0e418 | 1320 | |
0301abf4 LP |
1321 | if (c++ >= FOLLOW_MAX) |
1322 | return -ELOOP; | |
1323 | ||
b08d03ff LP |
1324 | path_kill_slashes(*filename); |
1325 | ||
87f0e418 | 1326 | /* Add the file name we are currently looking at to |
8f05424d LP |
1327 | * the names of this unit, but only if it is a valid |
1328 | * unit name. */ | |
0301abf4 | 1329 | name = file_name_from_path(*filename); |
87f0e418 | 1330 | |
8f05424d LP |
1331 | if (unit_name_is_valid(name)) { |
1332 | if (!(id = set_get(names, name))) { | |
1333 | ||
1334 | if (!(id = strdup(name))) | |
1335 | return -ENOMEM; | |
87f0e418 | 1336 | |
8f05424d LP |
1337 | if ((r = set_put(names, id)) < 0) { |
1338 | free(id); | |
1339 | return r; | |
1340 | } | |
87f0e418 | 1341 | } |
87f0e418 LP |
1342 | } |
1343 | ||
0301abf4 LP |
1344 | /* Try to open the file name, but don't if its a symlink */ |
1345 | if ((fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0) | |
87f0e418 LP |
1346 | break; |
1347 | ||
0301abf4 LP |
1348 | if (errno != ELOOP) |
1349 | return -errno; | |
1350 | ||
87f0e418 | 1351 | /* Hmm, so this is a symlink. Let's read the name, and follow it manually */ |
2c7108c4 | 1352 | if ((r = readlink_and_make_absolute(*filename, &target)) < 0) |
0301abf4 | 1353 | return r; |
87f0e418 | 1354 | |
0301abf4 | 1355 | free(*filename); |
2c7108c4 | 1356 | *filename = target; |
87f0e418 LP |
1357 | } |
1358 | ||
8f05424d | 1359 | if (!(f = fdopen(fd, "re"))) { |
87f0e418 | 1360 | r = -errno; |
9e2f7c11 | 1361 | close_nointr_nofail(fd); |
0301abf4 | 1362 | return r; |
87f0e418 LP |
1363 | } |
1364 | ||
1365 | *_f = f; | |
9e2f7c11 | 1366 | *_final = id; |
0301abf4 | 1367 | return 0; |
87f0e418 LP |
1368 | } |
1369 | ||
23a177ef LP |
1370 | static int merge_by_names(Unit **u, Set *names, const char *id) { |
1371 | char *k; | |
1372 | int r; | |
1373 | ||
1374 | assert(u); | |
1375 | assert(*u); | |
1376 | assert(names); | |
1377 | ||
1378 | /* Let's try to add in all symlink names we found */ | |
1379 | while ((k = set_steal_first(names))) { | |
1380 | ||
1381 | /* First try to merge in the other name into our | |
1382 | * unit */ | |
1383 | if ((r = unit_merge_by_name(*u, k)) < 0) { | |
1384 | Unit *other; | |
1385 | ||
1386 | /* Hmm, we couldn't merge the other unit into | |
1387 | * ours? Then let's try it the other way | |
1388 | * round */ | |
1389 | ||
1390 | other = manager_get_unit((*u)->meta.manager, k); | |
1391 | free(k); | |
1392 | ||
1393 | if (other) | |
1394 | if ((r = unit_merge(other, *u)) >= 0) { | |
1395 | *u = other; | |
1396 | return merge_by_names(u, names, NULL); | |
1397 | } | |
1398 | ||
1399 | return r; | |
1400 | } | |
1401 | ||
1402 | if (id == k) | |
1403 | unit_choose_id(*u, id); | |
1404 | ||
1405 | free(k); | |
1406 | } | |
1407 | ||
1408 | return 0; | |
1409 | } | |
1410 | ||
e537352b LP |
1411 | static void dump_items(FILE *f, const ConfigItem *items) { |
1412 | const ConfigItem *i; | |
1413 | const char *prev_section = NULL; | |
1414 | bool not_first = false; | |
1415 | ||
1416 | struct { | |
1417 | ConfigParserCallback callback; | |
1418 | const char *rvalue; | |
1419 | } table[] = { | |
1420 | { config_parse_int, "INTEGER" }, | |
1421 | { config_parse_unsigned, "UNSIGNED" }, | |
1422 | { config_parse_size, "SIZE" }, | |
1423 | { config_parse_bool, "BOOLEAN" }, | |
1424 | { config_parse_string, "STRING" }, | |
1425 | { config_parse_path, "PATH" }, | |
1426 | { config_parse_strv, "STRING [...]" }, | |
1427 | { config_parse_nice, "NICE" }, | |
dd6c17b1 | 1428 | { config_parse_oom_score_adjust, "OOMSCOREADJUST" }, |
e537352b LP |
1429 | { config_parse_io_class, "IOCLASS" }, |
1430 | { config_parse_io_priority, "IOPRIORITY" }, | |
1431 | { config_parse_cpu_sched_policy, "CPUSCHEDPOLICY" }, | |
1432 | { config_parse_cpu_sched_prio, "CPUSCHEDPRIO" }, | |
1433 | { config_parse_cpu_affinity, "CPUAFFINITY" }, | |
1434 | { config_parse_mode, "MODE" }, | |
ddb26e18 | 1435 | { config_parse_env_file, "FILE" }, |
e537352b LP |
1436 | { config_parse_output, "OUTPUT" }, |
1437 | { config_parse_input, "INPUT" }, | |
1438 | { config_parse_facility, "FACILITY" }, | |
1439 | { config_parse_level, "LEVEL" }, | |
1440 | { config_parse_capabilities, "CAPABILITIES" }, | |
1441 | { config_parse_secure_bits, "SECUREBITS" }, | |
1442 | { config_parse_bounding_set, "BOUNDINGSET" }, | |
03fae018 | 1443 | { config_parse_timer_slack_nsec, "TIMERSLACK" }, |
e537352b LP |
1444 | { config_parse_limit, "LIMIT" }, |
1445 | { config_parse_cgroup, "CGROUP [...]" }, | |
1446 | { config_parse_deps, "UNIT [...]" }, | |
1447 | { config_parse_names, "UNIT [...]" }, | |
1448 | { config_parse_exec, "PATH [ARGUMENT [...]]" }, | |
1449 | { config_parse_service_type, "SERVICETYPE" }, | |
1450 | { config_parse_service_restart, "SERVICERESTART" }, | |
1451 | { config_parse_sysv_priority, "SYSVPRIORITY" }, | |
1452 | { config_parse_kill_mode, "KILLMODE" }, | |
2e22afe9 | 1453 | { config_parse_kill_signal, "SIGNAL" }, |
e537352b LP |
1454 | { config_parse_listen, "SOCKET [...]" }, |
1455 | { config_parse_socket_bind, "SOCKETBIND" }, | |
93ef5e80 LP |
1456 | { config_parse_bindtodevice, "NETWORKINTERFACE" }, |
1457 | { config_parse_usec, "SECONDS" }, | |
1458 | { config_parse_path_strv, "PATH [...]" }, | |
932921b5 | 1459 | { config_parse_mount_flags, "MOUNTFLAG [...]" }, |
f2d3769a | 1460 | { config_parse_string_printf, "STRING" }, |
871d7de4 LP |
1461 | { config_parse_timer, "TIMER" }, |
1462 | { config_parse_timer_unit, "NAME" }, | |
c952c6ec LP |
1463 | { config_parse_path_spec, "PATH" }, |
1464 | { config_parse_path_unit, "UNIT" }, | |
8b03daeb LP |
1465 | { config_parse_notify_access, "ACCESS" }, |
1466 | { config_parse_ip_tos, "TOS" }, | |
e537352b LP |
1467 | }; |
1468 | ||
1469 | assert(f); | |
1470 | assert(items); | |
1471 | ||
1472 | for (i = items; i->lvalue; i++) { | |
1473 | unsigned j; | |
1474 | const char *rvalue = "OTHER"; | |
1475 | ||
1476 | if (!streq_ptr(i->section, prev_section)) { | |
1477 | if (!not_first) | |
1478 | not_first = true; | |
1479 | else | |
1480 | fputc('\n', f); | |
1481 | ||
1482 | fprintf(f, "[%s]\n", i->section); | |
1483 | prev_section = i->section; | |
1484 | } | |
1485 | ||
1486 | for (j = 0; j < ELEMENTSOF(table); j++) | |
1487 | if (i->parse == table[j].callback) { | |
1488 | rvalue = table[j].rvalue; | |
1489 | break; | |
1490 | } | |
1491 | ||
1492 | fprintf(f, "%s=%s\n", i->lvalue, rvalue); | |
1493 | } | |
1494 | } | |
1495 | ||
1496 | static int load_from_path(Unit *u, const char *path) { | |
87f0e418 LP |
1497 | |
1498 | static const char* const section_table[_UNIT_TYPE_MAX] = { | |
1499 | [UNIT_SERVICE] = "Service", | |
1500 | [UNIT_TIMER] = "Timer", | |
1501 | [UNIT_SOCKET] = "Socket", | |
1502 | [UNIT_TARGET] = "Target", | |
1503 | [UNIT_DEVICE] = "Device", | |
1504 | [UNIT_MOUNT] = "Mount", | |
1505 | [UNIT_AUTOMOUNT] = "Automount", | |
07b0b134 | 1506 | [UNIT_SNAPSHOT] = "Snapshot", |
01f78473 LP |
1507 | [UNIT_SWAP] = "Swap", |
1508 | [UNIT_PATH] = "Path" | |
42f4e3c4 LP |
1509 | }; |
1510 | ||
034c6ed7 | 1511 | #define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \ |
9eba9da4 LP |
1512 | { "WorkingDirectory", config_parse_path, &(context).working_directory, section }, \ |
1513 | { "RootDirectory", config_parse_path, &(context).root_directory, section }, \ | |
f2d3769a LP |
1514 | { "User", config_parse_string_printf, &(context).user, section }, \ |
1515 | { "Group", config_parse_string_printf, &(context).group, section }, \ | |
1c01f82b | 1516 | { "SupplementaryGroups", config_parse_strv, &(context).supplementary_groups, section }, \ |
fb33a393 | 1517 | { "Nice", config_parse_nice, &(context), section }, \ |
dd6c17b1 | 1518 | { "OOMScoreAdjust", config_parse_oom_score_adjust,&(context), section }, \ |
9eba9da4 | 1519 | { "IOSchedulingClass", config_parse_io_class, &(context), section }, \ |
94f04347 LP |
1520 | { "IOSchedulingPriority", config_parse_io_priority, &(context), section }, \ |
1521 | { "CPUSchedulingPolicy", config_parse_cpu_sched_policy,&(context), section }, \ | |
1522 | { "CPUSchedulingPriority", config_parse_cpu_sched_prio, &(context), section }, \ | |
38b48754 | 1523 | { "CPUSchedulingResetOnFork", config_parse_bool, &(context).cpu_sched_reset_on_fork, section }, \ |
94f04347 | 1524 | { "CPUAffinity", config_parse_cpu_affinity, &(context), section }, \ |
b5a0699f | 1525 | { "UMask", config_parse_mode, &(context).umask, section }, \ |
071830ff | 1526 | { "Environment", config_parse_strv, &(context).environment, section }, \ |
ddb26e18 | 1527 | { "EnvironmentFile", config_parse_env_file, &(context).environment, section }, \ |
80876c20 LP |
1528 | { "StandardInput", config_parse_input, &(context).std_input, section }, \ |
1529 | { "StandardOutput", config_parse_output, &(context).std_output, section }, \ | |
a4ddf827 | 1530 | { "StandardError", config_parse_output, &(context).std_error, section }, \ |
80876c20 | 1531 | { "TTYPath", config_parse_path, &(context).tty_path, section }, \ |
f2d3769a | 1532 | { "SyslogIdentifier", config_parse_string_printf, &(context).syslog_identifier, section }, \ |
071830ff | 1533 | { "SyslogFacility", config_parse_facility, &(context).syslog_priority, section }, \ |
94f04347 | 1534 | { "SyslogLevel", config_parse_level, &(context).syslog_priority, section }, \ |
74922904 | 1535 | { "SyslogLevelPrefix", config_parse_bool, &(context).syslog_level_prefix, section }, \ |
94f04347 LP |
1536 | { "Capabilities", config_parse_capabilities, &(context), section }, \ |
1537 | { "SecureBits", config_parse_secure_bits, &(context), section }, \ | |
1538 | { "CapabilityBoundingSetDrop", config_parse_bounding_set, &(context), section }, \ | |
03fae018 | 1539 | { "TimerSlackNSec", config_parse_timer_slack_nsec,&(context), section }, \ |
94f04347 LP |
1540 | { "LimitCPU", config_parse_limit, &(context).rlimit[RLIMIT_CPU], section }, \ |
1541 | { "LimitFSIZE", config_parse_limit, &(context).rlimit[RLIMIT_FSIZE], section }, \ | |
1542 | { "LimitDATA", config_parse_limit, &(context).rlimit[RLIMIT_DATA], section }, \ | |
1543 | { "LimitSTACK", config_parse_limit, &(context).rlimit[RLIMIT_STACK], section }, \ | |
1544 | { "LimitCORE", config_parse_limit, &(context).rlimit[RLIMIT_CORE], section }, \ | |
1545 | { "LimitRSS", config_parse_limit, &(context).rlimit[RLIMIT_RSS], section }, \ | |
1546 | { "LimitNOFILE", config_parse_limit, &(context).rlimit[RLIMIT_NOFILE], section }, \ | |
1547 | { "LimitAS", config_parse_limit, &(context).rlimit[RLIMIT_AS], section }, \ | |
1548 | { "LimitNPROC", config_parse_limit, &(context).rlimit[RLIMIT_NPROC], section }, \ | |
1549 | { "LimitMEMLOCK", config_parse_limit, &(context).rlimit[RLIMIT_MEMLOCK], section }, \ | |
1550 | { "LimitLOCKS", config_parse_limit, &(context).rlimit[RLIMIT_LOCKS], section }, \ | |
1551 | { "LimitSIGPENDING", config_parse_limit, &(context).rlimit[RLIMIT_SIGPENDING], section }, \ | |
1552 | { "LimitMSGQUEUE", config_parse_limit, &(context).rlimit[RLIMIT_MSGQUEUE], section }, \ | |
1553 | { "LimitNICE", config_parse_limit, &(context).rlimit[RLIMIT_NICE], section }, \ | |
1554 | { "LimitRTPRIO", config_parse_limit, &(context).rlimit[RLIMIT_RTPRIO], section }, \ | |
451a074f | 1555 | { "LimitRTTIME", config_parse_limit, &(context).rlimit[RLIMIT_RTTIME], section }, \ |
15ae422b LP |
1556 | { "ControlGroup", config_parse_cgroup, u, section }, \ |
1557 | { "ReadWriteDirectories", config_parse_path_strv, &(context).read_write_dirs, section }, \ | |
1558 | { "ReadOnlyDirectories", config_parse_path_strv, &(context).read_only_dirs, section }, \ | |
1559 | { "InaccessibleDirectories",config_parse_path_strv, &(context).inaccessible_dirs, section }, \ | |
1560 | { "PrivateTmp", config_parse_bool, &(context).private_tmp, section }, \ | |
df1f0afe | 1561 | { "MountFlags", config_parse_mount_flags, &(context), section }, \ |
f2d3769a | 1562 | { "TCPWrapName", config_parse_string_printf, &(context).tcpwrap_name, section }, \ |
2e22afe9 LP |
1563 | { "PAMName", config_parse_string_printf, &(context).pam_name, section }, \ |
1564 | { "KillMode", config_parse_kill_mode, &(context).kill_mode, section }, \ | |
1565 | { "KillSignal", config_parse_kill_signal, &(context).kill_signal, section } | |
034c6ed7 | 1566 | |
3efd4195 | 1567 | const ConfigItem items[] = { |
09477267 | 1568 | { "Names", config_parse_names, u, "Unit" }, |
f2d3769a | 1569 | { "Description", config_parse_string_printf, &u->meta.description, "Unit" }, |
09477267 LP |
1570 | { "Requires", config_parse_deps, UINT_TO_PTR(UNIT_REQUIRES), "Unit" }, |
1571 | { "RequiresOverridable", config_parse_deps, UINT_TO_PTR(UNIT_REQUIRES_OVERRIDABLE), "Unit" }, | |
1572 | { "Requisite", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE), "Unit" }, | |
1573 | { "RequisiteOverridable", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE_OVERRIDABLE), "Unit" }, | |
1574 | { "Wants", config_parse_deps, UINT_TO_PTR(UNIT_WANTS), "Unit" }, | |
1575 | { "Conflicts", config_parse_deps, UINT_TO_PTR(UNIT_CONFLICTS), "Unit" }, | |
1576 | { "Before", config_parse_deps, UINT_TO_PTR(UNIT_BEFORE), "Unit" }, | |
1577 | { "After", config_parse_deps, UINT_TO_PTR(UNIT_AFTER), "Unit" }, | |
45fb0699 | 1578 | { "OnFailure", config_parse_deps, UINT_TO_PTR(UNIT_ON_FAILURE), "Unit" }, |
09477267 LP |
1579 | { "RecursiveStop", config_parse_bool, &u->meta.recursive_stop, "Unit" }, |
1580 | { "StopWhenUnneeded", config_parse_bool, &u->meta.stop_when_unneeded, "Unit" }, | |
b5e9dba8 LP |
1581 | { "RefuseManualStart", config_parse_bool, &u->meta.refuse_manual_start, "Unit" }, |
1582 | { "RefuseManualStop", config_parse_bool, &u->meta.refuse_manual_stop, "Unit" }, | |
2528a7a6 | 1583 | { "AllowIsolate", config_parse_bool, &u->meta.allow_isolate, "Unit" }, |
a40eb732 | 1584 | { "DefaultDependencies", config_parse_bool, &u->meta.default_dependencies, "Unit" }, |
3b6fdb5b | 1585 | { "IgnoreDependencyFailure",config_parse_bool, &u->meta.ignore_dependency_failure, "Unit" }, |
faf919f1 | 1586 | { "JobTimeoutSec", config_parse_usec, &u->meta.job_timeout, "Unit" }, |
1c01f82b LP |
1587 | |
1588 | { "PIDFile", config_parse_path, &u->service.pid_file, "Service" }, | |
1589 | { "ExecStartPre", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START_PRE, "Service" }, | |
1590 | { "ExecStart", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START, "Service" }, | |
1591 | { "ExecStartPost", config_parse_exec, u->service.exec_command+SERVICE_EXEC_START_POST, "Service" }, | |
1592 | { "ExecReload", config_parse_exec, u->service.exec_command+SERVICE_EXEC_RELOAD, "Service" }, | |
1593 | { "ExecStop", config_parse_exec, u->service.exec_command+SERVICE_EXEC_STOP, "Service" }, | |
1594 | { "ExecStopPost", config_parse_exec, u->service.exec_command+SERVICE_EXEC_STOP_POST, "Service" }, | |
1595 | { "RestartSec", config_parse_usec, &u->service.restart_usec, "Service" }, | |
1596 | { "TimeoutSec", config_parse_usec, &u->service.timeout_usec, "Service" }, | |
0d87eb42 LP |
1597 | { "Type", config_parse_service_type, &u->service.type, "Service" }, |
1598 | { "Restart", config_parse_service_restart, &u->service.restart, "Service" }, | |
81a2b7ce LP |
1599 | { "PermissionsStartOnly", config_parse_bool, &u->service.permissions_start_only, "Service" }, |
1600 | { "RootDirectoryStartOnly", config_parse_bool, &u->service.root_directory_start_only, "Service" }, | |
02ee865a | 1601 | { "RemainAfterExit", config_parse_bool, &u->service.remain_after_exit, "Service" }, |
a9a1e00a | 1602 | { "SysVStartPriority", config_parse_sysv_priority, &u->service.sysv_start_priority, "Service" }, |
b778d555 | 1603 | { "NonBlocking", config_parse_bool, &u->service.exec_context.non_blocking, "Service" }, |
f2d3769a | 1604 | { "BusName", config_parse_string_printf, &u->service.bus_name, "Service" }, |
c952c6ec | 1605 | { "NotifyAccess", config_parse_notify_access, &u->service.notify_access, "Service" }, |
87f0e418 LP |
1606 | EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"), |
1607 | ||
1c01f82b LP |
1608 | { "ListenStream", config_parse_listen, &u->socket, "Socket" }, |
1609 | { "ListenDatagram", config_parse_listen, &u->socket, "Socket" }, | |
1610 | { "ListenSequentialPacket", config_parse_listen, &u->socket, "Socket" }, | |
1611 | { "ListenFIFO", config_parse_listen, &u->socket, "Socket" }, | |
1612 | { "BindIPv6Only", config_parse_socket_bind, &u->socket, "Socket" }, | |
1613 | { "Backlog", config_parse_unsigned, &u->socket.backlog, "Socket" }, | |
acbb0225 | 1614 | { "BindToDevice", config_parse_bindtodevice, &u->socket, "Socket" }, |
1c01f82b LP |
1615 | { "ExecStartPre", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_START_PRE, "Socket" }, |
1616 | { "ExecStartPost", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_START_POST, "Socket" }, | |
1617 | { "ExecStopPre", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_STOP_PRE, "Socket" }, | |
1618 | { "ExecStopPost", config_parse_exec, u->socket.exec_command+SOCKET_EXEC_STOP_POST, "Socket" }, | |
108736d0 | 1619 | { "TimeoutSec", config_parse_usec, &u->socket.timeout_usec, "Socket" }, |
b5a0699f LP |
1620 | { "DirectoryMode", config_parse_mode, &u->socket.directory_mode, "Socket" }, |
1621 | { "SocketMode", config_parse_mode, &u->socket.socket_mode, "Socket" }, | |
4f2d528d | 1622 | { "Accept", config_parse_bool, &u->socket.accept, "Socket" }, |
6cf6bbc2 | 1623 | { "MaxConnections", config_parse_unsigned, &u->socket.max_connections, "Socket" }, |
4fd5948e LP |
1624 | { "KeepAlive", config_parse_bool, &u->socket.keep_alive, "Socket" }, |
1625 | { "Priority", config_parse_int, &u->socket.priority, "Socket" }, | |
1626 | { "ReceiveBuffer", config_parse_size, &u->socket.receive_buffer, "Socket" }, | |
1627 | { "SendBuffer", config_parse_size, &u->socket.send_buffer, "Socket" }, | |
1628 | { "IPTOS", config_parse_ip_tos, &u->socket.ip_tos, "Socket" }, | |
1629 | { "IPTTL", config_parse_int, &u->socket.ip_ttl, "Socket" }, | |
1630 | { "Mark", config_parse_int, &u->socket.mark, "Socket" }, | |
1631 | { "PipeSize", config_parse_size, &u->socket.pipe_size, "Socket" }, | |
1632 | { "FreeBind", config_parse_bool, &u->socket.free_bind, "Socket" }, | |
cebf8b20 | 1633 | { "TCPCongestion", config_parse_string, &u->socket.tcp_congestion, "Socket" }, |
87f0e418 LP |
1634 | EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"), |
1635 | ||
e537352b LP |
1636 | { "What", config_parse_string, &u->mount.parameters_fragment.what, "Mount" }, |
1637 | { "Where", config_parse_path, &u->mount.where, "Mount" }, | |
1638 | { "Options", config_parse_string, &u->mount.parameters_fragment.options, "Mount" }, | |
1639 | { "Type", config_parse_string, &u->mount.parameters_fragment.fstype, "Mount" }, | |
1640 | { "TimeoutSec", config_parse_usec, &u->mount.timeout_usec, "Mount" }, | |
3e5235b0 | 1641 | { "DirectoryMode", config_parse_mode, &u->mount.directory_mode, "Mount" }, |
e537352b | 1642 | EXEC_CONTEXT_CONFIG_ITEMS(u->mount.exec_context, "Mount"), |
034c6ed7 | 1643 | |
8d567588 | 1644 | { "Where", config_parse_path, &u->automount.where, "Automount" }, |
1cf18f27 | 1645 | { "DirectoryMode", config_parse_mode, &u->automount.directory_mode, "Automount" }, |
8d567588 | 1646 | |
01f78473 LP |
1647 | { "What", config_parse_path, &u->swap.parameters_fragment.what, "Swap" }, |
1648 | { "Priority", config_parse_int, &u->swap.parameters_fragment.priority, "Swap" }, | |
1649 | ||
03fae018 LP |
1650 | { "OnActiveSec", config_parse_timer, &u->timer, "Timer" }, |
1651 | { "OnBootSec", config_parse_timer, &u->timer, "Timer" }, | |
1652 | { "OnStartupSec", config_parse_timer, &u->timer, "Timer" }, | |
1653 | { "OnUnitActiveSec", config_parse_timer, &u->timer, "Timer" }, | |
1654 | { "OnUnitInactiveSec", config_parse_timer, &u->timer, "Timer" }, | |
01f78473 | 1655 | { "Unit", config_parse_timer_unit, &u->timer, "Timer" }, |
07b0b134 | 1656 | |
01f78473 LP |
1657 | { "PathExists", config_parse_path_spec, &u->path, "Path" }, |
1658 | { "PathChanged", config_parse_path_spec, &u->path, "Path" }, | |
1659 | { "DirectoryNotEmpty", config_parse_path_spec, &u->path, "Path" }, | |
1660 | { "Unit", config_parse_path_unit, &u->path, "Path" }, | |
871d7de4 | 1661 | |
10e87ee7 LP |
1662 | /* The [Install] section is ignored here. */ |
1663 | { "Alias", NULL, NULL, "Install" }, | |
1664 | { "WantedBy", NULL, NULL, "Install" }, | |
1665 | { "Also", NULL, NULL, "Install" }, | |
1666 | ||
3efd4195 LP |
1667 | { NULL, NULL, NULL, NULL } |
1668 | }; | |
1669 | ||
034c6ed7 | 1670 | #undef EXEC_CONTEXT_CONFIG_ITEMS |
42f4e3c4 | 1671 | |
10e87ee7 | 1672 | const char *sections[4]; |
0301abf4 | 1673 | int r; |
87f0e418 | 1674 | Set *symlink_names; |
23a177ef LP |
1675 | FILE *f = NULL; |
1676 | char *filename = NULL, *id = NULL; | |
1677 | Unit *merged; | |
45fb0699 | 1678 | struct stat st; |
23a177ef | 1679 | |
e537352b LP |
1680 | if (!u) { |
1681 | /* Dirty dirty hack. */ | |
1682 | dump_items((FILE*) path, items); | |
1683 | return 0; | |
1684 | } | |
1685 | ||
23a177ef | 1686 | assert(u); |
e537352b | 1687 | assert(path); |
3efd4195 | 1688 | |
09477267 | 1689 | sections[0] = "Unit"; |
87f0e418 | 1690 | sections[1] = section_table[u->meta.type]; |
10e87ee7 LP |
1691 | sections[2] = "Install"; |
1692 | sections[3] = NULL; | |
42f4e3c4 | 1693 | |
87f0e418 LP |
1694 | if (!(symlink_names = set_new(string_hash_func, string_compare_func))) |
1695 | return -ENOMEM; | |
3efd4195 | 1696 | |
036643a2 LP |
1697 | if (path_is_absolute(path)) { |
1698 | ||
1699 | if (!(filename = strdup(path))) { | |
1700 | r = -ENOMEM; | |
1701 | goto finish; | |
1702 | } | |
1703 | ||
1704 | if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) { | |
1705 | free(filename); | |
1706 | filename = NULL; | |
1707 | ||
1708 | if (r != -ENOENT) | |
1709 | goto finish; | |
1710 | } | |
1711 | ||
1712 | } else { | |
1713 | char **p; | |
1714 | ||
84e3543e | 1715 | STRV_FOREACH(p, u->meta.manager->lookup_paths.unit_path) { |
036643a2 LP |
1716 | |
1717 | /* Instead of opening the path right away, we manually | |
1718 | * follow all symlinks and add their name to our unit | |
1719 | * name set while doing so */ | |
1720 | if (!(filename = path_make_absolute(path, *p))) { | |
1721 | r = -ENOMEM; | |
1722 | goto finish; | |
1723 | } | |
1724 | ||
fe51822e LP |
1725 | if (u->meta.manager->unit_path_cache && |
1726 | !set_get(u->meta.manager->unit_path_cache, filename)) | |
1727 | r = -ENOENT; | |
1728 | else | |
1729 | r = open_follow(&filename, &f, symlink_names, &id); | |
1730 | ||
1731 | if (r < 0) { | |
036643a2 LP |
1732 | char *sn; |
1733 | ||
1734 | free(filename); | |
1735 | filename = NULL; | |
1736 | ||
1737 | if (r != -ENOENT) | |
1738 | goto finish; | |
1739 | ||
1740 | /* Empty the symlink names for the next run */ | |
1741 | while ((sn = set_steal_first(symlink_names))) | |
1742 | free(sn); | |
3efd4195 | 1743 | |
036643a2 LP |
1744 | continue; |
1745 | } | |
1746 | ||
1747 | break; | |
1748 | } | |
1749 | } | |
034c6ed7 | 1750 | |
036643a2 | 1751 | if (!filename) { |
8f05424d | 1752 | /* Hmm, no suitable file found? */ |
23a177ef | 1753 | r = 0; |
0301abf4 LP |
1754 | goto finish; |
1755 | } | |
87f0e418 | 1756 | |
23a177ef LP |
1757 | merged = u; |
1758 | if ((r = merge_by_names(&merged, symlink_names, id)) < 0) | |
0301abf4 | 1759 | goto finish; |
87f0e418 | 1760 | |
23a177ef | 1761 | if (merged != u) { |
e537352b | 1762 | u->meta.load_state = UNIT_MERGED; |
23a177ef LP |
1763 | r = 0; |
1764 | goto finish; | |
034c6ed7 LP |
1765 | } |
1766 | ||
45fb0699 LP |
1767 | zero(st); |
1768 | if (fstat(fileno(f), &st) < 0) { | |
1769 | r = -errno; | |
1770 | goto finish; | |
1771 | } | |
1772 | ||
23a177ef | 1773 | /* Now, parse the file contents */ |
10e87ee7 | 1774 | if ((r = config_parse(filename, f, sections, items, false, u)) < 0) |
23a177ef | 1775 | goto finish; |
b08d03ff | 1776 | |
6be1e7d5 LP |
1777 | free(u->meta.fragment_path); |
1778 | u->meta.fragment_path = filename; | |
0301abf4 | 1779 | filename = NULL; |
87f0e418 | 1780 | |
45fb0699 LP |
1781 | u->meta.fragment_mtime = timespec_load(&st.st_mtim); |
1782 | ||
e537352b | 1783 | u->meta.load_state = UNIT_LOADED; |
23a177ef | 1784 | r = 0; |
87f0e418 LP |
1785 | |
1786 | finish: | |
53ec43c6 | 1787 | set_free_free(symlink_names); |
0301abf4 LP |
1788 | free(filename); |
1789 | ||
23a177ef LP |
1790 | if (f) |
1791 | fclose(f); | |
1792 | ||
0301abf4 LP |
1793 | return r; |
1794 | } | |
1795 | ||
e537352b | 1796 | int unit_load_fragment(Unit *u) { |
23a177ef | 1797 | int r; |
294d81f1 LP |
1798 | Iterator i; |
1799 | const char *t; | |
0301abf4 LP |
1800 | |
1801 | assert(u); | |
294d81f1 LP |
1802 | assert(u->meta.load_state == UNIT_STUB); |
1803 | assert(u->meta.id); | |
23a177ef | 1804 | |
294d81f1 LP |
1805 | /* First, try to find the unit under its id. We always look |
1806 | * for unit files in the default directories, to make it easy | |
1807 | * to override things by placing things in /etc/systemd/system */ | |
1808 | if ((r = load_from_path(u, u->meta.id)) < 0) | |
1809 | return r; | |
1810 | ||
1811 | /* Try to find an alias we can load this with */ | |
1812 | if (u->meta.load_state == UNIT_STUB) | |
1813 | SET_FOREACH(t, u->meta.names, i) { | |
1814 | ||
1815 | if (t == u->meta.id) | |
1816 | continue; | |
1817 | ||
1818 | if ((r = load_from_path(u, t)) < 0) | |
1819 | return r; | |
1820 | ||
1821 | if (u->meta.load_state != UNIT_STUB) | |
1822 | break; | |
1823 | } | |
23a177ef | 1824 | |
294d81f1 LP |
1825 | /* And now, try looking for it under the suggested (originally linked) path */ |
1826 | if (u->meta.load_state == UNIT_STUB && u->meta.fragment_path) | |
e537352b | 1827 | if ((r = load_from_path(u, u->meta.fragment_path)) < 0) |
23a177ef | 1828 | return r; |
0301abf4 | 1829 | |
294d81f1 LP |
1830 | /* Look for a template */ |
1831 | if (u->meta.load_state == UNIT_STUB && u->meta.instance) { | |
1832 | char *k; | |
1833 | ||
1834 | if (!(k = unit_name_template(u->meta.id))) | |
1835 | return -ENOMEM; | |
1836 | ||
1837 | r = load_from_path(u, k); | |
1838 | free(k); | |
0301abf4 | 1839 | |
294d81f1 | 1840 | if (r < 0) |
9e2f7c11 | 1841 | return r; |
890f434c | 1842 | |
e537352b | 1843 | if (u->meta.load_state == UNIT_STUB) |
23a177ef | 1844 | SET_FOREACH(t, u->meta.names, i) { |
87f0e418 | 1845 | |
9e2f7c11 | 1846 | if (t == u->meta.id) |
23a177ef | 1847 | continue; |
071830ff | 1848 | |
294d81f1 LP |
1849 | if (!(k = unit_name_template(t))) |
1850 | return -ENOMEM; | |
1851 | ||
1852 | r = load_from_path(u, k); | |
1853 | free(k); | |
1854 | ||
1855 | if (r < 0) | |
23a177ef | 1856 | return r; |
890f434c | 1857 | |
e537352b | 1858 | if (u->meta.load_state != UNIT_STUB) |
23a177ef LP |
1859 | break; |
1860 | } | |
071830ff LP |
1861 | } |
1862 | ||
23a177ef | 1863 | return 0; |
3efd4195 | 1864 | } |
e537352b LP |
1865 | |
1866 | void unit_dump_config_items(FILE *f) { | |
1867 | /* OK, this wins a prize for extreme ugliness. */ | |
1868 | ||
1869 | load_from_path(NULL, (const void*) f); | |
1870 | } |