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