]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
timesyncd: the IP_TOS sockopt is really just an optimization, we shouldn't fail if...
[thirdparty/systemd.git] / src / core / load-fragment.c
CommitLineData
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
bb112710 7 Copyright 2012 Holger Hans Peter Freyther
a7334b09
LP
8
9 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
a7334b09
LP
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 17 Lesser General Public License for more details.
a7334b09 18
5430f7f2 19 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21***/
22
87f0e418 23#include <linux/oom.h>
3efd4195
LP
24#include <assert.h>
25#include <errno.h>
26#include <string.h>
87f0e418
LP
27#include <unistd.h>
28#include <fcntl.h>
94f04347
LP
29#include <sched.h>
30#include <sys/prctl.h>
15ae422b 31#include <sys/mount.h>
25e870b5 32#include <linux/fs.h>
45fb0699 33#include <sys/stat.h>
3d57c6ab
LP
34#include <sys/time.h>
35#include <sys/resource.h>
54d76c92
DM
36#include <sys/types.h>
37#include <grp.h>
17df7223 38
c0467cf3
RC
39#ifdef HAVE_SECCOMP
40#include <seccomp.h>
c0467cf3 41#endif
3efd4195 42
84f6181c 43#include "sd-messages.h"
87f0e418 44#include "unit.h"
3efd4195
LP
45#include "strv.h"
46#include "conf-parser.h"
47#include "load-fragment.h"
16354eff 48#include "log.h"
9eba9da4 49#include "ioprio.h"
94f04347
LP
50#include "securebits.h"
51#include "missing.h"
9e2f7c11 52#include "unit-name.h"
41f9172f 53#include "unit-printf.h"
7f110ff9 54#include "utf8.h"
9eb977db 55#include "path-util.h"
853b8397 56#include "env-util.h"
4ad49000 57#include "cgroup.h"
718db961
LP
58#include "bus-util.h"
59#include "bus-error.h"
17df7223 60#include "errno-list.h"
4298d0b5 61#include "af-list.h"
3efd4195 62
57183d11
LP
63#ifdef HAVE_SECCOMP
64#include "seccomp-util.h"
65#endif
66
7f8aa671 67#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
17df7223
LP
68int config_parse_warn_compat(
69 const char *unit,
70 const char *filename,
71 unsigned line,
72 const char *section,
73 unsigned section_line,
74 const char *lvalue,
75 int ltype,
76 const char *rvalue,
77 void *data,
78 void *userdata) {
e8e581bf
ZJS
79
80 log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
81 "Support for option %s= has been disabled at compile time and is ignored",
82 lvalue);
07459bb6
FF
83 return 0;
84}
85#endif
86
1e2fd62d 87int config_parse_unit_deps(const char *unit,
e8e581bf
ZJS
88 const char *filename,
89 unsigned line,
90 const char *section,
71a61510 91 unsigned section_line,
e8e581bf
ZJS
92 const char *lvalue,
93 int ltype,
94 const char *rvalue,
95 void *data,
96 void *userdata) {
3efd4195 97
f975e971 98 UnitDependency d = ltype;
87f0e418 99 Unit *u = userdata;
a2a5291b 100 const char *word, *state;
3efd4195 101 size_t l;
3efd4195
LP
102
103 assert(filename);
104 assert(lvalue);
105 assert(rvalue);
3efd4195 106
a2a5291b 107 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
7fd1b19b 108 _cleanup_free_ char *t = NULL, *k = NULL;
3efd4195 109 int r;
3efd4195 110
a2a5291b 111 t = strndup(word, l);
57020a3a 112 if (!t)
74051b9b 113 return log_oom();
3efd4195 114
19f6d710
LP
115 r = unit_name_printf(u, t, &k);
116 if (r < 0) {
117 log_syntax(unit, LOG_ERR, filename, line, -r,
118 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
119 continue;
120 }
9e2f7c11 121
701cc384 122 r = unit_add_dependency_by_name(u, d, k, NULL, true);
57020a3a 123 if (r < 0)
e8e581bf
ZJS
124 log_syntax(unit, LOG_ERR, filename, line, -r,
125 "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
3efd4195 126 }
b2fadec6
ZJS
127 if (!isempty(state))
128 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring.");
3efd4195
LP
129
130 return 0;
131}
132
e8e581bf
ZJS
133int config_parse_unit_string_printf(const char *unit,
134 const char *filename,
135 unsigned line,
136 const char *section,
71a61510 137 unsigned section_line,
e8e581bf
ZJS
138 const char *lvalue,
139 int ltype,
140 const char *rvalue,
141 void *data,
142 void *userdata) {
932921b5
LP
143
144 Unit *u = userdata;
74051b9b 145 _cleanup_free_ char *k = NULL;
19f6d710 146 int r;
932921b5
LP
147
148 assert(filename);
149 assert(lvalue);
150 assert(rvalue);
f2d3769a 151 assert(u);
932921b5 152
19f6d710
LP
153 r = unit_full_printf(u, rvalue, &k);
154 if (r < 0)
155 log_syntax(unit, LOG_ERR, filename, line, -r,
156 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
932921b5 157
71a61510 158 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 159 k ? k : rvalue, data, userdata);
932921b5
LP
160}
161
e8e581bf
ZJS
162int config_parse_unit_strv_printf(const char *unit,
163 const char *filename,
164 unsigned line,
165 const char *section,
71a61510 166 unsigned section_line,
e8e581bf
ZJS
167 const char *lvalue,
168 int ltype,
169 const char *rvalue,
170 void *data,
171 void *userdata) {
8fef7659
LP
172
173 Unit *u = userdata;
74051b9b 174 _cleanup_free_ char *k = NULL;
19f6d710 175 int r;
8fef7659
LP
176
177 assert(filename);
178 assert(lvalue);
179 assert(rvalue);
180 assert(u);
181
19f6d710
LP
182 r = unit_full_printf(u, rvalue, &k);
183 if (r < 0)
184 log_syntax(unit, LOG_ERR, filename, line, -r,
185 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
8fef7659 186
71a61510 187 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 188 k ? k : rvalue, data, userdata);
8fef7659
LP
189}
190
e8e581bf
ZJS
191int config_parse_unit_path_printf(const char *unit,
192 const char *filename,
193 unsigned line,
194 const char *section,
71a61510 195 unsigned section_line,
e8e581bf
ZJS
196 const char *lvalue,
197 int ltype,
198 const char *rvalue,
199 void *data,
200 void *userdata) {
6ea832a2 201
74051b9b 202 _cleanup_free_ char *k = NULL;
811ba7a0 203 Unit *u = userdata;
19f6d710 204 int r;
6ea832a2
LP
205
206 assert(filename);
207 assert(lvalue);
208 assert(rvalue);
6ea832a2
LP
209 assert(u);
210
19f6d710 211 r = unit_full_printf(u, rvalue, &k);
811ba7a0
LP
212 if (r < 0) {
213 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
214 return 0;
215 }
6ea832a2 216
811ba7a0
LP
217 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
218}
219
220int config_parse_unit_path_strv_printf(
221 const char *unit,
222 const char *filename,
223 unsigned line,
224 const char *section,
225 unsigned section_line,
226 const char *lvalue,
227 int ltype,
228 const char *rvalue,
229 void *data,
230 void *userdata) {
231
a2a5291b
ZJS
232 char ***x = data;
233 const char *word, *state;
811ba7a0
LP
234 Unit *u = userdata;
235 size_t l;
236 int r;
237
238 assert(filename);
239 assert(lvalue);
240 assert(rvalue);
241 assert(u);
242
a2a5291b 243 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
811ba7a0
LP
244 _cleanup_free_ char *k = NULL;
245 char t[l+1];
246
a2a5291b 247 memcpy(t, word, l);
811ba7a0
LP
248 t[l] = 0;
249
250 r = unit_full_printf(u, t, &k);
251 if (r < 0) {
252 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", t, strerror(-r));
253 return 0;
254 }
255
256 if (!utf8_is_valid(k)) {
257 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
258 return 0;
259 }
260
261 if (!path_is_absolute(k)) {
262 log_syntax(unit, LOG_ERR, filename, line, -r, "Symlink path %s is not absolute, ignoring: %s", k, strerror(-r));
263 return 0;
264 }
265
266 path_kill_slashes(k);
267
268 r = strv_push(x, k);
269 if (r < 0)
270 return log_oom();
271
272 k = NULL;
273 }
b2fadec6
ZJS
274 if (!isempty(state))
275 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring.");
811ba7a0
LP
276
277 return 0;
6ea832a2
LP
278}
279
e8e581bf
ZJS
280int config_parse_socket_listen(const char *unit,
281 const char *filename,
282 unsigned line,
283 const char *section,
71a61510 284 unsigned section_line,
e8e581bf
ZJS
285 const char *lvalue,
286 int ltype,
287 const char *rvalue,
288 void *data,
289 void *userdata) {
42f4e3c4 290
b1389b0d
ZJS
291 _cleanup_free_ SocketPort *p = NULL;
292 SocketPort *tail;
542563ba 293 Socket *s;
19f6d710 294 int r;
16354eff 295
42f4e3c4
LP
296 assert(filename);
297 assert(lvalue);
298 assert(rvalue);
299 assert(data);
300
595ed347 301 s = SOCKET(data);
542563ba 302
74051b9b
LP
303 if (isempty(rvalue)) {
304 /* An empty assignment removes all ports */
305 socket_free_ports(s);
306 return 0;
307 }
308
7f110ff9
LP
309 p = new0(SocketPort, 1);
310 if (!p)
74051b9b 311 return log_oom();
916abb21 312
74051b9b 313 if (ltype != SOCKET_SOCKET) {
916abb21 314
74051b9b 315 p->type = ltype;
19f6d710
LP
316 r = unit_full_printf(UNIT(s), rvalue, &p->path);
317 if (r < 0) {
487060c2 318 p->path = strdup(rvalue);
b1389b0d 319 if (!p->path)
487060c2 320 return log_oom();
b1389b0d 321 else
19f6d710
LP
322 log_syntax(unit, LOG_ERR, filename, line, -r,
323 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
916abb21
LP
324 }
325
326 path_kill_slashes(p->path);
327
7a22745a 328 } else if (streq(lvalue, "ListenNetlink")) {
74051b9b 329 _cleanup_free_ char *k = NULL;
1fd45a90 330
7a22745a 331 p->type = SOCKET_SOCKET;
19f6d710
LP
332 r = unit_full_printf(UNIT(s), rvalue, &k);
333 if (r < 0)
334 log_syntax(unit, LOG_ERR, filename, line, -r,
335 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
7a22745a 336
b1389b0d 337 r = socket_address_parse_netlink(&p->address, k ?: rvalue);
1fd45a90 338 if (r < 0) {
19f6d710 339 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf 340 "Failed to parse address value, ignoring: %s", rvalue);
7a22745a
LP
341 return 0;
342 }
343
542563ba 344 } else {
74051b9b 345 _cleanup_free_ char *k = NULL;
1fd45a90 346
542563ba 347 p->type = SOCKET_SOCKET;
19f6d710
LP
348 r = unit_full_printf(UNIT(s), rvalue, &k);
349 if (r < 0)
350 log_syntax(unit, LOG_ERR, filename, line, -r,
351 "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
542563ba 352
487060c2 353 r = socket_address_parse(&p->address, k ? k : rvalue);
1fd45a90 354 if (r < 0) {
19f6d710 355 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf 356 "Failed to parse address value, ignoring: %s", rvalue);
c0b34696 357 return 0;
542563ba
LP
358 }
359
360 if (streq(lvalue, "ListenStream"))
361 p->address.type = SOCK_STREAM;
362 else if (streq(lvalue, "ListenDatagram"))
363 p->address.type = SOCK_DGRAM;
364 else {
365 assert(streq(lvalue, "ListenSequentialPacket"));
366 p->address.type = SOCK_SEQPACKET;
367 }
368
369 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
e8e581bf
ZJS
370 log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
371 "Address family not supported, ignoring: %s", rvalue);
c0b34696 372 return 0;
542563ba 373 }
16354eff
LP
374 }
375
542563ba 376 p->fd = -1;
2e41a51e 377 p->socket = s;
49f91047
LP
378
379 if (s->ports) {
71fda00f
LP
380 LIST_FIND_TAIL(port, s->ports, tail);
381 LIST_INSERT_AFTER(port, s->ports, tail, p);
49f91047 382 } else
71fda00f 383 LIST_PREPEND(port, s->ports, p);
b1389b0d 384 p = NULL;
542563ba 385
16354eff 386 return 0;
42f4e3c4
LP
387}
388
e8e581bf
ZJS
389int config_parse_socket_bind(const char *unit,
390 const char *filename,
391 unsigned line,
392 const char *section,
71a61510 393 unsigned section_line,
e8e581bf
ZJS
394 const char *lvalue,
395 int ltype,
396 const char *rvalue,
397 void *data,
398 void *userdata) {
42f4e3c4 399
542563ba 400 Socket *s;
c0120d99 401 SocketAddressBindIPv6Only b;
42f4e3c4
LP
402
403 assert(filename);
404 assert(lvalue);
405 assert(rvalue);
406 assert(data);
407
595ed347 408 s = SOCKET(data);
542563ba 409
5198dabc
LP
410 b = socket_address_bind_ipv6_only_from_string(rvalue);
411 if (b < 0) {
c0120d99
LP
412 int r;
413
5198dabc
LP
414 r = parse_boolean(rvalue);
415 if (r < 0) {
e8e581bf
ZJS
416 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
417 "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
c0b34696 418 return 0;
c0120d99 419 }
42f4e3c4 420
c0120d99
LP
421 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
422 } else
423 s->bind_ipv6_only = b;
542563ba 424
42f4e3c4
LP
425 return 0;
426}
427
e8e581bf
ZJS
428int config_parse_exec_nice(const char *unit,
429 const char *filename,
430 unsigned line,
431 const char *section,
71a61510 432 unsigned section_line,
e8e581bf
ZJS
433 const char *lvalue,
434 int ltype,
435 const char *rvalue,
436 void *data,
437 void *userdata) {
034c6ed7 438
fb33a393 439 ExecContext *c = data;
e8e581bf 440 int priority, r;
034c6ed7
LP
441
442 assert(filename);
443 assert(lvalue);
444 assert(rvalue);
445 assert(data);
446
e8e581bf
ZJS
447 r = safe_atoi(rvalue, &priority);
448 if (r < 0) {
449 log_syntax(unit, LOG_ERR, filename, line, -r,
450 "Failed to parse nice priority, ignoring: %s. ", rvalue);
c0b34696 451 return 0;
034c6ed7
LP
452 }
453
454 if (priority < PRIO_MIN || priority >= PRIO_MAX) {
e8e581bf
ZJS
455 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
456 "Nice priority out of range, ignoring: %s", rvalue);
c0b34696 457 return 0;
034c6ed7
LP
458 }
459
fb33a393 460 c->nice = priority;
71155933 461 c->nice_set = true;
fb33a393 462
034c6ed7
LP
463 return 0;
464}
465
e8e581bf
ZJS
466int config_parse_exec_oom_score_adjust(const char* unit,
467 const char *filename,
468 unsigned line,
469 const char *section,
71a61510 470 unsigned section_line,
e8e581bf
ZJS
471 const char *lvalue,
472 int ltype,
473 const char *rvalue,
474 void *data,
475 void *userdata) {
034c6ed7 476
fb33a393 477 ExecContext *c = data;
e8e581bf 478 int oa, r;
034c6ed7
LP
479
480 assert(filename);
481 assert(lvalue);
482 assert(rvalue);
483 assert(data);
484
e8e581bf
ZJS
485 r = safe_atoi(rvalue, &oa);
486 if (r < 0) {
487 log_syntax(unit, LOG_ERR, filename, line, -r,
488 "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
c0b34696 489 return 0;
034c6ed7
LP
490 }
491
dd6c17b1 492 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
e8e581bf
ZJS
493 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
494 "OOM score adjust value out of range, ignoring: %s", rvalue);
c0b34696 495 return 0;
034c6ed7
LP
496 }
497
dd6c17b1
LP
498 c->oom_score_adjust = oa;
499 c->oom_score_adjust_set = true;
fb33a393 500
034c6ed7
LP
501 return 0;
502}
503
e8e581bf
ZJS
504int config_parse_exec(const char *unit,
505 const char *filename,
506 unsigned line,
507 const char *section,
71a61510 508 unsigned section_line,
e8e581bf
ZJS
509 const char *lvalue,
510 int ltype,
511 const char *rvalue,
512 void *data,
513 void *userdata) {
034c6ed7 514
61e5d8ed
LP
515 ExecCommand **e = data, *nce;
516 char *path, **n;
034c6ed7 517 unsigned k;
7f110ff9 518 int r;
034c6ed7
LP
519
520 assert(filename);
521 assert(lvalue);
522 assert(rvalue);
61e5d8ed 523 assert(e);
034c6ed7 524
74051b9b
LP
525 e += ltype;
526
527 if (isempty(rvalue)) {
528 /* An empty assignment resets the list */
529 exec_command_free_list(*e);
530 *e = NULL;
531 return 0;
532 }
533
6c666e26
LP
534 /* We accept an absolute path as first argument, or
535 * alternatively an absolute prefixed with @ to allow
536 * overriding of argv[0]. */
61e5d8ed 537 for (;;) {
0f67f1ef 538 int i;
a2a5291b 539 const char *word, *state;
61e5d8ed 540 size_t l;
b708e7ce 541 bool honour_argv0 = false, ignore = false;
6c666e26 542
61e5d8ed
LP
543 path = NULL;
544 nce = NULL;
545 n = NULL;
6c666e26 546
61e5d8ed 547 rvalue += strspn(rvalue, WHITESPACE);
034c6ed7 548
61e5d8ed
LP
549 if (rvalue[0] == 0)
550 break;
034c6ed7 551
0f67f1ef
ZJS
552 for (i = 0; i < 2; i++) {
553 if (rvalue[0] == '-' && !ignore) {
554 ignore = true;
555 rvalue ++;
556 }
b708e7ce 557
0f67f1ef
ZJS
558 if (rvalue[0] == '@' && !honour_argv0) {
559 honour_argv0 = true;
560 rvalue ++;
561 }
b708e7ce 562 }
61e5d8ed 563
b708e7ce 564 if (*rvalue != '/') {
e8e581bf
ZJS
565 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
566 "Executable path is not absolute, ignoring: %s", rvalue);
c0b34696 567 return 0;
6c666e26 568 }
034c6ed7 569
61e5d8ed 570 k = 0;
a2a5291b
ZJS
571 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
572 if (strneq(word, ";", MAX(l, 1U)))
b2fadec6 573 goto found;
034c6ed7 574
61e5d8ed
LP
575 k++;
576 }
b2fadec6
ZJS
577 if (!isempty(state)) {
578 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
579 "Trailing garbage, ignoring.");
580 return 0;
581 }
034c6ed7 582
b2fadec6 583 found:
7f110ff9
LP
584 n = new(char*, k + !honour_argv0);
585 if (!n)
74051b9b 586 return log_oom();
61e5d8ed
LP
587
588 k = 0;
a2a5291b
ZJS
589 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
590 if (strneq(word, ";", MAX(l, 1U)))
61e5d8ed 591 break;
a2a5291b
ZJS
592 else if (strneq(word, "\\;", MAX(l, 1U)))
593 word ++;
61e5d8ed 594
a2a5291b 595 if (honour_argv0 && word == rvalue) {
b708e7ce 596 assert(!path);
7f110ff9 597
a2a5291b 598 path = strndup(word, l);
7f110ff9 599 if (!path) {
74051b9b 600 r = log_oom();
7f110ff9
LP
601 goto fail;
602 }
603
604 if (!utf8_is_valid(path)) {
b5d74213 605 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
7f110ff9 606 r = 0;
61e5d8ed 607 goto fail;
7f110ff9
LP
608 }
609
61e5d8ed 610 } else {
7f110ff9
LP
611 char *c;
612
a2a5291b 613 c = n[k++] = cunescape_length(word, l);
7f110ff9 614 if (!c) {
74051b9b 615 r = log_oom();
61e5d8ed 616 goto fail;
7f110ff9
LP
617 }
618
619 if (!utf8_is_valid(c)) {
b5d74213 620 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
7f110ff9
LP
621 r = 0;
622 goto fail;
623 }
61e5d8ed
LP
624 }
625 }
626
627 n[k] = NULL;
628
629 if (!n[0]) {
e8e581bf
ZJS
630 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
631 "Invalid command line, ignoring: %s", rvalue);
7f110ff9
LP
632 r = 0;
633 goto fail;
61e5d8ed
LP
634 }
635
7f110ff9
LP
636 if (!path) {
637 path = strdup(n[0]);
638 if (!path) {
74051b9b 639 r = log_oom();
61e5d8ed 640 goto fail;
7f110ff9
LP
641 }
642 }
6c666e26 643
61e5d8ed 644 assert(path_is_absolute(path));
6c666e26 645
7f110ff9
LP
646 nce = new0(ExecCommand, 1);
647 if (!nce) {
74051b9b 648 r = log_oom();
61e5d8ed 649 goto fail;
7f110ff9 650 }
61e5d8ed
LP
651
652 nce->argv = n;
653 nce->path = path;
b708e7ce 654 nce->ignore = ignore;
034c6ed7 655
61e5d8ed 656 path_kill_slashes(nce->path);
034c6ed7 657
61e5d8ed 658 exec_command_append_list(e, nce);
01f78473 659
61e5d8ed
LP
660 rvalue = state;
661 }
034c6ed7
LP
662
663 return 0;
664
665fail:
6c666e26
LP
666 n[k] = NULL;
667 strv_free(n);
668 free(path);
034c6ed7
LP
669 free(nce);
670
7f110ff9 671 return r;
034c6ed7
LP
672}
673
f975e971
LP
674DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
675DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
034c6ed7 676
e8e581bf
ZJS
677int config_parse_socket_bindtodevice(const char* unit,
678 const char *filename,
679 unsigned line,
680 const char *section,
71a61510 681 unsigned section_line,
e8e581bf
ZJS
682 const char *lvalue,
683 int ltype,
684 const char *rvalue,
685 void *data,
686 void *userdata) {
acbb0225
LP
687
688 Socket *s = data;
689 char *n;
690
691 assert(filename);
692 assert(lvalue);
693 assert(rvalue);
694 assert(data);
695
696 if (rvalue[0] && !streq(rvalue, "*")) {
74051b9b
LP
697 n = strdup(rvalue);
698 if (!n)
699 return log_oom();
acbb0225
LP
700 } else
701 n = NULL;
702
703 free(s->bind_to_device);
704 s->bind_to_device = n;
705
706 return 0;
707}
708
f975e971
LP
709DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
710DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
87f0e418 711
e8e581bf
ZJS
712int config_parse_exec_io_class(const char *unit,
713 const char *filename,
714 unsigned line,
715 const char *section,
71a61510 716 unsigned section_line,
e8e581bf
ZJS
717 const char *lvalue,
718 int ltype,
719 const char *rvalue,
720 void *data,
721 void *userdata) {
94f04347
LP
722
723 ExecContext *c = data;
724 int x;
725
726 assert(filename);
727 assert(lvalue);
728 assert(rvalue);
729 assert(data);
730
f8b69d1d
MS
731 x = ioprio_class_from_string(rvalue);
732 if (x < 0) {
e8e581bf
ZJS
733 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
734 "Failed to parse IO scheduling class, ignoring: %s", rvalue);
c0b34696 735 return 0;
0d87eb42 736 }
94f04347
LP
737
738 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
739 c->ioprio_set = true;
740
741 return 0;
742}
743
e8e581bf
ZJS
744int config_parse_exec_io_priority(const char *unit,
745 const char *filename,
746 unsigned line,
747 const char *section,
71a61510 748 unsigned section_line,
e8e581bf
ZJS
749 const char *lvalue,
750 int ltype,
751 const char *rvalue,
752 void *data,
753 void *userdata) {
94f04347
LP
754
755 ExecContext *c = data;
e8e581bf 756 int i, r;
94f04347
LP
757
758 assert(filename);
759 assert(lvalue);
760 assert(rvalue);
761 assert(data);
762
e8e581bf
ZJS
763 r = safe_atoi(rvalue, &i);
764 if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
765 log_syntax(unit, LOG_ERR, filename, line, -r,
766 "Failed to parse IO priority, ignoring: %s", rvalue);
c0b34696 767 return 0;
071830ff
LP
768 }
769
94f04347
LP
770 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
771 c->ioprio_set = true;
772
071830ff
LP
773 return 0;
774}
775
e8e581bf
ZJS
776int config_parse_exec_cpu_sched_policy(const char *unit,
777 const char *filename,
778 unsigned line,
779 const char *section,
71a61510 780 unsigned section_line,
e8e581bf
ZJS
781 const char *lvalue,
782 int ltype,
783 const char *rvalue,
784 void *data,
785 void *userdata) {
9eba9da4 786
94f04347
LP
787
788 ExecContext *c = data;
789 int x;
790
791 assert(filename);
792 assert(lvalue);
793 assert(rvalue);
794 assert(data);
795
f8b69d1d
MS
796 x = sched_policy_from_string(rvalue);
797 if (x < 0) {
e8e581bf
ZJS
798 log_syntax(unit, LOG_ERR, filename, line, -x,
799 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 800 return 0;
0d87eb42 801 }
94f04347
LP
802
803 c->cpu_sched_policy = x;
bb112710
HHPF
804 /* Moving to or from real-time policy? We need to adjust the priority */
805 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
94f04347
LP
806 c->cpu_sched_set = true;
807
808 return 0;
809}
810
e8e581bf
ZJS
811int config_parse_exec_cpu_sched_prio(const char *unit,
812 const char *filename,
813 unsigned line,
814 const char *section,
71a61510 815 unsigned section_line,
e8e581bf
ZJS
816 const char *lvalue,
817 int ltype,
818 const char *rvalue,
819 void *data,
820 void *userdata) {
9eba9da4
LP
821
822 ExecContext *c = data;
e8e581bf 823 int i, min, max, r;
9eba9da4
LP
824
825 assert(filename);
826 assert(lvalue);
827 assert(rvalue);
828 assert(data);
829
e8e581bf
ZJS
830 r = safe_atoi(rvalue, &i);
831 if (r < 0) {
832 log_syntax(unit, LOG_ERR, filename, line, -r,
833 "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 834 return 0;
94f04347 835 }
9eba9da4 836
bb112710
HHPF
837 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
838 min = sched_get_priority_min(c->cpu_sched_policy);
839 max = sched_get_priority_max(c->cpu_sched_policy);
840
841 if (i < min || i > max) {
e8e581bf
ZJS
842 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
843 "CPU scheduling priority is out of range, ignoring: %s", rvalue);
bb112710
HHPF
844 return 0;
845 }
846
94f04347
LP
847 c->cpu_sched_priority = i;
848 c->cpu_sched_set = true;
849
850 return 0;
851}
852
e8e581bf
ZJS
853int config_parse_exec_cpu_affinity(const char *unit,
854 const char *filename,
855 unsigned line,
856 const char *section,
71a61510 857 unsigned section_line,
e8e581bf
ZJS
858 const char *lvalue,
859 int ltype,
860 const char *rvalue,
861 void *data,
862 void *userdata) {
94f04347
LP
863
864 ExecContext *c = data;
a2a5291b 865 const char *word, *state;
94f04347 866 size_t l;
94f04347
LP
867
868 assert(filename);
869 assert(lvalue);
870 assert(rvalue);
871 assert(data);
872
74051b9b
LP
873 if (isempty(rvalue)) {
874 /* An empty assignment resets the CPU list */
875 if (c->cpuset)
876 CPU_FREE(c->cpuset);
877 c->cpuset = NULL;
878 return 0;
879 }
880
a2a5291b 881 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
7fd1b19b 882 _cleanup_free_ char *t = NULL;
94f04347
LP
883 int r;
884 unsigned cpu;
885
a2a5291b 886 t = strndup(word, l);
c040936b 887 if (!t)
74051b9b 888 return log_oom();
94f04347 889
487393e9 890 r = safe_atou(t, &cpu);
487393e9 891
c040936b
ZJS
892 if (!c->cpuset) {
893 c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
894 if (!c->cpuset)
74051b9b 895 return log_oom();
c040936b 896 }
82c121a4 897
82c121a4 898 if (r < 0 || cpu >= c->cpuset_ncpus) {
e8e581bf
ZJS
899 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
900 "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
c0b34696 901 return 0;
9eba9da4 902 }
94f04347 903
82c121a4 904 CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
9eba9da4 905 }
b2fadec6
ZJS
906 if (!isempty(state))
907 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
908 "Trailing garbage, ignoring.");
9eba9da4 909
94f04347
LP
910 return 0;
911}
912
e8e581bf
ZJS
913int config_parse_exec_capabilities(const char *unit,
914 const char *filename,
915 unsigned line,
916 const char *section,
71a61510 917 unsigned section_line,
e8e581bf
ZJS
918 const char *lvalue,
919 int ltype,
920 const char *rvalue,
921 void *data,
922 void *userdata) {
94f04347
LP
923
924 ExecContext *c = data;
925 cap_t cap;
926
927 assert(filename);
928 assert(lvalue);
929 assert(rvalue);
930 assert(data);
931
74051b9b
LP
932 cap = cap_from_text(rvalue);
933 if (!cap) {
e8e581bf
ZJS
934 log_syntax(unit, LOG_ERR, filename, line, errno,
935 "Failed to parse capabilities, ignoring: %s", rvalue);
c0b34696 936 return 0;
94f04347
LP
937 }
938
939 if (c->capabilities)
940 cap_free(c->capabilities);
941 c->capabilities = cap;
942
943 return 0;
944}
945
e8e581bf
ZJS
946int config_parse_exec_secure_bits(const char *unit,
947 const char *filename,
948 unsigned line,
949 const char *section,
71a61510 950 unsigned section_line,
e8e581bf
ZJS
951 const char *lvalue,
952 int ltype,
953 const char *rvalue,
954 void *data,
955 void *userdata) {
94f04347
LP
956
957 ExecContext *c = data;
94f04347 958 size_t l;
a2a5291b 959 const char *word, *state;
94f04347
LP
960
961 assert(filename);
962 assert(lvalue);
963 assert(rvalue);
964 assert(data);
965
74051b9b
LP
966 if (isempty(rvalue)) {
967 /* An empty assignment resets the field */
968 c->secure_bits = 0;
969 return 0;
970 }
971
a2a5291b
ZJS
972 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
973 if (first_word(word, "keep-caps"))
cbb21cca 974 c->secure_bits |= 1<<SECURE_KEEP_CAPS;
a2a5291b 975 else if (first_word(word, "keep-caps-locked"))
cbb21cca 976 c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
a2a5291b 977 else if (first_word(word, "no-setuid-fixup"))
cbb21cca 978 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
a2a5291b 979 else if (first_word(word, "no-setuid-fixup-locked"))
cbb21cca 980 c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
a2a5291b 981 else if (first_word(word, "noroot"))
cbb21cca 982 c->secure_bits |= 1<<SECURE_NOROOT;
a2a5291b 983 else if (first_word(word, "noroot-locked"))
cbb21cca 984 c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
9eba9da4 985 else {
e8e581bf
ZJS
986 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
987 "Failed to parse secure bits, ignoring: %s", rvalue);
c0b34696 988 return 0;
9eba9da4
LP
989 }
990 }
b2fadec6
ZJS
991 if (!isempty(state))
992 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
993 "Invalid syntax, garbage at the end, ignoring.");
9eba9da4 994
94f04347
LP
995 return 0;
996}
997
e8e581bf
ZJS
998int config_parse_bounding_set(const char *unit,
999 const char *filename,
1000 unsigned line,
1001 const char *section,
71a61510 1002 unsigned section_line,
e8e581bf
ZJS
1003 const char *lvalue,
1004 int ltype,
1005 const char *rvalue,
1006 void *data,
1007 void *userdata) {
94f04347 1008
ec8927ca 1009 uint64_t *capability_bounding_set_drop = data;
a2a5291b 1010 const char *word, *state;
94f04347 1011 size_t l;
260abb78
LP
1012 bool invert = false;
1013 uint64_t sum = 0;
94f04347
LP
1014
1015 assert(filename);
1016 assert(lvalue);
1017 assert(rvalue);
1018 assert(data);
1019
260abb78
LP
1020 if (rvalue[0] == '~') {
1021 invert = true;
1022 rvalue++;
1023 }
1024
1025 /* Note that we store this inverted internally, since the
1026 * kernel wants it like this. But we actually expose it
1027 * non-inverted everywhere to have a fully normalized
1028 * interface. */
1029
a2a5291b 1030 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
7fd1b19b 1031 _cleanup_free_ char *t = NULL;
94f04347
LP
1032 int r;
1033 cap_value_t cap;
1034
a2a5291b 1035 t = strndup(word, l);
ec8927ca 1036 if (!t)
74051b9b 1037 return log_oom();
94f04347
LP
1038
1039 r = cap_from_name(t, &cap);
94f04347 1040 if (r < 0) {
e8e581bf
ZJS
1041 log_syntax(unit, LOG_ERR, filename, line, errno,
1042 "Failed to parse capability in bounding set, ignoring: %s", t);
8351ceae 1043 continue;
94f04347
LP
1044 }
1045
260abb78 1046 sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
94f04347 1047 }
b2fadec6
ZJS
1048 if (!isempty(state))
1049 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1050 "Trailing garbage, ignoring.");
9eba9da4 1051
260abb78 1052 if (invert)
ec8927ca 1053 *capability_bounding_set_drop |= sum;
260abb78 1054 else
ec8927ca 1055 *capability_bounding_set_drop |= ~sum;
260abb78 1056
9eba9da4
LP
1057 return 0;
1058}
1059
e8e581bf
ZJS
1060int config_parse_limit(const char *unit,
1061 const char *filename,
1062 unsigned line,
1063 const char *section,
71a61510 1064 unsigned section_line,
e8e581bf
ZJS
1065 const char *lvalue,
1066 int ltype,
1067 const char *rvalue,
1068 void *data,
1069 void *userdata) {
94f04347
LP
1070
1071 struct rlimit **rl = data;
1072 unsigned long long u;
94f04347
LP
1073
1074 assert(filename);
1075 assert(lvalue);
1076 assert(rvalue);
1077 assert(data);
1078
f975e971
LP
1079 rl += ltype;
1080
3d57c6ab
LP
1081 if (streq(rvalue, "infinity"))
1082 u = (unsigned long long) RLIM_INFINITY;
e8e581bf
ZJS
1083 else {
1084 int r;
1085
1086 r = safe_atollu(rvalue, &u);
1087 if (r < 0) {
1088 log_syntax(unit, LOG_ERR, filename, line, -r,
1089 "Failed to parse resource value, ignoring: %s", rvalue);
1090 return 0;
1091 }
94f04347
LP
1092 }
1093
74051b9b
LP
1094 if (!*rl) {
1095 *rl = new(struct rlimit, 1);
1096 if (!*rl)
1097 return log_oom();
1098 }
9eba9da4 1099
94f04347 1100 (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
9eba9da4
LP
1101 return 0;
1102}
1103
07459bb6 1104#ifdef HAVE_SYSV_COMPAT
e8e581bf
ZJS
1105int config_parse_sysv_priority(const char *unit,
1106 const char *filename,
1107 unsigned line,
1108 const char *section,
71a61510 1109 unsigned section_line,
e8e581bf
ZJS
1110 const char *lvalue,
1111 int ltype,
1112 const char *rvalue,
1113 void *data,
1114 void *userdata) {
a9a1e00a
LP
1115
1116 int *priority = data;
e8e581bf 1117 int i, r;
a9a1e00a
LP
1118
1119 assert(filename);
1120 assert(lvalue);
1121 assert(rvalue);
1122 assert(data);
1123
e8e581bf
ZJS
1124 r = safe_atoi(rvalue, &i);
1125 if (r < 0 || i < 0) {
1126 log_syntax(unit, LOG_ERR, filename, line, -r,
1127 "Failed to parse SysV start priority, ignoring: %s", rvalue);
c0b34696 1128 return 0;
a9a1e00a
LP
1129 }
1130
1131 *priority = (int) i;
1132 return 0;
1133}
07459bb6 1134#endif
a9a1e00a 1135
f975e971 1136DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
50159e6a 1137
e8e581bf
ZJS
1138int config_parse_kill_signal(const char *unit,
1139 const char *filename,
1140 unsigned line,
1141 const char *section,
71a61510 1142 unsigned section_line,
e8e581bf
ZJS
1143 const char *lvalue,
1144 int ltype,
1145 const char *rvalue,
1146 void *data,
1147 void *userdata) {
2e22afe9
LP
1148
1149 int *sig = data;
1150 int r;
1151
1152 assert(filename);
1153 assert(lvalue);
1154 assert(rvalue);
1155 assert(sig);
1156
74051b9b
LP
1157 r = signal_from_string_try_harder(rvalue);
1158 if (r <= 0) {
e8e581bf
ZJS
1159 log_syntax(unit, LOG_ERR, filename, line, -r,
1160 "Failed to parse kill signal, ignoring: %s", rvalue);
c0b34696 1161 return 0;
2e22afe9
LP
1162 }
1163
1164 *sig = r;
1165 return 0;
1166}
1167
e8e581bf
ZJS
1168int config_parse_exec_mount_flags(const char *unit,
1169 const char *filename,
1170 unsigned line,
1171 const char *section,
71a61510 1172 unsigned section_line,
e8e581bf
ZJS
1173 const char *lvalue,
1174 int ltype,
1175 const char *rvalue,
1176 void *data,
1177 void *userdata) {
15ae422b
LP
1178
1179 ExecContext *c = data;
a2a5291b 1180 const char *word, *state;
15ae422b 1181 size_t l;
15ae422b
LP
1182 unsigned long flags = 0;
1183
1184 assert(filename);
1185 assert(lvalue);
1186 assert(rvalue);
1187 assert(data);
1188
a2a5291b 1189 FOREACH_WORD_SEPARATOR(word, l, rvalue, ", ", state) {
7fd1b19b 1190 _cleanup_free_ char *t;
ac97e2c5 1191
a2a5291b 1192 t = strndup(word, l);
ac97e2c5 1193 if (!t)
74051b9b 1194 return log_oom();
ac97e2c5
ZJS
1195
1196 if (streq(t, "shared"))
c2c13f2d 1197 flags = MS_SHARED;
ac97e2c5 1198 else if (streq(t, "slave"))
c2c13f2d 1199 flags = MS_SLAVE;
a2a5291b 1200 else if (streq(word, "private"))
c2c13f2d 1201 flags = MS_PRIVATE;
15ae422b 1202 else {
1e2fd62d
ZJS
1203 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1204 "Failed to parse mount flag %s, ignoring: %s", t, rvalue);
c0b34696 1205 return 0;
15ae422b
LP
1206 }
1207 }
b2fadec6
ZJS
1208 if (!isempty(state))
1209 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1210 "Trailing garbage, ignoring.");
15ae422b
LP
1211
1212 c->mount_flags = flags;
1213 return 0;
1214}
1215
5f8640fb
LP
1216int config_parse_exec_selinux_context(
1217 const char *unit,
1218 const char *filename,
1219 unsigned line,
1220 const char *section,
1221 unsigned section_line,
1222 const char *lvalue,
1223 int ltype,
1224 const char *rvalue,
1225 void *data,
1226 void *userdata) {
1227
1228 ExecContext *c = data;
1229 Unit *u = userdata;
1230 bool ignore;
1231 char *k;
1232 int r;
1233
1234 assert(filename);
1235 assert(lvalue);
1236 assert(rvalue);
1237 assert(data);
1238
1239 if (isempty(rvalue)) {
1240 free(c->selinux_context);
1241 c->selinux_context = NULL;
1242 c->selinux_context_ignore = false;
1243 return 0;
1244 }
1245
1246 if (rvalue[0] == '-') {
1247 ignore = true;
1248 rvalue++;
1249 } else
1250 ignore = false;
1251
1252 r = unit_name_printf(u, rvalue, &k);
1253 if (r < 0) {
1e2fd62d
ZJS
1254 log_syntax(unit, LOG_ERR, filename, line, -r,
1255 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
5f8640fb
LP
1256 return 0;
1257 }
1258
1259 free(c->selinux_context);
1260 c->selinux_context = k;
1261 c->selinux_context_ignore = ignore;
1262
1263 return 0;
1264}
1265
eef65bf3
MS
1266int config_parse_exec_apparmor_profile(
1267 const char *unit,
1268 const char *filename,
1269 unsigned line,
1270 const char *section,
1271 unsigned section_line,
1272 const char *lvalue,
1273 int ltype,
1274 const char *rvalue,
1275 void *data,
1276 void *userdata) {
1277
1278 ExecContext *c = data;
1279 Unit *u = userdata;
1280 bool ignore;
1281 char *k;
1282 int r;
1283
1284 assert(filename);
1285 assert(lvalue);
1286 assert(rvalue);
1287 assert(data);
1288
1289 if (isempty(rvalue)) {
1290 free(c->apparmor_profile);
1291 c->apparmor_profile = NULL;
1292 c->apparmor_profile_ignore = false;
1293 return 0;
1294 }
1295
1296 if (rvalue[0] == '-') {
1297 ignore = true;
1298 rvalue++;
1299 } else
1300 ignore = false;
1301
1302 r = unit_name_printf(u, rvalue, &k);
1303 if (r < 0) {
1e2fd62d
ZJS
1304 log_syntax(unit, LOG_ERR, filename, line, -r,
1305 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
eef65bf3
MS
1306 return 0;
1307 }
1308
1309 free(c->apparmor_profile);
1310 c->apparmor_profile = k;
1311 c->apparmor_profile_ignore = ignore;
1312
1313 return 0;
1314}
1315
e8e581bf
ZJS
1316int config_parse_timer(const char *unit,
1317 const char *filename,
1318 unsigned line,
1319 const char *section,
71a61510 1320 unsigned section_line,
e8e581bf
ZJS
1321 const char *lvalue,
1322 int ltype,
1323 const char *rvalue,
1324 void *data,
1325 void *userdata) {
871d7de4
LP
1326
1327 Timer *t = data;
36697dc0 1328 usec_t u = 0;
871d7de4
LP
1329 TimerValue *v;
1330 TimerBase b;
36697dc0 1331 CalendarSpec *c = NULL;
871d7de4
LP
1332
1333 assert(filename);
1334 assert(lvalue);
1335 assert(rvalue);
1336 assert(data);
1337
74051b9b
LP
1338 if (isempty(rvalue)) {
1339 /* Empty assignment resets list */
1340 timer_free_values(t);
1341 return 0;
1342 }
1343
36697dc0
LP
1344 b = timer_base_from_string(lvalue);
1345 if (b < 0) {
e8e581bf
ZJS
1346 log_syntax(unit, LOG_ERR, filename, line, -b,
1347 "Failed to parse timer base, ignoring: %s", lvalue);
c0b34696 1348 return 0;
871d7de4
LP
1349 }
1350
36697dc0
LP
1351 if (b == TIMER_CALENDAR) {
1352 if (calendar_spec_from_string(rvalue, &c) < 0) {
e8e581bf
ZJS
1353 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1354 "Failed to parse calendar specification, ignoring: %s",
1355 rvalue);
36697dc0
LP
1356 return 0;
1357 }
36697dc0 1358 } else {
7f602784 1359 if (parse_sec(rvalue, &u) < 0) {
e8e581bf
ZJS
1360 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1361 "Failed to parse timer value, ignoring: %s",
1362 rvalue);
36697dc0
LP
1363 return 0;
1364 }
871d7de4
LP
1365 }
1366
36697dc0 1367 v = new0(TimerValue, 1);
4d5e13a1
TA
1368 if (!v) {
1369 if (c)
eed93867 1370 calendar_spec_free(c);
74051b9b 1371 return log_oom();
4d5e13a1 1372 }
871d7de4
LP
1373
1374 v->base = b;
1375 v->value = u;
36697dc0 1376 v->calendar_spec = c;
871d7de4 1377
71fda00f 1378 LIST_PREPEND(value, t->values, v);
871d7de4
LP
1379
1380 return 0;
1381}
1382
3ecaa09b
LP
1383int config_parse_trigger_unit(
1384 const char *unit,
1385 const char *filename,
1386 unsigned line,
1387 const char *section,
71a61510 1388 unsigned section_line,
3ecaa09b
LP
1389 const char *lvalue,
1390 int ltype,
1391 const char *rvalue,
1392 void *data,
1393 void *userdata) {
871d7de4 1394
74051b9b 1395 _cleanup_free_ char *p = NULL;
3ecaa09b
LP
1396 Unit *u = data;
1397 UnitType type;
1398 int r;
398ef8ba
LP
1399
1400 assert(filename);
1401 assert(lvalue);
1402 assert(rvalue);
1403 assert(data);
1404
3ecaa09b
LP
1405 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
1406 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1407 "Multiple units to trigger specified, ignoring: %s", rvalue);
1408 return 0;
1409 }
871d7de4 1410
19f6d710
LP
1411 r = unit_name_printf(u, rvalue, &p);
1412 if (r < 0)
1413 log_syntax(unit, LOG_ERR, filename, line, -r,
1414 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
74051b9b 1415
19f6d710 1416 type = unit_name_to_type(p ?: rvalue);
3ecaa09b 1417 if (type < 0) {
e8e581bf 1418 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3ecaa09b 1419 "Unit type not valid, ignoring: %s", rvalue);
c0b34696 1420 return 0;
871d7de4
LP
1421 }
1422
3ecaa09b
LP
1423 if (type == u->type) {
1424 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1425 "Trigger cannot be of same type, ignoring: %s", rvalue);
1426 return 0;
1427 }
1428
19f6d710 1429 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
57020a3a 1430 if (r < 0) {
e8e581bf 1431 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 1432 "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
c0b34696 1433 return 0;
871d7de4
LP
1434 }
1435
1436 return 0;
1437}
1438
e8e581bf
ZJS
1439int config_parse_path_spec(const char *unit,
1440 const char *filename,
1441 unsigned line,
1442 const char *section,
71a61510 1443 unsigned section_line,
e8e581bf
ZJS
1444 const char *lvalue,
1445 int ltype,
1446 const char *rvalue,
1447 void *data,
1448 void *userdata) {
01f78473
LP
1449
1450 Path *p = data;
1451 PathSpec *s;
1452 PathType b;
7fd1b19b 1453 _cleanup_free_ char *k = NULL;
19f6d710 1454 int r;
01f78473
LP
1455
1456 assert(filename);
1457 assert(lvalue);
1458 assert(rvalue);
1459 assert(data);
1460
74051b9b
LP
1461 if (isempty(rvalue)) {
1462 /* Empty assignment clears list */
1463 path_free_specs(p);
1464 return 0;
1465 }
1466
93e4c84b
LP
1467 b = path_type_from_string(lvalue);
1468 if (b < 0) {
e8e581bf
ZJS
1469 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1470 "Failed to parse path type, ignoring: %s", lvalue);
c0b34696 1471 return 0;
01f78473
LP
1472 }
1473
19f6d710
LP
1474 r = unit_full_printf(UNIT(p), rvalue, &k);
1475 if (r < 0) {
487060c2
LP
1476 k = strdup(rvalue);
1477 if (!k)
1478 return log_oom();
1479 else
19f6d710 1480 log_syntax(unit, LOG_ERR, filename, line, -r,
e8e581bf
ZJS
1481 "Failed to resolve unit specifiers on %s. Ignoring.",
1482 rvalue);
487060c2 1483 }
93e4c84b
LP
1484
1485 if (!path_is_absolute(k)) {
e8e581bf
ZJS
1486 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1487 "Path is not absolute, ignoring: %s", k);
c0b34696 1488 return 0;
01f78473
LP
1489 }
1490
93e4c84b 1491 s = new0(PathSpec, 1);
543295ad 1492 if (!s)
93e4c84b 1493 return log_oom();
01f78473 1494
718db961 1495 s->unit = UNIT(p);
93e4c84b 1496 s->path = path_kill_slashes(k);
543295ad 1497 k = NULL;
01f78473
LP
1498 s->type = b;
1499 s->inotify_fd = -1;
1500
71fda00f 1501 LIST_PREPEND(spec, p->specs, s);
01f78473
LP
1502
1503 return 0;
1504}
1505
e8e581bf
ZJS
1506int config_parse_socket_service(const char *unit,
1507 const char *filename,
1508 unsigned line,
1509 const char *section,
71a61510 1510 unsigned section_line,
e8e581bf
ZJS
1511 const char *lvalue,
1512 int ltype,
1513 const char *rvalue,
1514 void *data,
1515 void *userdata) {
d9ff321a 1516
718db961 1517 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
d9ff321a
LP
1518 Socket *s = data;
1519 int r;
4ff77f66 1520 Unit *x;
74051b9b 1521 _cleanup_free_ char *p = NULL;
d9ff321a
LP
1522
1523 assert(filename);
1524 assert(lvalue);
1525 assert(rvalue);
1526 assert(data);
1527
19f6d710 1528 r = unit_name_printf(UNIT(s), rvalue, &p);
613b411c 1529 if (r < 0) {
1e2fd62d
ZJS
1530 log_syntax(unit, LOG_ERR, filename, line, -r,
1531 "Failed to resolve specifiers, ignoring: %s", rvalue);
613b411c
LP
1532 return 0;
1533 }
74051b9b 1534
613b411c 1535 if (!endswith(p, ".service")) {
1e2fd62d
ZJS
1536 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1537 "Unit must be of type service, ignoring: %s", rvalue);
d9ff321a
LP
1538 return 0;
1539 }
1540
613b411c 1541 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
4ff77f66 1542 if (r < 0) {
1e2fd62d
ZJS
1543 log_syntax(unit, LOG_ERR, filename, line, -r,
1544 "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
d9ff321a
LP
1545 return 0;
1546 }
1547
4ff77f66
LP
1548 unit_ref_set(&s->service, x);
1549
d9ff321a
LP
1550 return 0;
1551}
1552
e8e581bf
ZJS
1553int config_parse_service_sockets(const char *unit,
1554 const char *filename,
1555 unsigned line,
1556 const char *section,
71a61510 1557 unsigned section_line,
e8e581bf
ZJS
1558 const char *lvalue,
1559 int ltype,
1560 const char *rvalue,
1561 void *data,
1562 void *userdata) {
f976f3f6
LP
1563
1564 Service *s = data;
1565 int r;
a2a5291b 1566 const char *word, *state;
f976f3f6
LP
1567 size_t l;
1568
1569 assert(filename);
1570 assert(lvalue);
1571 assert(rvalue);
1572 assert(data);
1573
a2a5291b 1574 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
7fd1b19b 1575 _cleanup_free_ char *t = NULL, *k = NULL;
f976f3f6 1576
a2a5291b 1577 t = strndup(word, l);
57020a3a 1578 if (!t)
74051b9b 1579 return log_oom();
f976f3f6 1580
19f6d710
LP
1581 r = unit_name_printf(UNIT(s), t, &k);
1582 if (r < 0)
1583 log_syntax(unit, LOG_ERR, filename, line, -r,
1584 "Failed to resolve specifiers, ignoring: %s", strerror(-r));
57020a3a 1585
19f6d710 1586 if (!endswith(k ?: t, ".socket")) {
e8e581bf 1587 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
19f6d710 1588 "Unit must be of type socket, ignoring: %s", k ?: t);
f976f3f6
LP
1589 continue;
1590 }
1591
19f6d710 1592 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
57020a3a 1593 if (r < 0)
e8e581bf
ZJS
1594 log_syntax(unit, LOG_ERR, filename, line, -r,
1595 "Failed to add dependency on %s, ignoring: %s",
19f6d710 1596 k ?: t, strerror(-r));
f976f3f6 1597
19f6d710 1598 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
57020a3a 1599 if (r < 0)
f976f3f6
LP
1600 return r;
1601 }
b2fadec6
ZJS
1602 if (!isempty(state))
1603 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1604 "Trailing garbage, ignoring.");
f976f3f6
LP
1605
1606 return 0;
1607}
1608
e8e581bf
ZJS
1609int config_parse_service_timeout(const char *unit,
1610 const char *filename,
1611 unsigned line,
1612 const char *section,
71a61510 1613 unsigned section_line,
e8e581bf
ZJS
1614 const char *lvalue,
1615 int ltype,
1616 const char *rvalue,
1617 void *data,
1618 void *userdata) {
98709151
LN
1619
1620 Service *s = userdata;
1621 int r;
1622
1623 assert(filename);
1624 assert(lvalue);
1625 assert(rvalue);
1626 assert(s);
1627
71a61510 1628 r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 1629 rvalue, data, userdata);
74051b9b 1630 if (r < 0)
d568a335 1631 return r;
98709151 1632
d568a335
MS
1633 if (streq(lvalue, "TimeoutSec")) {
1634 s->start_timeout_defined = true;
1635 s->timeout_stop_usec = s->timeout_start_usec;
1636 } else if (streq(lvalue, "TimeoutStartSec"))
1637 s->start_timeout_defined = true;
1638
1639 return 0;
98709151
LN
1640}
1641
e821075a
LP
1642int config_parse_busname_service(
1643 const char *unit,
1644 const char *filename,
1645 unsigned line,
1646 const char *section,
1647 unsigned section_line,
1648 const char *lvalue,
1649 int ltype,
1650 const char *rvalue,
1651 void *data,
1652 void *userdata) {
1653
1654 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1655 BusName *n = data;
1656 int r;
1657 Unit *x;
1658 _cleanup_free_ char *p = NULL;
1659
1660 assert(filename);
1661 assert(lvalue);
1662 assert(rvalue);
1663 assert(data);
1664
1665 r = unit_name_printf(UNIT(n), rvalue, &p);
1666 if (r < 0) {
1e2fd62d
ZJS
1667 log_syntax(unit, LOG_ERR, filename, line, -r,
1668 "Failed to resolve specifiers, ignoring: %s", rvalue);
e821075a
LP
1669 return 0;
1670 }
1671
1672 if (!endswith(p, ".service")) {
1e2fd62d
ZJS
1673 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1674 "Unit must be of type service, ignoring: %s", rvalue);
e821075a
LP
1675 return 0;
1676 }
1677
1678 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
1679 if (r < 0) {
1e2fd62d
ZJS
1680 log_syntax(unit, LOG_ERR, filename, line, -r,
1681 "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
e821075a
LP
1682 return 0;
1683 }
1684
1685 unit_ref_set(&n->service, x);
1686
1687 return 0;
1688}
1689
5369c77d 1690DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world, bus_policy_access, BusPolicyAccess, "Failed to parse bus name policy access");
a4152e3f 1691
54d76c92
DM
1692int config_parse_bus_policy(
1693 const char *unit,
1694 const char *filename,
1695 unsigned line,
1696 const char *section,
1697 unsigned section_line,
1698 const char *lvalue,
1699 int ltype,
1700 const char *rvalue,
1701 void *data,
1702 void *userdata) {
1703
1704 _cleanup_free_ BusNamePolicy *p = NULL;
1705 _cleanup_free_ char *id_str = NULL;
1706 BusName *busname = data;
1707 char *access_str;
54d76c92
DM
1708
1709 assert(filename);
1710 assert(lvalue);
1711 assert(rvalue);
1712 assert(data);
1713
1714 p = new0(BusNamePolicy, 1);
1715 if (!p)
1716 return log_oom();
1717
1718 if (streq(lvalue, "AllowUser"))
1719 p->type = BUSNAME_POLICY_TYPE_USER;
1720 else if (streq(lvalue, "AllowGroup"))
1721 p->type = BUSNAME_POLICY_TYPE_GROUP;
54d76c92
DM
1722 else
1723 assert_not_reached("Unknown lvalue");
1724
1725 id_str = strdup(rvalue);
1726 if (!id_str)
1727 return log_oom();
1728
a4152e3f
LP
1729 access_str = strpbrk(id_str, WHITESPACE);
1730 if (!access_str) {
1e2fd62d
ZJS
1731 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1732 "Invalid busname policy value '%s'", rvalue);
a4152e3f 1733 return 0;
54d76c92
DM
1734 }
1735
a4152e3f
LP
1736 *access_str = '\0';
1737 access_str++;
1738 access_str += strspn(access_str, WHITESPACE);
1739
5369c77d 1740 p->access = bus_policy_access_from_string(access_str);
54d76c92 1741 if (p->access < 0) {
1e2fd62d
ZJS
1742 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1743 "Invalid busname policy access type '%s'", access_str);
54d76c92
DM
1744 return 0;
1745 }
1746
a4152e3f
LP
1747 p->name = id_str;
1748 id_str = NULL;
1749
54d76c92
DM
1750 LIST_PREPEND(policy, busname->policy, p);
1751 p = NULL;
1752
1753 return 0;
1754}
1755
50199623
DM
1756int config_parse_bus_endpoint_policy(
1757 const char *unit,
1758 const char *filename,
1759 unsigned line,
1760 const char *section,
1761 unsigned section_line,
1762 const char *lvalue,
1763 int ltype,
1764 const char *rvalue,
1765 void *data,
1766 void *userdata) {
1767
1768 _cleanup_free_ char *name = NULL;
1769 BusPolicyAccess access;
1770 ExecContext *c = data;
1771 char *access_str;
1772 int r;
1773
1774 assert(filename);
1775 assert(lvalue);
1776 assert(rvalue);
1777 assert(data);
1778
1779 name = strdup(rvalue);
1780 if (!name)
1781 return log_oom();
1782
1783 access_str = strpbrk(name, WHITESPACE);
1784 if (!access_str) {
1785 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1786 "Invalid endpoint policy value '%s'", rvalue);
1787 return 0;
1788 }
1789
1790 *access_str = '\0';
1791 access_str++;
1792 access_str += strspn(access_str, WHITESPACE);
1793
1794 access = bus_policy_access_from_string(access_str);
1795 if (access <= _BUS_POLICY_ACCESS_INVALID ||
1796 access >= _BUS_POLICY_ACCESS_MAX) {
1797 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1798 "Invalid endpoint policy access type '%s'", access_str);
1799 return 0;
1800 }
1801
1802 if (!c->bus_endpoint) {
1803 r = bus_endpoint_new(&c->bus_endpoint);
1804
1805 if (r < 0)
1806 return r;
1807 }
1808
1809 return bus_endpoint_add_policy(c->bus_endpoint, name, access);
1810}
1811
e8e581bf
ZJS
1812int config_parse_unit_env_file(const char *unit,
1813 const char *filename,
1814 unsigned line,
1815 const char *section,
71a61510 1816 unsigned section_line,
e8e581bf
ZJS
1817 const char *lvalue,
1818 int ltype,
1819 const char *rvalue,
1820 void *data,
1821 void *userdata) {
ddb26e18 1822
853b8397 1823 char ***env = data;
8fef7659 1824 Unit *u = userdata;
19f6d710
LP
1825 _cleanup_free_ char *n = NULL;
1826 const char *s;
853b8397 1827 int r;
ddb26e18
LP
1828
1829 assert(filename);
1830 assert(lvalue);
1831 assert(rvalue);
1832 assert(data);
1833
74051b9b
LP
1834 if (isempty(rvalue)) {
1835 /* Empty assignment frees the list */
74051b9b
LP
1836 strv_free(*env);
1837 *env = NULL;
1838 return 0;
1839 }
1840
19f6d710
LP
1841 r = unit_full_printf(u, rvalue, &n);
1842 if (r < 0)
1e2fd62d 1843 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 1844 "Failed to resolve specifiers, ignoring: %s", rvalue);
8fef7659 1845
19f6d710 1846 s = n ?: rvalue;
8fef7659 1847 if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
e8e581bf
ZJS
1848 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1849 "Path '%s' is not absolute, ignoring.", s);
afe4bfe2
LP
1850 return 0;
1851 }
1852
853b8397
LP
1853 r = strv_extend(env, s);
1854 if (r < 0)
1855 return log_oom();
1856
1857 return 0;
1858}
1859
e8e581bf
ZJS
1860int config_parse_environ(const char *unit,
1861 const char *filename,
1862 unsigned line,
1863 const char *section,
71a61510 1864 unsigned section_line,
e8e581bf
ZJS
1865 const char *lvalue,
1866 int ltype,
1867 const char *rvalue,
1868 void *data,
1869 void *userdata) {
853b8397
LP
1870
1871 Unit *u = userdata;
a2a5291b
ZJS
1872 char*** env = data;
1873 const char *word, *state;
853b8397
LP
1874 size_t l;
1875 _cleanup_free_ char *k = NULL;
19f6d710 1876 int r;
853b8397
LP
1877
1878 assert(filename);
1879 assert(lvalue);
1880 assert(rvalue);
97d0e5f8 1881 assert(data);
853b8397
LP
1882
1883 if (isempty(rvalue)) {
1884 /* Empty assignment resets the list */
1885 strv_free(*env);
1886 *env = NULL;
1887 return 0;
1888 }
1889
19f6d710
LP
1890 if (u) {
1891 r = unit_full_printf(u, rvalue, &k);
1892 if (r < 0)
1893 log_syntax(unit, LOG_ERR, filename, line, -r,
1894 "Failed to resolve specifiers, ignoring: %s", rvalue);
1895 }
97d0e5f8 1896
19f6d710
LP
1897 if (!k)
1898 k = strdup(rvalue);
8fef7659 1899 if (!k)
74051b9b 1900 return log_oom();
ddb26e18 1901
a2a5291b 1902 FOREACH_WORD_QUOTED(word, l, k, state) {
853b8397
LP
1903 _cleanup_free_ char *n;
1904 char **x;
1905
a2a5291b 1906 n = cunescape_length(word, l);
853b8397
LP
1907 if (!n)
1908 return log_oom();
1909
1910 if (!env_assignment_is_valid(n)) {
e8e581bf
ZJS
1911 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1912 "Invalid environment assignment, ignoring: %s", rvalue);
853b8397
LP
1913 continue;
1914 }
1915
1916 x = strv_env_set(*env, n);
1917 if (!x)
1918 return log_oom();
1919
1920 strv_free(*env);
1921 *env = x;
1922 }
b2fadec6
ZJS
1923 if (!isempty(state))
1924 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1925 "Trailing garbage, ignoring.");
ddb26e18 1926
8c7be95e 1927 return 0;
ddb26e18
LP
1928}
1929
e8e581bf
ZJS
1930int config_parse_ip_tos(const char *unit,
1931 const char *filename,
1932 unsigned line,
1933 const char *section,
71a61510 1934 unsigned section_line,
e8e581bf
ZJS
1935 const char *lvalue,
1936 int ltype,
1937 const char *rvalue,
1938 void *data,
1939 void *userdata) {
4fd5948e
LP
1940
1941 int *ip_tos = data, x;
4fd5948e
LP
1942
1943 assert(filename);
1944 assert(lvalue);
1945 assert(rvalue);
1946 assert(data);
1947
f8b69d1d
MS
1948 x = ip_tos_from_string(rvalue);
1949 if (x < 0) {
e8e581bf
ZJS
1950 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
1951 "Failed to parse IP TOS value, ignoring: %s", rvalue);
f8b69d1d
MS
1952 return 0;
1953 }
4fd5948e
LP
1954
1955 *ip_tos = x;
1956 return 0;
1957}
1958
e8e581bf
ZJS
1959int config_parse_unit_condition_path(const char *unit,
1960 const char *filename,
1961 unsigned line,
1962 const char *section,
71a61510 1963 unsigned section_line,
e8e581bf
ZJS
1964 const char *lvalue,
1965 int ltype,
1966 const char *rvalue,
1967 void *data,
1968 void *userdata) {
52661efd 1969
2b583ce6 1970 ConditionType cond = ltype;
52661efd 1971 Unit *u = data;
267632f0 1972 bool trigger, negate;
52661efd 1973 Condition *c;
2fbe635a 1974 _cleanup_free_ char *p = NULL;
19f6d710 1975 int r;
52661efd
LP
1976
1977 assert(filename);
1978 assert(lvalue);
1979 assert(rvalue);
1980 assert(data);
1981
74051b9b
LP
1982 if (isempty(rvalue)) {
1983 /* Empty assignment resets the list */
1984 condition_free_list(u->conditions);
1985 u->conditions = NULL;
1986 return 0;
1987 }
1988
ab7f148f
LP
1989 trigger = rvalue[0] == '|';
1990 if (trigger)
267632f0
LP
1991 rvalue++;
1992
ab7f148f
LP
1993 negate = rvalue[0] == '!';
1994 if (negate)
52661efd
LP
1995 rvalue++;
1996
19f6d710
LP
1997 r = unit_full_printf(u, rvalue, &p);
1998 if (r < 0)
1999 log_syntax(unit, LOG_ERR, filename, line, -r,
2000 "Failed to resolve specifiers, ignoring: %s", rvalue);
2001 if (!p) {
2002 p = strdup(rvalue);
2003 if (!p)
2004 return log_oom();
2005 }
095b2d7a
AK
2006
2007 if (!path_is_absolute(p)) {
e8e581bf
ZJS
2008 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2009 "Path in condition not absolute, ignoring: %s", p);
52661efd
LP
2010 return 0;
2011 }
2012
095b2d7a 2013 c = condition_new(cond, p, trigger, negate);
ab7f148f 2014 if (!c)
74051b9b 2015 return log_oom();
52661efd 2016
71fda00f 2017 LIST_PREPEND(conditions, u->conditions, c);
52661efd
LP
2018 return 0;
2019}
2020
e8e581bf
ZJS
2021int config_parse_unit_condition_string(const char *unit,
2022 const char *filename,
2023 unsigned line,
2024 const char *section,
71a61510 2025 unsigned section_line,
e8e581bf
ZJS
2026 const char *lvalue,
2027 int ltype,
2028 const char *rvalue,
2029 void *data,
2030 void *userdata) {
039655a4 2031
41584525 2032 ConditionType cond = ltype;
039655a4 2033 Unit *u = data;
267632f0 2034 bool trigger, negate;
039655a4 2035 Condition *c;
2fbe635a 2036 _cleanup_free_ char *s = NULL;
19f6d710 2037 int r;
039655a4
LP
2038
2039 assert(filename);
2040 assert(lvalue);
2041 assert(rvalue);
2042 assert(data);
2043
74051b9b
LP
2044 if (isempty(rvalue)) {
2045 /* Empty assignment resets the list */
2046 condition_free_list(u->conditions);
2047 u->conditions = NULL;
2048 return 0;
2049 }
2050
c0d6e764
LP
2051 trigger = rvalue[0] == '|';
2052 if (trigger)
267632f0
LP
2053 rvalue++;
2054
c0d6e764
LP
2055 negate = rvalue[0] == '!';
2056 if (negate)
039655a4
LP
2057 rvalue++;
2058
19f6d710
LP
2059 r = unit_full_printf(u, rvalue, &s);
2060 if (r < 0)
2061 log_syntax(unit, LOG_ERR, filename, line, -r,
2062 "Failed to resolve specifiers, ignoring: %s", rvalue);
2063 if (!s) {
2064 s = strdup(rvalue);
2065 if (!s)
2066 return log_oom();
2067 }
095b2d7a
AK
2068
2069 c = condition_new(cond, s, trigger, negate);
c0d6e764
LP
2070 if (!c)
2071 return log_oom();
039655a4 2072
71fda00f 2073 LIST_PREPEND(conditions, u->conditions, c);
039655a4
LP
2074 return 0;
2075}
2076
e8e581bf
ZJS
2077int config_parse_unit_condition_null(const char *unit,
2078 const char *filename,
2079 unsigned line,
2080 const char *section,
71a61510 2081 unsigned section_line,
e8e581bf
ZJS
2082 const char *lvalue,
2083 int ltype,
2084 const char *rvalue,
2085 void *data,
2086 void *userdata) {
d257ddef
LP
2087
2088 Unit *u = data;
2089 Condition *c;
267632f0 2090 bool trigger, negate;
d257ddef
LP
2091 int b;
2092
2093 assert(filename);
2094 assert(lvalue);
2095 assert(rvalue);
2096 assert(data);
2097
74051b9b
LP
2098 if (isempty(rvalue)) {
2099 /* Empty assignment resets the list */
2100 condition_free_list(u->conditions);
2101 u->conditions = NULL;
2102 return 0;
2103 }
2104
2105 trigger = rvalue[0] == '|';
2106 if (trigger)
267632f0
LP
2107 rvalue++;
2108
74051b9b
LP
2109 negate = rvalue[0] == '!';
2110 if (negate)
d257ddef
LP
2111 rvalue++;
2112
74051b9b
LP
2113 b = parse_boolean(rvalue);
2114 if (b < 0) {
e8e581bf
ZJS
2115 log_syntax(unit, LOG_ERR, filename, line, -b,
2116 "Failed to parse boolean value in condition, ignoring: %s",
2117 rvalue);
d257ddef
LP
2118 return 0;
2119 }
2120
2121 if (!b)
2122 negate = !negate;
2123
74051b9b
LP
2124 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2125 if (!c)
2126 return log_oom();
d257ddef 2127
71fda00f 2128 LIST_PREPEND(conditions, u->conditions, c);
d257ddef
LP
2129 return 0;
2130}
2131
f975e971 2132DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
bf500566 2133DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action, failure_action, FailureAction, "Failed to parse failure action specifier");
c952c6ec 2134
a57f7e2c
LP
2135int config_parse_unit_requires_mounts_for(
2136 const char *unit,
2137 const char *filename,
2138 unsigned line,
2139 const char *section,
71a61510 2140 unsigned section_line,
a57f7e2c
LP
2141 const char *lvalue,
2142 int ltype,
2143 const char *rvalue,
2144 void *data,
2145 void *userdata) {
7c8fa05c
LP
2146
2147 Unit *u = userdata;
a2a5291b 2148 const char *word, *state;
a57f7e2c 2149 size_t l;
7c8fa05c
LP
2150
2151 assert(filename);
2152 assert(lvalue);
2153 assert(rvalue);
2154 assert(data);
2155
a2a5291b 2156 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
8a7935a2 2157 int r;
a57f7e2c
LP
2158 _cleanup_free_ char *n;
2159
a2a5291b 2160 n = strndup(word, l);
a57f7e2c
LP
2161 if (!n)
2162 return log_oom();
7c8fa05c 2163
a57f7e2c 2164 if (!utf8_is_valid(n)) {
b5d74213 2165 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
a57f7e2c
LP
2166 continue;
2167 }
7c8fa05c 2168
a57f7e2c
LP
2169 r = unit_require_mounts_for(u, n);
2170 if (r < 0) {
1e2fd62d 2171 log_syntax(unit, LOG_ERR, filename, line, -r,
a57f7e2c
LP
2172 "Failed to add required mount for, ignoring: %s", rvalue);
2173 continue;
2174 }
2175 }
b2fadec6
ZJS
2176 if (!isempty(state))
2177 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2178 "Trailing garbage, ignoring.");
7c8fa05c 2179
8a7935a2 2180 return 0;
7c8fa05c 2181}
9e372868 2182
e8e581bf
ZJS
2183int config_parse_documentation(const char *unit,
2184 const char *filename,
2185 unsigned line,
2186 const char *section,
71a61510 2187 unsigned section_line,
e8e581bf
ZJS
2188 const char *lvalue,
2189 int ltype,
2190 const char *rvalue,
2191 void *data,
2192 void *userdata) {
49dbfa7b
LP
2193
2194 Unit *u = userdata;
2195 int r;
2196 char **a, **b;
2197
2198 assert(filename);
2199 assert(lvalue);
2200 assert(rvalue);
2201 assert(u);
2202
74051b9b
LP
2203 if (isempty(rvalue)) {
2204 /* Empty assignment resets the list */
2205 strv_free(u->documentation);
2206 u->documentation = NULL;
2207 return 0;
2208 }
2209
71a61510 2210 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 2211 rvalue, data, userdata);
49dbfa7b
LP
2212 if (r < 0)
2213 return r;
2214
2215 for (a = b = u->documentation; a && *a; a++) {
2216
2217 if (is_valid_documentation_url(*a))
2218 *(b++) = *a;
2219 else {
e8e581bf
ZJS
2220 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2221 "Invalid URL, ignoring: %s", *a);
49dbfa7b
LP
2222 free(*a);
2223 }
2224 }
f6d2d421
ZJS
2225 if (b)
2226 *b = NULL;
49dbfa7b
LP
2227
2228 return r;
2229}
2230
c0467cf3 2231#ifdef HAVE_SECCOMP
17df7223
LP
2232int config_parse_syscall_filter(
2233 const char *unit,
2234 const char *filename,
2235 unsigned line,
2236 const char *section,
2237 unsigned section_line,
2238 const char *lvalue,
2239 int ltype,
2240 const char *rvalue,
2241 void *data,
2242 void *userdata) {
2243
2244 static const char default_syscalls[] =
2245 "execve\0"
2246 "exit\0"
2247 "exit_group\0"
2248 "rt_sigreturn\0"
2249 "sigreturn\0";
2250
8351ceae
LP
2251 ExecContext *c = data;
2252 Unit *u = userdata;
b5fb3789 2253 bool invert = false;
a2a5291b 2254 const char *word, *state;
8351ceae 2255 size_t l;
17df7223 2256 int r;
8351ceae
LP
2257
2258 assert(filename);
2259 assert(lvalue);
2260 assert(rvalue);
2261 assert(u);
2262
74051b9b
LP
2263 if (isempty(rvalue)) {
2264 /* Empty assignment resets the list */
17df7223
LP
2265 set_free(c->syscall_filter);
2266 c->syscall_filter = NULL;
2267 c->syscall_whitelist = false;
74051b9b
LP
2268 return 0;
2269 }
2270
8351ceae
LP
2271 if (rvalue[0] == '~') {
2272 invert = true;
2273 rvalue++;
2274 }
2275
17df7223 2276 if (!c->syscall_filter) {
d5099efc 2277 c->syscall_filter = set_new(NULL);
17df7223
LP
2278 if (!c->syscall_filter)
2279 return log_oom();
2280
c0467cf3 2281 if (invert)
17df7223
LP
2282 /* Allow everything but the ones listed */
2283 c->syscall_whitelist = false;
c0467cf3 2284 else {
17df7223
LP
2285 const char *i;
2286
2287 /* Allow nothing but the ones listed */
2288 c->syscall_whitelist = true;
8351ceae 2289
17df7223
LP
2290 /* Accept default syscalls if we are on a whitelist */
2291 NULSTR_FOREACH(i, default_syscalls) {
2292 int id;
8351ceae 2293
17df7223 2294 id = seccomp_syscall_resolve_name(i);
c0467cf3
RC
2295 if (id < 0)
2296 continue;
8351ceae 2297
17df7223
LP
2298 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2299 if (r == -EEXIST)
2300 continue;
2301 if (r < 0)
2302 return log_oom();
c0467cf3
RC
2303 }
2304 }
8351ceae
LP
2305 }
2306
a2a5291b 2307 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
7fd1b19b 2308 _cleanup_free_ char *t = NULL;
17df7223 2309 int id;
8351ceae 2310
a2a5291b 2311 t = strndup(word, l);
8351ceae 2312 if (!t)
74051b9b 2313 return log_oom();
8351ceae 2314
c0467cf3 2315 id = seccomp_syscall_resolve_name(t);
8351ceae 2316 if (id < 0) {
1e2fd62d
ZJS
2317 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2318 "Failed to parse system call, ignoring: %s", t);
8351ceae
LP
2319 continue;
2320 }
2321
17df7223
LP
2322 /* If we previously wanted to forbid a syscall and now
2323 * we want to allow it, then remove it from the list
c0467cf3 2324 */
17df7223
LP
2325 if (!invert == c->syscall_whitelist) {
2326 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2327 if (r == -EEXIST)
2328 continue;
2329 if (r < 0)
2330 return log_oom();
2331 } else
2332 set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
c0467cf3 2333 }
b2fadec6
ZJS
2334 if (!isempty(state))
2335 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2336 "Trailing garbage, ignoring.");
c0467cf3 2337
760b9d7c
LP
2338 /* Turn on NNP, but only if it wasn't configured explicitly
2339 * before, and only if we are in user mode. */
2340 if (!c->no_new_privileges_set && u->manager->running_as == SYSTEMD_USER)
2341 c->no_new_privileges = true;
17df7223
LP
2342
2343 return 0;
2344}
2345
57183d11
LP
2346int config_parse_syscall_archs(
2347 const char *unit,
2348 const char *filename,
2349 unsigned line,
2350 const char *section,
2351 unsigned section_line,
2352 const char *lvalue,
2353 int ltype,
2354 const char *rvalue,
2355 void *data,
2356 void *userdata) {
2357
d3b1c508 2358 Set **archs = data;
a2a5291b 2359 const char *word, *state;
57183d11
LP
2360 size_t l;
2361 int r;
2362
2363 if (isempty(rvalue)) {
d3b1c508
LP
2364 set_free(*archs);
2365 *archs = NULL;
57183d11
LP
2366 return 0;
2367 }
2368
d5099efc 2369 r = set_ensure_allocated(archs, NULL);
57183d11
LP
2370 if (r < 0)
2371 return log_oom();
2372
a2a5291b 2373 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
57183d11
LP
2374 _cleanup_free_ char *t = NULL;
2375 uint32_t a;
2376
a2a5291b 2377 t = strndup(word, l);
57183d11
LP
2378 if (!t)
2379 return log_oom();
2380
2381 r = seccomp_arch_from_string(t, &a);
2382 if (r < 0) {
1e2fd62d
ZJS
2383 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2384 "Failed to parse system call architecture, ignoring: %s", t);
57183d11
LP
2385 continue;
2386 }
2387
d3b1c508 2388 r = set_put(*archs, UINT32_TO_PTR(a + 1));
57183d11
LP
2389 if (r == -EEXIST)
2390 continue;
2391 if (r < 0)
2392 return log_oom();
2393 }
b2fadec6
ZJS
2394 if (!isempty(state))
2395 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2396 "Trailing garbage, ignoring.");
57183d11
LP
2397
2398 return 0;
2399}
2400
17df7223
LP
2401int config_parse_syscall_errno(
2402 const char *unit,
2403 const char *filename,
2404 unsigned line,
2405 const char *section,
2406 unsigned section_line,
2407 const char *lvalue,
2408 int ltype,
2409 const char *rvalue,
2410 void *data,
2411 void *userdata) {
2412
2413 ExecContext *c = data;
2414 int e;
2415
2416 assert(filename);
2417 assert(lvalue);
2418 assert(rvalue);
2419
2420 if (isempty(rvalue)) {
2421 /* Empty assignment resets to KILL */
2422 c->syscall_errno = 0;
2423 return 0;
8351ceae
LP
2424 }
2425
17df7223
LP
2426 e = errno_from_name(rvalue);
2427 if (e < 0) {
1e2fd62d
ZJS
2428 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2429 "Failed to parse error number, ignoring: %s", rvalue);
17df7223
LP
2430 return 0;
2431 }
8351ceae 2432
17df7223 2433 c->syscall_errno = e;
8351ceae
LP
2434 return 0;
2435}
4298d0b5
LP
2436
2437int config_parse_address_families(
2438 const char *unit,
2439 const char *filename,
2440 unsigned line,
2441 const char *section,
2442 unsigned section_line,
2443 const char *lvalue,
2444 int ltype,
2445 const char *rvalue,
2446 void *data,
2447 void *userdata) {
2448
2449 ExecContext *c = data;
2450 Unit *u = userdata;
2451 bool invert = false;
a2a5291b 2452 const char *word, *state;
4298d0b5
LP
2453 size_t l;
2454 int r;
2455
2456 assert(filename);
2457 assert(lvalue);
2458 assert(rvalue);
2459 assert(u);
2460
2461 if (isempty(rvalue)) {
2462 /* Empty assignment resets the list */
2463 set_free(c->address_families);
2464 c->address_families = NULL;
2465 c->address_families_whitelist = false;
2466 return 0;
2467 }
2468
2469 if (rvalue[0] == '~') {
2470 invert = true;
2471 rvalue++;
2472 }
2473
2474 if (!c->address_families) {
d5099efc 2475 c->address_families = set_new(NULL);
4298d0b5
LP
2476 if (!c->address_families)
2477 return log_oom();
2478
2479 c->address_families_whitelist = !invert;
2480 }
2481
a2a5291b 2482 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
4298d0b5
LP
2483 _cleanup_free_ char *t = NULL;
2484 int af;
2485
a2a5291b 2486 t = strndup(word, l);
4298d0b5
LP
2487 if (!t)
2488 return log_oom();
2489
2490 af = af_from_name(t);
2491 if (af <= 0) {
1e2fd62d
ZJS
2492 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2493 "Failed to parse address family, ignoring: %s", t);
4298d0b5
LP
2494 continue;
2495 }
2496
2497 /* If we previously wanted to forbid an address family and now
2498 * we want to allow it, then remove it from the list
2499 */
2500 if (!invert == c->address_families_whitelist) {
2501 r = set_put(c->address_families, INT_TO_PTR(af));
2502 if (r == -EEXIST)
2503 continue;
2504 if (r < 0)
2505 return log_oom();
2506 } else
2507 set_remove(c->address_families, INT_TO_PTR(af));
2508 }
b2fadec6
ZJS
2509 if (!isempty(state))
2510 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2511 "Trailing garbage, ignoring.");
4298d0b5
LP
2512
2513 return 0;
2514}
c0467cf3 2515#endif
8351ceae 2516
a016b922
LP
2517int config_parse_unit_slice(
2518 const char *unit,
2519 const char *filename,
2520 unsigned line,
2521 const char *section,
71a61510 2522 unsigned section_line,
a016b922
LP
2523 const char *lvalue,
2524 int ltype,
2525 const char *rvalue,
2526 void *data,
2527 void *userdata) {
2528
2529 _cleanup_free_ char *k = NULL;
2530 Unit *u = userdata, *slice;
2531 int r;
2532
2533 assert(filename);
2534 assert(lvalue);
2535 assert(rvalue);
2536 assert(u);
2537
19f6d710
LP
2538 r = unit_name_printf(u, rvalue, &k);
2539 if (r < 0)
2540 log_syntax(unit, LOG_ERR, filename, line, -r,
a016b922 2541 "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
19f6d710
LP
2542 if (!k) {
2543 k = strdup(rvalue);
2544 if (!k)
2545 return log_oom();
2546 }
a016b922 2547
19f6d710 2548 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
a016b922
LP
2549 if (r < 0) {
2550 log_syntax(unit, LOG_ERR, filename, line, -r,
19f6d710 2551 "Failed to load slice unit %s. Ignoring.", k);
a016b922
LP
2552 return 0;
2553 }
2554
2555 if (slice->type != UNIT_SLICE) {
2556 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
19f6d710 2557 "Slice unit %s is not a slice. Ignoring.", k);
a016b922
LP
2558 return 0;
2559 }
2560
2561 unit_ref_set(&u->slice, slice);
2562 return 0;
2563}
2564
4ad49000
LP
2565DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
2566
2567int config_parse_cpu_shares(
2568 const char *unit,
2569 const char *filename,
2570 unsigned line,
2571 const char *section,
71a61510 2572 unsigned section_line,
4ad49000
LP
2573 const char *lvalue,
2574 int ltype,
2575 const char *rvalue,
2576 void *data,
2577 void *userdata) {
2578
db785129 2579 unsigned long *shares = data, lu;
95ae05c0
WC
2580 int r;
2581
2582 assert(filename);
2583 assert(lvalue);
2584 assert(rvalue);
2585
2586 if (isempty(rvalue)) {
db785129 2587 *shares = (unsigned long) -1;
95ae05c0
WC
2588 return 0;
2589 }
2590
2591 r = safe_atolu(rvalue, &lu);
2592 if (r < 0 || lu <= 0) {
1e2fd62d
ZJS
2593 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2594 "CPU shares '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
2595 return 0;
2596 }
2597
db785129 2598 *shares = lu;
4ad49000
LP
2599 return 0;
2600}
2601
b2f8b02e
LP
2602int config_parse_cpu_quota(
2603 const char *unit,
2604 const char *filename,
2605 unsigned line,
2606 const char *section,
2607 unsigned section_line,
2608 const char *lvalue,
2609 int ltype,
2610 const char *rvalue,
2611 void *data,
2612 void *userdata) {
2613
2614 CGroupContext *c = data;
9a054909 2615 double percent;
b2f8b02e
LP
2616
2617 assert(filename);
2618 assert(lvalue);
2619 assert(rvalue);
2620
2621 if (isempty(rvalue)) {
3a43da28 2622 c->cpu_quota_per_sec_usec = USEC_INFINITY;
b2f8b02e
LP
2623 return 0;
2624 }
2625
9a054909 2626 if (!endswith(rvalue, "%")) {
b2f8b02e 2627
1e2fd62d
ZJS
2628 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2629 "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue);
9a054909
LP
2630 return 0;
2631 }
b2f8b02e 2632
9a054909 2633 if (sscanf(rvalue, "%lf%%", &percent) != 1 || percent <= 0) {
1e2fd62d
ZJS
2634 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2635 "CPU quota '%s' invalid. Ignoring.", rvalue);
9a054909 2636 return 0;
b2f8b02e
LP
2637 }
2638
9a054909
LP
2639 c->cpu_quota_per_sec_usec = (usec_t) (percent * USEC_PER_SEC / 100);
2640
b2f8b02e
LP
2641 return 0;
2642}
2643
4ad49000
LP
2644int config_parse_memory_limit(
2645 const char *unit,
2646 const char *filename,
2647 unsigned line,
2648 const char *section,
71a61510 2649 unsigned section_line,
4ad49000
LP
2650 const char *lvalue,
2651 int ltype,
2652 const char *rvalue,
2653 void *data,
2654 void *userdata) {
2655
2656 CGroupContext *c = data;
4ad49000
LP
2657 off_t bytes;
2658 int r;
2659
4ad49000 2660 if (isempty(rvalue)) {
ddca82ac 2661 c->memory_limit = (uint64_t) -1;
4ad49000
LP
2662 return 0;
2663 }
2664
2665 assert_cc(sizeof(uint64_t) == sizeof(off_t));
2666
5556b5fe 2667 r = parse_size(rvalue, 1024, &bytes);
4ad49000 2668 if (r < 0) {
1e2fd62d
ZJS
2669 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2670 "Memory limit '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
2671 return 0;
2672 }
2673
ddca82ac 2674 c->memory_limit = (uint64_t) bytes;
4ad49000
LP
2675 return 0;
2676}
2677
2678int config_parse_device_allow(
2679 const char *unit,
2680 const char *filename,
2681 unsigned line,
2682 const char *section,
71a61510 2683 unsigned section_line,
4ad49000
LP
2684 const char *lvalue,
2685 int ltype,
2686 const char *rvalue,
2687 void *data,
2688 void *userdata) {
2689
2690 _cleanup_free_ char *path = NULL;
2691 CGroupContext *c = data;
2692 CGroupDeviceAllow *a;
2693 const char *m;
2694 size_t n;
2695
2696 if (isempty(rvalue)) {
2697 while (c->device_allow)
2698 cgroup_context_free_device_allow(c, c->device_allow);
2699
2700 return 0;
2701 }
2702
2703 n = strcspn(rvalue, WHITESPACE);
2704 path = strndup(rvalue, n);
2705 if (!path)
2706 return log_oom();
2707
90060676
LP
2708 if (!startswith(path, "/dev/") &&
2709 !startswith(path, "block-") &&
2710 !startswith(path, "char-")) {
1e2fd62d
ZJS
2711 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2712 "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
2713 return 0;
2714 }
2715
2716 m = rvalue + n + strspn(rvalue + n, WHITESPACE);
2717 if (isempty(m))
2718 m = "rwm";
2719
2720 if (!in_charset(m, "rwm")) {
1e2fd62d
ZJS
2721 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2722 "Invalid device rights '%s'. Ignoring.", m);
4ad49000
LP
2723 return 0;
2724 }
2725
2726 a = new0(CGroupDeviceAllow, 1);
2727 if (!a)
2728 return log_oom();
2729
2730 a->path = path;
2731 path = NULL;
2732 a->r = !!strchr(m, 'r');
2733 a->w = !!strchr(m, 'w');
2734 a->m = !!strchr(m, 'm');
2735
71fda00f 2736 LIST_PREPEND(device_allow, c->device_allow, a);
4ad49000
LP
2737 return 0;
2738}
2739
2740int config_parse_blockio_weight(
2741 const char *unit,
2742 const char *filename,
2743 unsigned line,
2744 const char *section,
71a61510 2745 unsigned section_line,
4ad49000
LP
2746 const char *lvalue,
2747 int ltype,
2748 const char *rvalue,
2749 void *data,
2750 void *userdata) {
2751
db785129 2752 unsigned long *weight = data, lu;
95ae05c0
WC
2753 int r;
2754
2755 assert(filename);
2756 assert(lvalue);
2757 assert(rvalue);
2758
2759 if (isempty(rvalue)) {
db785129 2760 *weight = (unsigned long) -1;
95ae05c0
WC
2761 return 0;
2762 }
2763
2764 r = safe_atolu(rvalue, &lu);
2765 if (r < 0 || lu < 10 || lu > 1000) {
1e2fd62d
ZJS
2766 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2767 "Block IO weight '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
2768 return 0;
2769 }
2770
db785129 2771 *weight = lu;
8e7076ca
LP
2772 return 0;
2773}
2774
2775int config_parse_blockio_device_weight(
2776 const char *unit,
2777 const char *filename,
2778 unsigned line,
2779 const char *section,
71a61510 2780 unsigned section_line,
8e7076ca
LP
2781 const char *lvalue,
2782 int ltype,
2783 const char *rvalue,
2784 void *data,
2785 void *userdata) {
2786
4ad49000 2787 _cleanup_free_ char *path = NULL;
8e7076ca 2788 CGroupBlockIODeviceWeight *w;
4ad49000
LP
2789 CGroupContext *c = data;
2790 unsigned long lu;
2791 const char *weight;
2792 size_t n;
2793 int r;
2794
2795 assert(filename);
2796 assert(lvalue);
2797 assert(rvalue);
2798
2799 if (isempty(rvalue)) {
4ad49000
LP
2800 while (c->blockio_device_weights)
2801 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
2802
2803 return 0;
2804 }
2805
2806 n = strcspn(rvalue, WHITESPACE);
2807 weight = rvalue + n;
8e7076ca 2808 if (!*weight) {
1e2fd62d
ZJS
2809 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2810 "Expected block device and device weight. Ignoring.");
8e7076ca
LP
2811 return 0;
2812 }
4ad49000 2813
8e7076ca
LP
2814 path = strndup(rvalue, n);
2815 if (!path)
2816 return log_oom();
4ad49000 2817
8e7076ca 2818 if (!path_startswith(path, "/dev")) {
1e2fd62d
ZJS
2819 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2820 "Invalid device node path '%s'. Ignoring.", path);
8e7076ca
LP
2821 return 0;
2822 }
4ad49000 2823
8e7076ca 2824 weight += strspn(weight, WHITESPACE);
4ad49000
LP
2825 r = safe_atolu(weight, &lu);
2826 if (r < 0 || lu < 10 || lu > 1000) {
1e2fd62d
ZJS
2827 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2828 "Block IO weight '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
2829 return 0;
2830 }
2831
8e7076ca
LP
2832 w = new0(CGroupBlockIODeviceWeight, 1);
2833 if (!w)
2834 return log_oom();
4ad49000 2835
8e7076ca
LP
2836 w->path = path;
2837 path = NULL;
4ad49000 2838
8e7076ca 2839 w->weight = lu;
4ad49000 2840
71fda00f 2841 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
2842 return 0;
2843}
2844
2845int config_parse_blockio_bandwidth(
2846 const char *unit,
2847 const char *filename,
2848 unsigned line,
2849 const char *section,
71a61510 2850 unsigned section_line,
4ad49000
LP
2851 const char *lvalue,
2852 int ltype,
2853 const char *rvalue,
2854 void *data,
2855 void *userdata) {
2856
2857 _cleanup_free_ char *path = NULL;
2858 CGroupBlockIODeviceBandwidth *b;
2859 CGroupContext *c = data;
2860 const char *bandwidth;
2861 off_t bytes;
47c0980d 2862 bool read;
4ad49000
LP
2863 size_t n;
2864 int r;
2865
2866 assert(filename);
2867 assert(lvalue);
2868 assert(rvalue);
2869
47c0980d
G
2870 read = streq("BlockIOReadBandwidth", lvalue);
2871
4ad49000 2872 if (isempty(rvalue)) {
47c0980d
G
2873 CGroupBlockIODeviceBandwidth *next;
2874
2875 LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
2876 if (b->read == read)
2877 cgroup_context_free_blockio_device_bandwidth(c, b);
4ad49000
LP
2878
2879 return 0;
2880 }
2881
2882 n = strcspn(rvalue, WHITESPACE);
2883 bandwidth = rvalue + n;
2884 bandwidth += strspn(bandwidth, WHITESPACE);
2885
2886 if (!*bandwidth) {
2887 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2888 "Expected space separated pair of device node and bandwidth. Ignoring.");
2889 return 0;
2890 }
2891
2892 path = strndup(rvalue, n);
2893 if (!path)
2894 return log_oom();
2895
2896 if (!path_startswith(path, "/dev")) {
2897 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2898 "Invalid device node path '%s'. Ignoring.", path);
2899 return 0;
2900 }
2901
5556b5fe 2902 r = parse_size(bandwidth, 1000, &bytes);
4ad49000 2903 if (r < 0 || bytes <= 0) {
1e2fd62d
ZJS
2904 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2905 "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
2906 return 0;
2907 }
2908
2909 b = new0(CGroupBlockIODeviceBandwidth, 1);
2910 if (!b)
2911 return log_oom();
2912
2913 b->path = path;
2914 path = NULL;
2915 b->bandwidth = (uint64_t) bytes;
47c0980d 2916 b->read = read;
4ad49000 2917
71fda00f 2918 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
4ad49000
LP
2919
2920 return 0;
2921}
2922
d420282b
LP
2923DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
2924
2925int config_parse_job_mode_isolate(
2926 const char *unit,
2927 const char *filename,
2928 unsigned line,
2929 const char *section,
2930 unsigned section_line,
2931 const char *lvalue,
2932 int ltype,
2933 const char *rvalue,
2934 void *data,
2935 void *userdata) {
2936
2937 JobMode *m = data;
2938 int r;
2939
2940 assert(filename);
2941 assert(lvalue);
2942 assert(rvalue);
2943
2944 r = parse_boolean(rvalue);
2945 if (r < 0) {
1e2fd62d
ZJS
2946 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2947 "Failed to parse boolean, ignoring: %s", rvalue);
d420282b
LP
2948 return 0;
2949 }
2950
2951 *m = r ? JOB_ISOLATE : JOB_REPLACE;
2952 return 0;
2953}
2954
ac45f971
LP
2955int config_parse_personality(
2956 const char *unit,
2957 const char *filename,
2958 unsigned line,
2959 const char *section,
2960 unsigned section_line,
2961 const char *lvalue,
2962 int ltype,
2963 const char *rvalue,
2964 void *data,
2965 void *userdata) {
2966
2967 unsigned long *personality = data, p;
2968
2969 assert(filename);
2970 assert(lvalue);
2971 assert(rvalue);
2972 assert(personality);
2973
2974 p = personality_from_string(rvalue);
2975 if (p == 0xffffffffUL) {
2976 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2977 "Failed to parse personality, ignoring: %s", rvalue);
2978 return 0;
2979 }
2980
2981 *personality = p;
2982 return 0;
2983}
2984
e66cf1a3
LP
2985int config_parse_runtime_directory(
2986 const char *unit,
2987 const char *filename,
2988 unsigned line,
2989 const char *section,
2990 unsigned section_line,
2991 const char *lvalue,
2992 int ltype,
2993 const char *rvalue,
2994 void *data,
2995 void *userdata) {
2996
a2a5291b
ZJS
2997 char***rt = data;
2998 const char *word, *state;
e66cf1a3
LP
2999 size_t l;
3000 int r;
3001
3002 assert(filename);
3003 assert(lvalue);
3004 assert(rvalue);
3005 assert(data);
3006
3007 if (isempty(rvalue)) {
3008 /* Empty assignment resets the list */
3009 strv_free(*rt);
3010 *rt = NULL;
3011 return 0;
3012 }
3013
a2a5291b 3014 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
e66cf1a3
LP
3015 _cleanup_free_ char *n;
3016
a2a5291b 3017 n = strndup(word, l);
e66cf1a3
LP
3018 if (!n)
3019 return log_oom();
3020
3021 if (!filename_is_safe(n)) {
1e2fd62d
ZJS
3022 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3023 "Runtime directory is not valid, ignoring assignment: %s", rvalue);
e66cf1a3
LP
3024 continue;
3025 }
3026
3027 r = strv_push(rt, n);
3028 if (r < 0)
3029 return log_oom();
3030
3031 n = NULL;
3032 }
b2fadec6
ZJS
3033 if (!isempty(state))
3034 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3035 "Trailing garbage, ignoring.");
e66cf1a3
LP
3036
3037 return 0;
3038}
3039
3af00fb8
LP
3040int config_parse_set_status(
3041 const char *unit,
3042 const char *filename,
3043 unsigned line,
3044 const char *section,
3045 unsigned section_line,
3046 const char *lvalue,
3047 int ltype,
3048 const char *rvalue,
3049 void *data,
3050 void *userdata) {
3051
3af00fb8 3052 size_t l;
a2a5291b 3053 const char *word, *state;
3af00fb8
LP
3054 int r;
3055 ExitStatusSet *status_set = data;
3056
3057 assert(filename);
3058 assert(lvalue);
3059 assert(rvalue);
3060 assert(data);
3061
3e2d435b 3062 /* Empty assignment resets the list */
3af00fb8 3063 if (isempty(rvalue)) {
3e2d435b 3064 exit_status_set_free(status_set);
3af00fb8
LP
3065 return 0;
3066 }
3067
a2a5291b 3068 FOREACH_WORD(word, l, rvalue, state) {
3af00fb8
LP
3069 _cleanup_free_ char *temp;
3070 int val;
3071
a2a5291b 3072 temp = strndup(word, l);
3af00fb8
LP
3073 if (!temp)
3074 return log_oom();
3075
3076 r = safe_atoi(temp, &val);
3077 if (r < 0) {
3078 val = signal_from_string_try_harder(temp);
3079
1e2fd62d
ZJS
3080 if (val <= 0) {
3081 log_syntax(unit, LOG_ERR, filename, line, -val,
3082 "Failed to parse value, ignoring: %s", word);
3af00fb8
LP
3083 return 0;
3084 }
3085 } else {
1e2fd62d
ZJS
3086 if (val < 0 || val > 255) {
3087 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
3088 "Value %d is outside range 0-255, ignoring", val);
3089 continue;
3af00fb8
LP
3090 }
3091 }
1e2fd62d 3092
d5099efc 3093 r = set_ensure_allocated(&status_set->status, NULL);
1e2fd62d
ZJS
3094 if (r < 0)
3095 return log_oom();
3096
3097 r = set_put(status_set->status, INT_TO_PTR(val));
3098 if (r < 0) {
3099 log_syntax(unit, LOG_ERR, filename, line, -r,
3100 "Unable to store: %s", word);
3101 return r;
3102 }
3af00fb8 3103 }
b2fadec6
ZJS
3104 if (!isempty(state))
3105 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3106 "Trailing garbage, ignoring.");
3af00fb8
LP
3107
3108 return 0;
3109}
3110
94828d2d
LP
3111int config_parse_namespace_path_strv(
3112 const char *unit,
3113 const char *filename,
3114 unsigned line,
3115 const char *section,
3116 unsigned section_line,
3117 const char *lvalue,
3118 int ltype,
3119 const char *rvalue,
3120 void *data,
3121 void *userdata) {
3122
a2a5291b
ZJS
3123 char*** sv = data;
3124 const char *word, *state;
94828d2d
LP
3125 size_t l;
3126 int r;
3127
3128 assert(filename);
3129 assert(lvalue);
3130 assert(rvalue);
3131 assert(data);
3132
3133 if (isempty(rvalue)) {
3134 /* Empty assignment resets the list */
3135 strv_free(*sv);
3136 *sv = NULL;
3137 return 0;
3138 }
3139
a2a5291b 3140 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
94828d2d
LP
3141 _cleanup_free_ char *n;
3142 int offset;
3143
a2a5291b 3144 n = strndup(word, l);
94828d2d
LP
3145 if (!n)
3146 return log_oom();
3147
3148 if (!utf8_is_valid(n)) {
b5d74213 3149 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
94828d2d
LP
3150 continue;
3151 }
3152
3153 offset = n[0] == '-';
3154 if (!path_is_absolute(n + offset)) {
1e2fd62d
ZJS
3155 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3156 "Not an absolute path, ignoring: %s", rvalue);
94828d2d
LP
3157 continue;
3158 }
3159
3160 path_kill_slashes(n);
3161
3162 r = strv_push(sv, n);
3163 if (r < 0)
3164 return log_oom();
3165
3166 n = NULL;
3167 }
b2fadec6
ZJS
3168 if (!isempty(state))
3169 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
3170 "Trailing garbage, ignoring.");
94828d2d
LP
3171
3172 return 0;
3173}
3174
f1721625 3175int config_parse_no_new_privileges(
760b9d7c
LP
3176 const char* unit,
3177 const char *filename,
3178 unsigned line,
3179 const char *section,
3180 unsigned section_line,
3181 const char *lvalue,
3182 int ltype,
3183 const char *rvalue,
3184 void *data,
3185 void *userdata) {
3186
3187 ExecContext *c = data;
3188 int k;
3189
3190 assert(filename);
3191 assert(lvalue);
3192 assert(rvalue);
3193 assert(data);
3194
3195 k = parse_boolean(rvalue);
3196 if (k < 0) {
1e2fd62d
ZJS
3197 log_syntax(unit, LOG_ERR, filename, line, -k,
3198 "Failed to parse boolean value, ignoring: %s", rvalue);
760b9d7c
LP
3199 return 0;
3200 }
3201
3202 c->no_new_privileges = !!k;
3203 c->no_new_privileges_set = true;
3204
3205 return 0;
3206}
3207
1b8689f9 3208int config_parse_protect_home(
417116f2
LP
3209 const char* unit,
3210 const char *filename,
3211 unsigned line,
3212 const char *section,
3213 unsigned section_line,
3214 const char *lvalue,
3215 int ltype,
3216 const char *rvalue,
3217 void *data,
3218 void *userdata) {
3219
3220 ExecContext *c = data;
3221 int k;
3222
3223 assert(filename);
3224 assert(lvalue);
3225 assert(rvalue);
3226 assert(data);
3227
3228 /* Our enum shall be a superset of booleans, hence first try
3229 * to parse as as boolean, and then as enum */
3230
3231 k = parse_boolean(rvalue);
3232 if (k > 0)
1b8689f9 3233 c->protect_home = PROTECT_HOME_YES;
417116f2 3234 else if (k == 0)
1b8689f9 3235 c->protect_home = PROTECT_HOME_NO;
417116f2 3236 else {
1b8689f9 3237 ProtectHome h;
417116f2 3238
1b8689f9 3239 h = protect_home_from_string(rvalue);
417116f2 3240 if (h < 0){
1e2fd62d
ZJS
3241 log_syntax(unit, LOG_ERR, filename, line, -h,
3242 "Failed to parse protect home value, ignoring: %s", rvalue);
417116f2
LP
3243 return 0;
3244 }
3245
1b8689f9
LP
3246 c->protect_home = h;
3247 }
3248
3249 return 0;
3250}
3251
3252int config_parse_protect_system(
3253 const char* unit,
3254 const char *filename,
3255 unsigned line,
3256 const char *section,
3257 unsigned section_line,
3258 const char *lvalue,
3259 int ltype,
3260 const char *rvalue,
3261 void *data,
3262 void *userdata) {
3263
3264 ExecContext *c = data;
3265 int k;
3266
3267 assert(filename);
3268 assert(lvalue);
3269 assert(rvalue);
3270 assert(data);
3271
3272 /* Our enum shall be a superset of booleans, hence first try
3273 * to parse as as boolean, and then as enum */
3274
3275 k = parse_boolean(rvalue);
3276 if (k > 0)
3277 c->protect_system = PROTECT_SYSTEM_YES;
3278 else if (k == 0)
3279 c->protect_system = PROTECT_SYSTEM_NO;
3280 else {
3281 ProtectSystem s;
3282
3283 s = protect_system_from_string(rvalue);
3284 if (s < 0){
1e2fd62d
ZJS
3285 log_syntax(unit, LOG_ERR, filename, line, -s,
3286 "Failed to parse protect system value, ignoring: %s", rvalue);
1b8689f9
LP
3287 return 0;
3288 }
3289
3290 c->protect_system = s;
417116f2
LP
3291 }
3292
3293 return 0;
3294}
3295
071830ff 3296#define FOLLOW_MAX 8
87f0e418 3297
9e2f7c11 3298static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
0301abf4 3299 unsigned c = 0;
87f0e418
LP
3300 int fd, r;
3301 FILE *f;
0301abf4 3302 char *id = NULL;
87f0e418
LP
3303
3304 assert(filename);
3305 assert(*filename);
3306 assert(_f);
3307 assert(names);
3308
0301abf4
LP
3309 /* This will update the filename pointer if the loaded file is
3310 * reached by a symlink. The old string will be freed. */
87f0e418 3311
0301abf4 3312 for (;;) {
2c7108c4 3313 char *target, *name;
87f0e418 3314
0301abf4
LP
3315 if (c++ >= FOLLOW_MAX)
3316 return -ELOOP;
3317
b08d03ff
LP
3318 path_kill_slashes(*filename);
3319
87f0e418 3320 /* Add the file name we are currently looking at to
8f05424d
LP
3321 * the names of this unit, but only if it is a valid
3322 * unit name. */
2b6bf07d 3323 name = basename(*filename);
87f0e418 3324
f78e6385 3325 if (unit_name_is_valid(name, TEMPLATE_VALID)) {
8f05424d 3326
15e11d81
LP
3327 id = set_get(names, name);
3328 if (!id) {
3329 id = strdup(name);
3330 if (!id)
8f05424d 3331 return -ENOMEM;
87f0e418 3332
ef42202a
ZJS
3333 r = set_consume(names, id);
3334 if (r < 0)
8f05424d 3335 return r;
87f0e418 3336 }
87f0e418
LP
3337 }
3338
0301abf4 3339 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
3340 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
3341 if (fd >= 0)
87f0e418
LP
3342 break;
3343
0301abf4
LP
3344 if (errno != ELOOP)
3345 return -errno;
3346
87f0e418 3347 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
3348 r = readlink_and_make_absolute(*filename, &target);
3349 if (r < 0)
0301abf4 3350 return r;
87f0e418 3351
0301abf4 3352 free(*filename);
2c7108c4 3353 *filename = target;
87f0e418
LP
3354 }
3355
9946996c
LP
3356 f = fdopen(fd, "re");
3357 if (!f) {
87f0e418 3358 r = -errno;
03e334a1 3359 safe_close(fd);
0301abf4 3360 return r;
87f0e418
LP
3361 }
3362
3363 *_f = f;
9e2f7c11 3364 *_final = id;
0301abf4 3365 return 0;
87f0e418
LP
3366}
3367
23a177ef
LP
3368static int merge_by_names(Unit **u, Set *names, const char *id) {
3369 char *k;
3370 int r;
3371
3372 assert(u);
3373 assert(*u);
3374 assert(names);
3375
3376 /* Let's try to add in all symlink names we found */
3377 while ((k = set_steal_first(names))) {
3378
3379 /* First try to merge in the other name into our
3380 * unit */
9946996c
LP
3381 r = unit_merge_by_name(*u, k);
3382 if (r < 0) {
23a177ef
LP
3383 Unit *other;
3384
3385 /* Hmm, we couldn't merge the other unit into
3386 * ours? Then let's try it the other way
3387 * round */
3388
ac155bb8 3389 other = manager_get_unit((*u)->manager, k);
23a177ef
LP
3390 free(k);
3391
9946996c
LP
3392 if (other) {
3393 r = unit_merge(other, *u);
3394 if (r >= 0) {
23a177ef
LP
3395 *u = other;
3396 return merge_by_names(u, names, NULL);
3397 }
9946996c 3398 }
23a177ef
LP
3399
3400 return r;
3401 }
3402
3403 if (id == k)
3404 unit_choose_id(*u, id);
3405
3406 free(k);
3407 }
3408
3409 return 0;
3410}
3411
e537352b 3412static int load_from_path(Unit *u, const char *path) {
0301abf4 3413 int r;
e48614c4
ZJS
3414 _cleanup_set_free_free_ Set *symlink_names = NULL;
3415 _cleanup_fclose_ FILE *f = NULL;
3416 _cleanup_free_ char *filename = NULL;
3417 char *id = NULL;
23a177ef 3418 Unit *merged;
45fb0699 3419 struct stat st;
23a177ef
LP
3420
3421 assert(u);
e537352b 3422 assert(path);
3efd4195 3423
d5099efc 3424 symlink_names = set_new(&string_hash_ops);
f975e971 3425 if (!symlink_names)
87f0e418 3426 return -ENOMEM;
3efd4195 3427
036643a2
LP
3428 if (path_is_absolute(path)) {
3429
9946996c 3430 filename = strdup(path);
e48614c4
ZJS
3431 if (!filename)
3432 return -ENOMEM;
036643a2 3433
9946996c
LP
3434 r = open_follow(&filename, &f, symlink_names, &id);
3435 if (r < 0) {
036643a2
LP
3436 free(filename);
3437 filename = NULL;
3438
3439 if (r != -ENOENT)
e48614c4 3440 return r;
036643a2
LP
3441 }
3442
3443 } else {
3444 char **p;
3445
ac155bb8 3446 STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
036643a2
LP
3447
3448 /* Instead of opening the path right away, we manually
3449 * follow all symlinks and add their name to our unit
3450 * name set while doing so */
9946996c 3451 filename = path_make_absolute(path, *p);
e48614c4
ZJS
3452 if (!filename)
3453 return -ENOMEM;
036643a2 3454
ac155bb8
MS
3455 if (u->manager->unit_path_cache &&
3456 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
3457 r = -ENOENT;
3458 else
3459 r = open_follow(&filename, &f, symlink_names, &id);
3460
3461 if (r < 0) {
036643a2
LP
3462 free(filename);
3463 filename = NULL;
3464
3465 if (r != -ENOENT)
e48614c4 3466 return r;
036643a2
LP
3467
3468 /* Empty the symlink names for the next run */
9946996c 3469 set_clear_free(symlink_names);
036643a2
LP
3470 continue;
3471 }
3472
3473 break;
3474 }
3475 }
034c6ed7 3476
e48614c4 3477 if (!filename)
8f05424d 3478 /* Hmm, no suitable file found? */
e48614c4 3479 return 0;
87f0e418 3480
23a177ef 3481 merged = u;
9946996c
LP
3482 r = merge_by_names(&merged, symlink_names, id);
3483 if (r < 0)
e48614c4 3484 return r;
87f0e418 3485
23a177ef 3486 if (merged != u) {
ac155bb8 3487 u->load_state = UNIT_MERGED;
e48614c4 3488 return 0;
034c6ed7
LP
3489 }
3490
e48614c4
ZJS
3491 if (fstat(fileno(f), &st) < 0)
3492 return -errno;
45fb0699 3493
00dc5d76 3494 if (null_or_empty(&st))
ac155bb8 3495 u->load_state = UNIT_MASKED;
00dc5d76 3496 else {
c2756a68
LP
3497 u->load_state = UNIT_LOADED;
3498
00dc5d76 3499 /* Now, parse the file contents */
36f822c4
ZJS
3500 r = config_parse(u->id, filename, f,
3501 UNIT_VTABLE(u)->sections,
3502 config_item_perf_lookup, load_fragment_gperf_lookup,
3503 false, true, false, u);
f975e971 3504 if (r < 0)
e48614c4 3505 return r;
00dc5d76 3506 }
b08d03ff 3507
ac155bb8
MS
3508 free(u->fragment_path);
3509 u->fragment_path = filename;
0301abf4 3510 filename = NULL;
87f0e418 3511
ac155bb8 3512 u->fragment_mtime = timespec_load(&st.st_mtim);
45fb0699 3513
1b64d026
LP
3514 if (u->source_path) {
3515 if (stat(u->source_path, &st) >= 0)
3516 u->source_mtime = timespec_load(&st.st_mtim);
3517 else
3518 u->source_mtime = 0;
3519 }
3520
e48614c4 3521 return 0;
0301abf4
LP
3522}
3523
e537352b 3524int unit_load_fragment(Unit *u) {
23a177ef 3525 int r;
294d81f1
LP
3526 Iterator i;
3527 const char *t;
0301abf4
LP
3528
3529 assert(u);
ac155bb8
MS
3530 assert(u->load_state == UNIT_STUB);
3531 assert(u->id);
23a177ef 3532
294d81f1
LP
3533 /* First, try to find the unit under its id. We always look
3534 * for unit files in the default directories, to make it easy
3535 * to override things by placing things in /etc/systemd/system */
9946996c
LP
3536 r = load_from_path(u, u->id);
3537 if (r < 0)
294d81f1
LP
3538 return r;
3539
3540 /* Try to find an alias we can load this with */
ac155bb8
MS
3541 if (u->load_state == UNIT_STUB)
3542 SET_FOREACH(t, u->names, i) {
294d81f1 3543
ac155bb8 3544 if (t == u->id)
294d81f1
LP
3545 continue;
3546
9946996c
LP
3547 r = load_from_path(u, t);
3548 if (r < 0)
294d81f1
LP
3549 return r;
3550
ac155bb8 3551 if (u->load_state != UNIT_STUB)
294d81f1
LP
3552 break;
3553 }
23a177ef 3554
294d81f1 3555 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 3556 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 3557
9946996c
LP
3558 r = load_from_path(u, u->fragment_path);
3559 if (r < 0)
23a177ef 3560 return r;
0301abf4 3561
ac155bb8 3562 if (u->load_state == UNIT_STUB) {
6ccb1b44
LP
3563 /* Hmm, this didn't work? Then let's get rid
3564 * of the fragment path stored for us, so that
3565 * we don't point to an invalid location. */
ac155bb8
MS
3566 free(u->fragment_path);
3567 u->fragment_path = NULL;
6ccb1b44
LP
3568 }
3569 }
3570
294d81f1 3571 /* Look for a template */
ac155bb8 3572 if (u->load_state == UNIT_STUB && u->instance) {
bc9fd78c 3573 _cleanup_free_ char *k;
294d81f1 3574
9946996c
LP
3575 k = unit_name_template(u->id);
3576 if (!k)
294d81f1
LP
3577 return -ENOMEM;
3578
3579 r = load_from_path(u, k);
294d81f1 3580 if (r < 0)
9e2f7c11 3581 return r;
890f434c 3582
ac155bb8
MS
3583 if (u->load_state == UNIT_STUB)
3584 SET_FOREACH(t, u->names, i) {
bc9fd78c 3585 _cleanup_free_ char *z = NULL;
87f0e418 3586
ac155bb8 3587 if (t == u->id)
23a177ef 3588 continue;
071830ff 3589
bc9fd78c
LP
3590 z = unit_name_template(t);
3591 if (!z)
294d81f1
LP
3592 return -ENOMEM;
3593
bc9fd78c 3594 r = load_from_path(u, z);
294d81f1 3595 if (r < 0)
23a177ef 3596 return r;
890f434c 3597
ac155bb8 3598 if (u->load_state != UNIT_STUB)
23a177ef
LP
3599 break;
3600 }
071830ff
LP
3601 }
3602
23a177ef 3603 return 0;
3efd4195 3604}
e537352b
LP
3605
3606void unit_dump_config_items(FILE *f) {
f975e971
LP
3607 static const struct {
3608 const ConfigParserCallback callback;
3609 const char *rvalue;
3610 } table[] = {
7f8aa671 3611#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
17df7223
LP
3612 { config_parse_warn_compat, "NOTSUPPORTED" },
3613#endif
f975e971
LP
3614 { config_parse_int, "INTEGER" },
3615 { config_parse_unsigned, "UNSIGNED" },
5556b5fe
LP
3616 { config_parse_iec_size, "SIZE" },
3617 { config_parse_iec_off, "SIZE" },
3618 { config_parse_si_size, "SIZE" },
f975e971
LP
3619 { config_parse_bool, "BOOLEAN" },
3620 { config_parse_string, "STRING" },
3621 { config_parse_path, "PATH" },
3622 { config_parse_unit_path_printf, "PATH" },
3623 { config_parse_strv, "STRING [...]" },
3624 { config_parse_exec_nice, "NICE" },
3625 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
3626 { config_parse_exec_io_class, "IOCLASS" },
3627 { config_parse_exec_io_priority, "IOPRIORITY" },
3628 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
3629 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
3630 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
3631 { config_parse_mode, "MODE" },
3632 { config_parse_unit_env_file, "FILE" },
3633 { config_parse_output, "OUTPUT" },
3634 { config_parse_input, "INPUT" },
ca37242e
LP
3635 { config_parse_log_facility, "FACILITY" },
3636 { config_parse_log_level, "LEVEL" },
f975e971
LP
3637 { config_parse_exec_capabilities, "CAPABILITIES" },
3638 { config_parse_exec_secure_bits, "SECUREBITS" },
ec8927ca 3639 { config_parse_bounding_set, "BOUNDINGSET" },
f975e971 3640 { config_parse_limit, "LIMIT" },
f975e971 3641 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
3642 { config_parse_exec, "PATH [ARGUMENT [...]]" },
3643 { config_parse_service_type, "SERVICETYPE" },
3644 { config_parse_service_restart, "SERVICERESTART" },
3645#ifdef HAVE_SYSV_COMPAT
3646 { config_parse_sysv_priority, "SYSVPRIORITY" },
f975e971
LP
3647#endif
3648 { config_parse_kill_mode, "KILLMODE" },
3649 { config_parse_kill_signal, "SIGNAL" },
3650 { config_parse_socket_listen, "SOCKET [...]" },
3651 { config_parse_socket_bind, "SOCKETBIND" },
3652 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 3653 { config_parse_sec, "SECONDS" },
d88a251b 3654 { config_parse_nsec, "NANOSECONDS" },
94828d2d 3655 { config_parse_namespace_path_strv, "PATH [...]" },
7c8fa05c 3656 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
3657 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
3658 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 3659 { config_parse_trigger_unit, "UNIT" },
f975e971 3660 { config_parse_timer, "TIMER" },
f975e971 3661 { config_parse_path_spec, "PATH" },
f975e971
LP
3662 { config_parse_notify_access, "ACCESS" },
3663 { config_parse_ip_tos, "TOS" },
3664 { config_parse_unit_condition_path, "CONDITION" },
3665 { config_parse_unit_condition_string, "CONDITION" },
3666 { config_parse_unit_condition_null, "CONDITION" },
a016b922 3667 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
3668 { config_parse_documentation, "URL" },
3669 { config_parse_service_timeout, "SECONDS" },
bf500566 3670 { config_parse_failure_action, "ACTION" },
7f0386f6
LP
3671 { config_parse_set_status, "STATUS" },
3672 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 3673 { config_parse_environ, "ENVIRON" },
c0467cf3 3674#ifdef HAVE_SECCOMP
17df7223 3675 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 3676 { config_parse_syscall_archs, "ARCHS" },
17df7223 3677 { config_parse_syscall_errno, "ERRNO" },
4298d0b5 3678 { config_parse_address_families, "FAMILIES" },
c0467cf3 3679#endif
7f0386f6
LP
3680 { config_parse_cpu_shares, "SHARES" },
3681 { config_parse_memory_limit, "LIMIT" },
3682 { config_parse_device_allow, "DEVICE" },
3683 { config_parse_device_policy, "POLICY" },
3684 { config_parse_blockio_bandwidth, "BANDWIDTH" },
3685 { config_parse_blockio_weight, "WEIGHT" },
3686 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
3687 { config_parse_long, "LONG" },
3688 { config_parse_socket_service, "SERVICE" },
6a6751fe
LP
3689#ifdef HAVE_SELINUX
3690 { config_parse_exec_selinux_context, "LABEL" },
3691#endif
3692 { config_parse_job_mode, "MODE" },
3693 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 3694 { config_parse_personality, "PERSONALITY" },
f975e971
LP
3695 };
3696
3697 const char *prev = NULL;
3698 const char *i;
3699
3700 assert(f);
e537352b 3701
f975e971
LP
3702 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
3703 const char *rvalue = "OTHER", *lvalue;
3704 unsigned j;
3705 size_t prefix_len;
3706 const char *dot;
3707 const ConfigPerfItem *p;
3708
3709 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
3710
3711 dot = strchr(i, '.');
3712 lvalue = dot ? dot + 1 : i;
3713 prefix_len = dot-i;
3714
3715 if (dot)
641906e9 3716 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
3717 if (prev)
3718 fputc('\n', f);
3719
3720 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
3721 }
3722
3723 for (j = 0; j < ELEMENTSOF(table); j++)
3724 if (p->parse == table[j].callback) {
3725 rvalue = table[j].rvalue;
3726 break;
3727 }
3728
3729 fprintf(f, "%s=%s\n", lvalue, rvalue);
3730 prev = i;
3731 }
e537352b 3732}