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