]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
*: fix some inconsistent control statement style
[thirdparty/systemd.git] / src / core / load-fragment.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
a7334b09
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
bb112710 6 Copyright 2012 Holger Hans Peter Freyther
a7334b09
LP
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
a7334b09
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
3efd4195 22#include <errno.h>
87f0e418 23#include <fcntl.h>
25e870b5 24#include <linux/fs.h>
5f5d8eab 25#include <linux/oom.h>
349cc4a5 26#if HAVE_SECCOMP
618234a5
LP
27#include <seccomp.h>
28#endif
5f5d8eab
LP
29#include <sched.h>
30#include <string.h>
3d57c6ab 31#include <sys/resource.h>
5f5d8eab 32#include <sys/stat.h>
3efd4195 33
5f5d8eab 34#include "af-list.h"
cf0fbc49 35#include "alloc-util.h"
5f5d8eab
LP
36#include "bus-error.h"
37#include "bus-internal.h"
38#include "bus-util.h"
39#include "cap-list.h"
a103496c 40#include "capability-util.h"
5f5d8eab 41#include "cgroup.h"
3efd4195 42#include "conf-parser.h"
618234a5 43#include "cpu-set-util.h"
5f5d8eab
LP
44#include "env-util.h"
45#include "errno-list.h"
4f5dd394 46#include "escape.h"
3ffd4af2 47#include "fd-util.h"
f4f15635 48#include "fs-util.h"
08f3be7a 49#include "hexdecoct.h"
d3070fbd 50#include "io-util.h"
9eba9da4 51#include "ioprio.h"
d3070fbd 52#include "journal-util.h"
3ffd4af2 53#include "load-fragment.h"
5f5d8eab 54#include "log.h"
94f04347 55#include "missing.h"
83555251 56#include "mount-util.h"
6bedfcbb 57#include "parse-util.h"
9eb977db 58#include "path-util.h"
7b3e062c 59#include "process-util.h"
d0a7c5f6 60#include "rlimit-util.h"
349cc4a5 61#if HAVE_SECCOMP
57183d11
LP
62#include "seccomp-util.h"
63#endif
5f5d8eab 64#include "securebits.h"
07d46372 65#include "securebits-util.h"
5f5d8eab 66#include "signal-util.h"
8fcde012 67#include "stat-util.h"
07630cea 68#include "string-util.h"
5f5d8eab
LP
69#include "strv.h"
70#include "unit-name.h"
71#include "unit-printf.h"
72#include "unit.h"
66dccd8d 73#include "user-util.h"
5f5d8eab 74#include "utf8.h"
49cf4170 75#include "web-util.h"
57183d11 76
17df7223
LP
77int config_parse_warn_compat(
78 const char *unit,
79 const char *filename,
80 unsigned line,
81 const char *section,
82 unsigned section_line,
83 const char *lvalue,
84 int ltype,
85 const char *rvalue,
86 void *data,
87 void *userdata) {
a2c0e528
ZJS
88 Disabled reason = ltype;
89
90 switch(reason) {
91 case DISABLED_CONFIGURATION:
12ca818f 92 log_syntax(unit, LOG_DEBUG, filename, line, 0,
a2c0e528
ZJS
93 "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
94 break;
9e37c954 95 case DISABLED_LEGACY:
12ca818f 96 log_syntax(unit, LOG_INFO, filename, line, 0,
9e37c954
ZJS
97 "Support for option %s= has been removed and it is ignored", lvalue);
98 break;
a2c0e528 99 case DISABLED_EXPERIMENTAL:
12ca818f 100 log_syntax(unit, LOG_INFO, filename, line, 0,
a2c0e528
ZJS
101 "Support for option %s= has not yet been enabled and it is ignored", lvalue);
102 break;
103 };
e8e581bf 104
07459bb6
FF
105 return 0;
106}
07459bb6 107
5afe510c
LP
108DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode");
109
f32b43bd
LP
110int config_parse_unit_deps(
111 const char *unit,
112 const char *filename,
113 unsigned line,
114 const char *section,
115 unsigned section_line,
116 const char *lvalue,
117 int ltype,
118 const char *rvalue,
119 void *data,
120 void *userdata) {
3efd4195 121
f975e971 122 UnitDependency d = ltype;
87f0e418 123 Unit *u = userdata;
3d793d29 124 const char *p;
3efd4195
LP
125
126 assert(filename);
127 assert(lvalue);
128 assert(rvalue);
3efd4195 129
3d793d29 130 p = rvalue;
9ed794a3 131 for (;;) {
3d793d29 132 _cleanup_free_ char *word = NULL, *k = NULL;
3efd4195 133 int r;
3efd4195 134
c89f52ac 135 r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
3d793d29
SS
136 if (r == 0)
137 break;
138 if (r == -ENOMEM)
74051b9b 139 return log_oom();
3d793d29
SS
140 if (r < 0) {
141 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
142 break;
143 }
3efd4195 144
3d793d29 145 r = unit_name_printf(u, word, &k);
19f6d710 146 if (r < 0) {
12ca818f 147 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
19f6d710
LP
148 continue;
149 }
9e2f7c11 150
eef85c4a 151 r = unit_add_dependency_by_name(u, d, k, NULL, true, UNIT_DEPENDENCY_FILE);
57020a3a 152 if (r < 0)
12ca818f 153 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
3efd4195
LP
154 }
155
156 return 0;
157}
158
f32b43bd
LP
159int config_parse_obsolete_unit_deps(
160 const char *unit,
161 const char *filename,
162 unsigned line,
163 const char *section,
164 unsigned section_line,
165 const char *lvalue,
166 int ltype,
167 const char *rvalue,
168 void *data,
169 void *userdata) {
170
171 log_syntax(unit, LOG_WARNING, filename, line, 0,
172 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue, unit_dependency_to_string(ltype));
173
174 return config_parse_unit_deps(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
175}
176
b02cb41c
LP
177int config_parse_unit_string_printf(
178 const char *unit,
179 const char *filename,
180 unsigned line,
181 const char *section,
182 unsigned section_line,
183 const char *lvalue,
184 int ltype,
185 const char *rvalue,
186 void *data,
187 void *userdata) {
932921b5 188
74051b9b 189 _cleanup_free_ char *k = NULL;
b02cb41c 190 Unit *u = userdata;
19f6d710 191 int r;
932921b5
LP
192
193 assert(filename);
194 assert(lvalue);
195 assert(rvalue);
f2d3769a 196 assert(u);
932921b5 197
19f6d710 198 r = unit_full_printf(u, rvalue, &k);
b02cb41c
LP
199 if (r < 0) {
200 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
201 return 0;
202 }
932921b5 203
b02cb41c 204 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
932921b5
LP
205}
206
12ca818f
LP
207int config_parse_unit_strv_printf(
208 const char *unit,
209 const char *filename,
210 unsigned line,
211 const char *section,
212 unsigned section_line,
213 const char *lvalue,
214 int ltype,
215 const char *rvalue,
216 void *data,
217 void *userdata) {
8fef7659
LP
218
219 Unit *u = userdata;
74051b9b 220 _cleanup_free_ char *k = NULL;
19f6d710 221 int r;
8fef7659
LP
222
223 assert(filename);
224 assert(lvalue);
225 assert(rvalue);
226 assert(u);
227
19f6d710 228 r = unit_full_printf(u, rvalue, &k);
12ca818f
LP
229 if (r < 0) {
230 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
231 return 0;
232 }
8fef7659 233
12ca818f 234 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
8fef7659
LP
235}
236
5f5d8eab
LP
237int config_parse_unit_path_printf(
238 const char *unit,
239 const char *filename,
240 unsigned line,
241 const char *section,
242 unsigned section_line,
243 const char *lvalue,
244 int ltype,
245 const char *rvalue,
246 void *data,
247 void *userdata) {
6ea832a2 248
74051b9b 249 _cleanup_free_ char *k = NULL;
811ba7a0 250 Unit *u = userdata;
19f6d710 251 int r;
2c75fb73 252 bool fatal = ltype;
6ea832a2
LP
253
254 assert(filename);
255 assert(lvalue);
256 assert(rvalue);
6ea832a2
LP
257 assert(u);
258
19f6d710 259 r = unit_full_printf(u, rvalue, &k);
811ba7a0 260 if (r < 0) {
2c75fb73
ZJS
261 log_syntax(unit, LOG_ERR, filename, line, r,
262 "Failed to resolve unit specifiers on %s%s: %m",
263 fatal ? "" : ", ignoring", rvalue);
264 return fatal ? -ENOEXEC : 0;
811ba7a0 265 }
6ea832a2 266
811ba7a0
LP
267 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
268}
269
270int config_parse_unit_path_strv_printf(
271 const char *unit,
272 const char *filename,
273 unsigned line,
274 const char *section,
275 unsigned section_line,
276 const char *lvalue,
277 int ltype,
278 const char *rvalue,
279 void *data,
280 void *userdata) {
281
a2a5291b 282 char ***x = data;
811ba7a0 283 Unit *u = userdata;
811ba7a0 284 int r;
035fe294 285 const char *p;
811ba7a0
LP
286
287 assert(filename);
288 assert(lvalue);
289 assert(rvalue);
290 assert(u);
291
499295fb 292 if (isempty(rvalue)) {
9f2d41a6 293 *x = strv_free(*x);
499295fb
YW
294 return 0;
295 }
296
035fe294
ZJS
297 for (p = rvalue;;) {
298 _cleanup_free_ char *word = NULL, *k = NULL;
811ba7a0 299
035fe294
ZJS
300 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
301 if (r == 0)
302 return 0;
303 if (r == -ENOMEM)
304 return log_oom();
305 if (r < 0) {
306 log_syntax(unit, LOG_WARNING, filename, line, r,
307 "Invalid syntax, ignoring: %s", rvalue);
308 return 0;
309 }
811ba7a0 310
035fe294 311 r = unit_full_printf(u, word, &k);
811ba7a0 312 if (r < 0) {
035fe294
ZJS
313 log_syntax(unit, LOG_ERR, filename, line, r,
314 "Failed to resolve unit specifiers on \"%s\", ignoring: %m", word);
811ba7a0
LP
315 return 0;
316 }
317
318 if (!utf8_is_valid(k)) {
0e05ee04 319 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
811ba7a0
LP
320 return 0;
321 }
322
323 if (!path_is_absolute(k)) {
035fe294
ZJS
324 log_syntax(unit, LOG_ERR, filename, line, 0,
325 "Symlink path is not absolute: %s", k);
811ba7a0
LP
326 return 0;
327 }
328
329 path_kill_slashes(k);
330
331 r = strv_push(x, k);
332 if (r < 0)
333 return log_oom();
811ba7a0
LP
334 k = NULL;
335 }
6ea832a2
LP
336}
337
e8e581bf
ZJS
338int config_parse_socket_listen(const char *unit,
339 const char *filename,
340 unsigned line,
341 const char *section,
71a61510 342 unsigned section_line,
e8e581bf
ZJS
343 const char *lvalue,
344 int ltype,
345 const char *rvalue,
346 void *data,
347 void *userdata) {
42f4e3c4 348
b1389b0d
ZJS
349 _cleanup_free_ SocketPort *p = NULL;
350 SocketPort *tail;
542563ba 351 Socket *s;
19f6d710 352 int r;
16354eff 353
42f4e3c4
LP
354 assert(filename);
355 assert(lvalue);
356 assert(rvalue);
357 assert(data);
358
595ed347 359 s = SOCKET(data);
542563ba 360
74051b9b
LP
361 if (isempty(rvalue)) {
362 /* An empty assignment removes all ports */
363 socket_free_ports(s);
364 return 0;
365 }
366
7f110ff9
LP
367 p = new0(SocketPort, 1);
368 if (!p)
74051b9b 369 return log_oom();
916abb21 370
74051b9b 371 if (ltype != SOCKET_SOCKET) {
916abb21 372
74051b9b 373 p->type = ltype;
19f6d710
LP
374 r = unit_full_printf(UNIT(s), rvalue, &p->path);
375 if (r < 0) {
12ca818f
LP
376 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
377 return 0;
916abb21
LP
378 }
379
380 path_kill_slashes(p->path);
381
7a22745a 382 } else if (streq(lvalue, "ListenNetlink")) {
74051b9b 383 _cleanup_free_ char *k = NULL;
1fd45a90 384
7a22745a 385 p->type = SOCKET_SOCKET;
19f6d710 386 r = unit_full_printf(UNIT(s), rvalue, &k);
12ca818f
LP
387 if (r < 0) {
388 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
389 return 0;
390 }
7a22745a 391
12ca818f 392 r = socket_address_parse_netlink(&p->address, k);
1fd45a90 393 if (r < 0) {
12ca818f 394 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue);
7a22745a
LP
395 return 0;
396 }
397
542563ba 398 } else {
74051b9b 399 _cleanup_free_ char *k = NULL;
1fd45a90 400
542563ba 401 p->type = SOCKET_SOCKET;
19f6d710 402 r = unit_full_printf(UNIT(s), rvalue, &k);
12ca818f
LP
403 if (r < 0) {
404 log_syntax(unit, LOG_ERR, filename, line, r,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
405 return 0;
406 }
542563ba 407
12ca818f 408 r = socket_address_parse_and_warn(&p->address, k);
1fd45a90 409 if (r < 0) {
f847b8b7
MS
410 if (r != -EAFNOSUPPORT)
411 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue);
412
c0b34696 413 return 0;
542563ba
LP
414 }
415
416 if (streq(lvalue, "ListenStream"))
417 p->address.type = SOCK_STREAM;
418 else if (streq(lvalue, "ListenDatagram"))
419 p->address.type = SOCK_DGRAM;
420 else {
421 assert(streq(lvalue, "ListenSequentialPacket"));
422 p->address.type = SOCK_SEQPACKET;
423 }
424
425 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
12ca818f 426 log_syntax(unit, LOG_ERR, filename, line, 0, "Address family not supported, ignoring: %s", rvalue);
c0b34696 427 return 0;
542563ba 428 }
16354eff
LP
429 }
430
542563ba 431 p->fd = -1;
15087cdb
PS
432 p->auxiliary_fds = NULL;
433 p->n_auxiliary_fds = 0;
2e41a51e 434 p->socket = s;
49f91047
LP
435
436 if (s->ports) {
71fda00f
LP
437 LIST_FIND_TAIL(port, s->ports, tail);
438 LIST_INSERT_AFTER(port, s->ports, tail, p);
49f91047 439 } else
71fda00f 440 LIST_PREPEND(port, s->ports, p);
b1389b0d 441 p = NULL;
542563ba 442
16354eff 443 return 0;
42f4e3c4
LP
444}
445
74bb646e
SS
446int config_parse_socket_protocol(const char *unit,
447 const char *filename,
448 unsigned line,
449 const char *section,
450 unsigned section_line,
451 const char *lvalue,
452 int ltype,
453 const char *rvalue,
454 void *data,
455 void *userdata) {
456 Socket *s;
457
458 assert(filename);
459 assert(lvalue);
460 assert(rvalue);
461 assert(data);
462
463 s = SOCKET(data);
464
465 if (streq(rvalue, "udplite"))
466 s->socket_protocol = IPPROTO_UDPLITE;
467 else if (streq(rvalue, "sctp"))
468 s->socket_protocol = IPPROTO_SCTP;
469 else {
470 log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue);
471 return 0;
472 }
473
474 return 0;
475}
476
e8e581bf
ZJS
477int config_parse_socket_bind(const char *unit,
478 const char *filename,
479 unsigned line,
480 const char *section,
71a61510 481 unsigned section_line,
e8e581bf
ZJS
482 const char *lvalue,
483 int ltype,
484 const char *rvalue,
485 void *data,
486 void *userdata) {
42f4e3c4 487
542563ba 488 Socket *s;
c0120d99 489 SocketAddressBindIPv6Only b;
42f4e3c4
LP
490
491 assert(filename);
492 assert(lvalue);
493 assert(rvalue);
494 assert(data);
495
595ed347 496 s = SOCKET(data);
542563ba 497
5198dabc
LP
498 b = socket_address_bind_ipv6_only_from_string(rvalue);
499 if (b < 0) {
c0120d99
LP
500 int r;
501
5198dabc
LP
502 r = parse_boolean(rvalue);
503 if (r < 0) {
12ca818f 504 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
c0b34696 505 return 0;
c0120d99 506 }
42f4e3c4 507
c0120d99
LP
508 s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
509 } else
510 s->bind_ipv6_only = b;
542563ba 511
42f4e3c4
LP
512 return 0;
513}
514
41bf0590
LP
515int config_parse_exec_nice(
516 const char *unit,
517 const char *filename,
518 unsigned line,
519 const char *section,
520 unsigned section_line,
521 const char *lvalue,
522 int ltype,
523 const char *rvalue,
524 void *data,
525 void *userdata) {
034c6ed7 526
fb33a393 527 ExecContext *c = data;
e8e581bf 528 int priority, r;
034c6ed7
LP
529
530 assert(filename);
531 assert(lvalue);
532 assert(rvalue);
533 assert(data);
534
41bf0590 535 r = parse_nice(rvalue, &priority);
e8e581bf 536 if (r < 0) {
41bf0590
LP
537 if (r == -ERANGE)
538 log_syntax(unit, LOG_ERR, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue);
539 else
540 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue);
034c6ed7 541
c0b34696 542 return 0;
034c6ed7
LP
543 }
544
fb33a393 545 c->nice = priority;
71155933 546 c->nice_set = true;
fb33a393 547
034c6ed7
LP
548 return 0;
549}
550
e8e581bf
ZJS
551int config_parse_exec_oom_score_adjust(const char* unit,
552 const char *filename,
553 unsigned line,
554 const char *section,
71a61510 555 unsigned section_line,
e8e581bf
ZJS
556 const char *lvalue,
557 int ltype,
558 const char *rvalue,
559 void *data,
560 void *userdata) {
034c6ed7 561
fb33a393 562 ExecContext *c = data;
e8e581bf 563 int oa, r;
034c6ed7
LP
564
565 assert(filename);
566 assert(lvalue);
567 assert(rvalue);
568 assert(data);
569
e8e581bf
ZJS
570 r = safe_atoi(rvalue, &oa);
571 if (r < 0) {
12ca818f 572 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
c0b34696 573 return 0;
034c6ed7
LP
574 }
575
dd6c17b1 576 if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
12ca818f 577 log_syntax(unit, LOG_ERR, filename, line, 0, "OOM score adjust value out of range, ignoring: %s", rvalue);
c0b34696 578 return 0;
034c6ed7
LP
579 }
580
dd6c17b1
LP
581 c->oom_score_adjust = oa;
582 c->oom_score_adjust_set = true;
fb33a393 583
034c6ed7
LP
584 return 0;
585}
586
527b7a42
LP
587int config_parse_exec(
588 const char *unit,
589 const char *filename,
590 unsigned line,
591 const char *section,
592 unsigned section_line,
593 const char *lvalue,
594 int ltype,
595 const char *rvalue,
596 void *data,
597 void *userdata) {
034c6ed7 598
46a0d98a 599 ExecCommand **e = data;
5125e762 600 Unit *u = userdata;
46a0d98a
FB
601 const char *p;
602 bool semicolon;
7f110ff9 603 int r;
034c6ed7
LP
604
605 assert(filename);
606 assert(lvalue);
607 assert(rvalue);
61e5d8ed 608 assert(e);
034c6ed7 609
74051b9b 610 e += ltype;
c83f1f30 611 rvalue += strspn(rvalue, WHITESPACE);
c83f1f30 612
74051b9b
LP
613 if (isempty(rvalue)) {
614 /* An empty assignment resets the list */
f1acf85a 615 *e = exec_command_free_list(*e);
74051b9b
LP
616 return 0;
617 }
618
bd1b973f 619 p = rvalue;
46a0d98a 620 do {
dea7b6b0 621 _cleanup_free_ char *path = NULL, *firstword = NULL;
165a31c0
LP
622 ExecCommandFlags flags = 0;
623 bool ignore = false, separate_argv0 = false;
dea7b6b0 624 _cleanup_free_ ExecCommand *nce = NULL;
46a0d98a
FB
625 _cleanup_strv_free_ char **n = NULL;
626 size_t nlen = 0, nbufsize = 0;
5125e762 627 const char *f;
6c666e26 628
46a0d98a
FB
629 semicolon = false;
630
9a82ab95 631 r = extract_first_word_and_warn(&p, &firstword, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
46a0d98a
FB
632 if (r <= 0)
633 return 0;
6c666e26 634
46a0d98a 635 f = firstword;
007f48bb 636 for (;;) {
165a31c0
LP
637 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
638 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
639 * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if
640 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
641 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
642 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
643 * other sandboxing, with some special exceptions for changing UID.
644 *
645 * The idea is that '!!' may be used to write services that can take benefit of systemd's
646 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
647 * privilege dropping within the daemon if the kernel does not offer that. */
648
649 if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE)) {
650 flags |= EXEC_COMMAND_IGNORE_FAILURE;
46a0d98a 651 ignore = true;
165a31c0 652 } else if (*f == '@' && !separate_argv0)
46a0d98a 653 separate_argv0 = true;
165a31c0
LP
654 else if (*f == '+' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC)))
655 flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
656 else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC)))
657 flags |= EXEC_COMMAND_NO_SETUID;
658 else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))) {
659 flags &= ~EXEC_COMMAND_NO_SETUID;
660 flags |= EXEC_COMMAND_AMBIENT_MAGIC;
661 } else
46a0d98a 662 break;
313cefa1 663 f++;
61e5d8ed 664 }
46a0d98a 665
5125e762
LP
666 r = unit_full_printf(u, f, &path);
667 if (r < 0) {
bb28e684
ZJS
668 log_syntax(unit, LOG_ERR, filename, line, r,
669 "Failed to resolve unit specifiers on %s%s: %m",
670 f, ignore ? ", ignoring" : "");
671 return ignore ? 0 : -ENOEXEC;
5125e762
LP
672 }
673
674 if (isempty(path)) {
46a0d98a 675 /* First word is either "-" or "@" with no command. */
bb28e684
ZJS
676 log_syntax(unit, LOG_ERR, filename, line, 0,
677 "Empty path in command line%s: \"%s\"",
678 ignore ? ", ignoring" : "", rvalue);
679 return ignore ? 0 : -ENOEXEC;
b2fadec6 680 }
5125e762 681 if (!string_is_safe(path)) {
bb28e684
ZJS
682 log_syntax(unit, LOG_ERR, filename, line, 0,
683 "Executable path contains special characters%s: %s",
684 ignore ? ", ignoring" : "", rvalue);
685 return ignore ? 0 : -ENOEXEC;
46a0d98a 686 }
5125e762 687 if (!path_is_absolute(path)) {
bb28e684
ZJS
688 log_syntax(unit, LOG_ERR, filename, line, 0,
689 "Executable path is not absolute%s: %s",
690 ignore ? ", ignoring" : "", rvalue);
691 return ignore ? 0 : -ENOEXEC;
46a0d98a 692 }
5125e762 693 if (endswith(path, "/")) {
bb28e684
ZJS
694 log_syntax(unit, LOG_ERR, filename, line, 0,
695 "Executable path specifies a directory%s: %s",
696 ignore ? ", ignoring" : "", rvalue);
697 return ignore ? 0 : -ENOEXEC;
46a0d98a 698 }
61e5d8ed 699
46a0d98a 700 if (!separate_argv0) {
5125e762
LP
701 char *w = NULL;
702
46a0d98a
FB
703 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
704 return log_oom();
5125e762
LP
705
706 w = strdup(path);
707 if (!w)
46a0d98a 708 return log_oom();
5125e762 709 n[nlen++] = w;
46a0d98a
FB
710 n[nlen] = NULL;
711 }
7f110ff9 712
46a0d98a
FB
713 path_kill_slashes(path);
714
4b1c1753 715 while (!isempty(p)) {
5125e762 716 _cleanup_free_ char *word = NULL, *resolved = NULL;
46a0d98a
FB
717
718 /* Check explicitly for an unquoted semicolon as
719 * command separator token. */
720 if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
313cefa1 721 p++;
46a0d98a
FB
722 p += strspn(p, WHITESPACE);
723 semicolon = true;
724 break;
c8539536 725 }
7f110ff9 726
5125e762
LP
727 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
728 * extract_first_word() would return the same for all of those. */
46a0d98a 729 if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
5125e762
LP
730 char *w;
731
46a0d98a
FB
732 p += 2;
733 p += strspn(p, WHITESPACE);
5125e762 734
46a0d98a
FB
735 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
736 return log_oom();
5125e762
LP
737
738 w = strdup(";");
739 if (!w)
46a0d98a 740 return log_oom();
5125e762 741 n[nlen++] = w;
46a0d98a
FB
742 n[nlen] = NULL;
743 continue;
61e5d8ed 744 }
c8539536 745
9a82ab95 746 r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
46a0d98a
FB
747 if (r == 0)
748 break;
5125e762 749 if (r < 0)
bb28e684 750 return ignore ? 0 : -ENOEXEC;
5125e762
LP
751
752 r = unit_full_printf(u, word, &resolved);
753 if (r < 0) {
bb28e684
ZJS
754 log_syntax(unit, LOG_ERR, filename, line, r,
755 "Failed to resolve unit specifiers on %s%s: %m",
756 word, ignore ? ", ignoring" : "");
757 return ignore ? 0 : -ENOEXEC;
5125e762 758 }
46a0d98a
FB
759
760 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
761 return log_oom();
5125e762 762 n[nlen++] = resolved;
46a0d98a 763 n[nlen] = NULL;
5125e762 764 resolved = NULL;
61e5d8ed
LP
765 }
766
46a0d98a 767 if (!n || !n[0]) {
bb28e684
ZJS
768 log_syntax(unit, LOG_ERR, filename, line, 0,
769 "Empty executable name or zeroeth argument%s: %s",
770 ignore ? ", ignoring" : "", rvalue);
771 return ignore ? 0 : -ENOEXEC;
7f110ff9 772 }
6c666e26 773
7f110ff9 774 nce = new0(ExecCommand, 1);
46a0d98a
FB
775 if (!nce)
776 return log_oom();
61e5d8ed
LP
777
778 nce->argv = n;
779 nce->path = path;
165a31c0 780 nce->flags = flags;
034c6ed7 781
61e5d8ed 782 exec_command_append_list(e, nce);
01f78473 783
46a0d98a
FB
784 /* Do not _cleanup_free_ these. */
785 n = NULL;
786 path = NULL;
787 nce = NULL;
034c6ed7 788
46a0d98a
FB
789 rvalue = p;
790 } while (semicolon);
034c6ed7 791
46a0d98a 792 return 0;
034c6ed7
LP
793}
794
f975e971
LP
795DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
796DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
034c6ed7 797
d31645ad
LP
798int config_parse_socket_bindtodevice(
799 const char* unit,
800 const char *filename,
801 unsigned line,
802 const char *section,
803 unsigned section_line,
804 const char *lvalue,
805 int ltype,
806 const char *rvalue,
807 void *data,
808 void *userdata) {
acbb0225
LP
809
810 Socket *s = data;
811 char *n;
812
813 assert(filename);
814 assert(lvalue);
815 assert(rvalue);
816 assert(data);
817
818 if (rvalue[0] && !streq(rvalue, "*")) {
d31645ad
LP
819 if (!ifname_valid(rvalue)) {
820 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is invalid, ignoring: %s", rvalue);
821 return 0;
822 }
823
74051b9b
LP
824 n = strdup(rvalue);
825 if (!n)
826 return log_oom();
acbb0225
LP
827 } else
828 n = NULL;
829
830 free(s->bind_to_device);
831 s->bind_to_device = n;
832
833 return 0;
834}
835
9bd6a50e
LP
836int config_parse_exec_input(
837 const char *unit,
838 const char *filename,
839 unsigned line,
840 const char *section,
841 unsigned section_line,
842 const char *lvalue,
843 int ltype,
844 const char *rvalue,
845 void *data,
846 void *userdata) {
52c239d7 847
52c239d7 848 ExecContext *c = data;
2038c3f5
LP
849 Unit *u = userdata;
850 const char *n;
851 ExecInput ei;
52c239d7
LB
852 int r;
853
854 assert(data);
855 assert(filename);
856 assert(line);
857 assert(rvalue);
858
2038c3f5
LP
859 n = startswith(rvalue, "fd:");
860 if (n) {
861 _cleanup_free_ char *resolved = NULL;
862
863 r = unit_full_printf(u, n, &resolved);
864 if (r < 0)
865 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
866
867 if (isempty(resolved))
868 resolved = mfree(resolved);
869 else if (!fdname_is_valid(resolved)) {
870 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name: %s", resolved);
871 return -EINVAL;
52c239d7 872 }
9bd6a50e 873
2038c3f5
LP
874 free_and_replace(c->stdio_fdname[STDIN_FILENO], resolved);
875
876 ei = EXEC_INPUT_NAMED_FD;
877
878 } else if ((n = startswith(rvalue, "file:"))) {
879 _cleanup_free_ char *resolved = NULL;
880
881 r = unit_full_printf(u, n, &resolved);
52c239d7 882 if (r < 0)
2038c3f5 883 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
9bd6a50e 884
2038c3f5
LP
885 if (!path_is_absolute(resolved)) {
886 log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires an absolute path name: %s", resolved);
887 return -EINVAL;
888 }
889
99be45a4 890 if (!path_is_normalized(resolved)) {
2038c3f5
LP
891 log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires a normalized path name: %s", resolved);
892 return -EINVAL;
893 }
894
895 free_and_replace(c->stdio_file[STDIN_FILENO], resolved);
896
897 ei = EXEC_INPUT_FILE;
9bd6a50e 898
52c239d7 899 } else {
9bd6a50e
LP
900 ei = exec_input_from_string(rvalue);
901 if (ei < 0) {
52c239d7 902 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse input specifier, ignoring: %s", rvalue);
9bd6a50e
LP
903 return 0;
904 }
52c239d7 905 }
9bd6a50e 906
2038c3f5 907 c->std_input = ei;
9bd6a50e 908 return 0;
52c239d7
LB
909}
910
08f3be7a
LP
911int config_parse_exec_input_text(
912 const char *unit,
913 const char *filename,
914 unsigned line,
915 const char *section,
916 unsigned section_line,
917 const char *lvalue,
918 int ltype,
919 const char *rvalue,
920 void *data,
921 void *userdata) {
922
923 _cleanup_free_ char *unescaped = NULL, *resolved = NULL;
924 ExecContext *c = data;
925 Unit *u = userdata;
926 size_t sz;
927 void *p;
928 int r;
929
930 assert(data);
931 assert(filename);
932 assert(line);
933 assert(rvalue);
934
935 if (isempty(rvalue)) {
936 /* Reset if the empty string is assigned */
937 c->stdin_data = mfree(c->stdin_data);
938 c->stdin_data_size = 0;
52c239d7
LB
939 return 0;
940 }
08f3be7a
LP
941
942 r = cunescape(rvalue, 0, &unescaped);
2038c3f5
LP
943 if (r < 0)
944 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode C escaped text: %s", rvalue);
08f3be7a
LP
945
946 r = unit_full_printf(u, unescaped, &resolved);
2038c3f5
LP
947 if (r < 0)
948 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", unescaped);
08f3be7a
LP
949
950 sz = strlen(resolved);
951 if (c->stdin_data_size + sz + 1 < c->stdin_data_size || /* check for overflow */
952 c->stdin_data_size + sz + 1 > EXEC_STDIN_DATA_MAX) {
2038c3f5
LP
953 log_syntax(unit, LOG_ERR, filename, line, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
954 return -E2BIG;
08f3be7a
LP
955 }
956
957 p = realloc(c->stdin_data, c->stdin_data_size + sz + 1);
958 if (!p)
959 return log_oom();
960
961 *((char*) mempcpy((char*) p + c->stdin_data_size, resolved, sz)) = '\n';
962
963 c->stdin_data = p;
964 c->stdin_data_size += sz + 1;
965
966 return 0;
52c239d7
LB
967}
968
08f3be7a
LP
969int config_parse_exec_input_data(
970 const char *unit,
971 const char *filename,
972 unsigned line,
973 const char *section,
974 unsigned section_line,
975 const char *lvalue,
976 int ltype,
977 const char *rvalue,
978 void *data,
979 void *userdata) {
980
981 _cleanup_free_ char *cleaned = NULL;
982 _cleanup_free_ void *p = NULL;
983 ExecContext *c = data;
984 size_t sz;
985 void *q;
986 int r;
987
988 assert(data);
989 assert(filename);
990 assert(line);
991 assert(rvalue);
992
993 if (isempty(rvalue)) {
994 /* Reset if the empty string is assigned */
995 c->stdin_data = mfree(c->stdin_data);
996 c->stdin_data_size = 0;
997 return 0;
998 }
999
1000 /* Be tolerant to whitespace. Remove it all before decoding */
1001 cleaned = strdup(rvalue);
1002 if (!cleaned)
1003 return log_oom();
1004 delete_chars(cleaned, WHITESPACE);
1005
1006 r = unbase64mem(cleaned, (size_t) -1, &p, &sz);
2038c3f5
LP
1007 if (r < 0)
1008 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode base64 data, ignoring: %s", cleaned);
08f3be7a
LP
1009
1010 assert(sz > 0);
1011
1012 if (c->stdin_data_size + sz < c->stdin_data_size || /* check for overflow */
1013 c->stdin_data_size + sz > EXEC_STDIN_DATA_MAX) {
2038c3f5
LP
1014 log_syntax(unit, LOG_ERR, filename, line, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
1015 return -E2BIG;
08f3be7a
LP
1016 }
1017
1018 q = realloc(c->stdin_data, c->stdin_data_size + sz);
1019 if (!q)
1020 return log_oom();
1021
1022 memcpy((uint8_t*) q + c->stdin_data_size, p, sz);
1023
1024 c->stdin_data = q;
1025 c->stdin_data_size += sz;
1026
1027 return 0;
1028}
1029
2038c3f5
LP
1030int config_parse_exec_output(
1031 const char *unit,
1032 const char *filename,
1033 unsigned line,
1034 const char *section,
1035 unsigned section_line,
1036 const char *lvalue,
1037 int ltype,
1038 const char *rvalue,
1039 void *data,
1040 void *userdata) {
1041
1042 _cleanup_free_ char *resolved = NULL;
1043 const char *n;
52c239d7 1044 ExecContext *c = data;
2038c3f5 1045 Unit *u = userdata;
52c239d7 1046 ExecOutput eo;
52c239d7
LB
1047 int r;
1048
1049 assert(data);
1050 assert(filename);
1051 assert(line);
1052 assert(lvalue);
1053 assert(rvalue);
1054
2038c3f5
LP
1055 n = startswith(rvalue, "fd:");
1056 if (n) {
1057 r = unit_full_printf(u, n, &resolved);
1058 if (r < 0)
1059 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
1060
1061 if (isempty(resolved))
1062 resolved = mfree(resolved);
1063 else if (!fdname_is_valid(resolved)) {
1064 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name: %s", resolved);
1065 return -EINVAL;
52c239d7 1066 }
2038c3f5 1067
52c239d7 1068 eo = EXEC_OUTPUT_NAMED_FD;
2038c3f5
LP
1069
1070 } else if ((n = startswith(rvalue, "file:"))) {
1071
1072 r = unit_full_printf(u, n, &resolved);
1073 if (r < 0)
1074 return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
1075
1076 if (!path_is_absolute(resolved)) {
1077 log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires an absolute path name: %s", resolved);
1078 return -EINVAL;
1079 }
1080
99be45a4 1081 if (!path_is_normalized(resolved)) {
2038c3f5
LP
1082 log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires a normalized path name, ignoring: %s", resolved);
1083 return -EINVAL;
1084 }
1085
1086 eo = EXEC_OUTPUT_FILE;
1087
52c239d7
LB
1088 } else {
1089 eo = exec_output_from_string(rvalue);
2038c3f5 1090 if (eo < 0) {
52c239d7
LB
1091 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output specifier, ignoring: %s", rvalue);
1092 return 0;
1093 }
1094 }
1095
1096 if (streq(lvalue, "StandardOutput")) {
2038c3f5
LP
1097 if (eo == EXEC_OUTPUT_NAMED_FD)
1098 free_and_replace(c->stdio_fdname[STDOUT_FILENO], resolved);
1099 else
1100 free_and_replace(c->stdio_file[STDOUT_FILENO], resolved);
1101
52c239d7 1102 c->std_output = eo;
2038c3f5 1103
52c239d7 1104 } else {
2038c3f5
LP
1105 assert(streq(lvalue, "StandardError"));
1106
1107 if (eo == EXEC_OUTPUT_NAMED_FD)
1108 free_and_replace(c->stdio_fdname[STDERR_FILENO], resolved);
1109 else
1110 free_and_replace(c->stdio_file[STDERR_FILENO], resolved);
1111
1112 c->std_error = eo;
52c239d7 1113 }
2038c3f5
LP
1114
1115 return 0;
52c239d7 1116}
87f0e418 1117
e8e581bf
ZJS
1118int config_parse_exec_io_class(const char *unit,
1119 const char *filename,
1120 unsigned line,
1121 const char *section,
71a61510 1122 unsigned section_line,
e8e581bf
ZJS
1123 const char *lvalue,
1124 int ltype,
1125 const char *rvalue,
1126 void *data,
1127 void *userdata) {
94f04347
LP
1128
1129 ExecContext *c = data;
1130 int x;
1131
1132 assert(filename);
1133 assert(lvalue);
1134 assert(rvalue);
1135 assert(data);
1136
f8b69d1d
MS
1137 x = ioprio_class_from_string(rvalue);
1138 if (x < 0) {
12ca818f 1139 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue);
c0b34696 1140 return 0;
0d87eb42 1141 }
94f04347
LP
1142
1143 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
1144 c->ioprio_set = true;
1145
1146 return 0;
1147}
1148
e8e581bf
ZJS
1149int config_parse_exec_io_priority(const char *unit,
1150 const char *filename,
1151 unsigned line,
1152 const char *section,
71a61510 1153 unsigned section_line,
e8e581bf
ZJS
1154 const char *lvalue,
1155 int ltype,
1156 const char *rvalue,
1157 void *data,
1158 void *userdata) {
94f04347
LP
1159
1160 ExecContext *c = data;
e8e581bf 1161 int i, r;
94f04347
LP
1162
1163 assert(filename);
1164 assert(lvalue);
1165 assert(rvalue);
1166 assert(data);
1167
7f452159
LP
1168 r = ioprio_parse_priority(rvalue, &i);
1169 if (r < 0) {
12ca818f 1170 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue);
c0b34696 1171 return 0;
071830ff
LP
1172 }
1173
94f04347
LP
1174 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
1175 c->ioprio_set = true;
1176
071830ff
LP
1177 return 0;
1178}
1179
e8e581bf
ZJS
1180int config_parse_exec_cpu_sched_policy(const char *unit,
1181 const char *filename,
1182 unsigned line,
1183 const char *section,
71a61510 1184 unsigned section_line,
e8e581bf
ZJS
1185 const char *lvalue,
1186 int ltype,
1187 const char *rvalue,
1188 void *data,
1189 void *userdata) {
9eba9da4 1190
94f04347
LP
1191
1192 ExecContext *c = data;
1193 int x;
1194
1195 assert(filename);
1196 assert(lvalue);
1197 assert(rvalue);
1198 assert(data);
1199
f8b69d1d
MS
1200 x = sched_policy_from_string(rvalue);
1201 if (x < 0) {
12ca818f 1202 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 1203 return 0;
0d87eb42 1204 }
94f04347
LP
1205
1206 c->cpu_sched_policy = x;
bb112710
HHPF
1207 /* Moving to or from real-time policy? We need to adjust the priority */
1208 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
94f04347
LP
1209 c->cpu_sched_set = true;
1210
1211 return 0;
1212}
1213
e8e581bf
ZJS
1214int config_parse_exec_cpu_sched_prio(const char *unit,
1215 const char *filename,
1216 unsigned line,
1217 const char *section,
71a61510 1218 unsigned section_line,
e8e581bf
ZJS
1219 const char *lvalue,
1220 int ltype,
1221 const char *rvalue,
1222 void *data,
1223 void *userdata) {
9eba9da4
LP
1224
1225 ExecContext *c = data;
e8e581bf 1226 int i, min, max, r;
9eba9da4
LP
1227
1228 assert(filename);
1229 assert(lvalue);
1230 assert(rvalue);
1231 assert(data);
1232
e8e581bf
ZJS
1233 r = safe_atoi(rvalue, &i);
1234 if (r < 0) {
12ca818f 1235 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 1236 return 0;
94f04347 1237 }
9eba9da4 1238
bb112710
HHPF
1239 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1240 min = sched_get_priority_min(c->cpu_sched_policy);
1241 max = sched_get_priority_max(c->cpu_sched_policy);
1242
1243 if (i < min || i > max) {
12ca818f 1244 log_syntax(unit, LOG_ERR, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue);
bb112710
HHPF
1245 return 0;
1246 }
1247
94f04347
LP
1248 c->cpu_sched_priority = i;
1249 c->cpu_sched_set = true;
1250
1251 return 0;
1252}
1253
e8e581bf
ZJS
1254int config_parse_exec_cpu_affinity(const char *unit,
1255 const char *filename,
1256 unsigned line,
1257 const char *section,
71a61510 1258 unsigned section_line,
e8e581bf
ZJS
1259 const char *lvalue,
1260 int ltype,
1261 const char *rvalue,
1262 void *data,
1263 void *userdata) {
94f04347
LP
1264
1265 ExecContext *c = data;
9d5ca7f8
FB
1266 _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
1267 int ncpus;
94f04347
LP
1268
1269 assert(filename);
1270 assert(lvalue);
1271 assert(rvalue);
1272 assert(data);
1273
765d143b 1274 ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
9d5ca7f8
FB
1275 if (ncpus < 0)
1276 return ncpus;
487393e9 1277
9d5ca7f8
FB
1278 if (c->cpuset)
1279 CPU_FREE(c->cpuset);
82c121a4 1280
9d5ca7f8
FB
1281 if (ncpus == 0)
1282 /* An empty assignment resets the CPU list */
1283 c->cpuset = NULL;
1284 else {
1285 c->cpuset = cpuset;
1286 cpuset = NULL;
9eba9da4 1287 }
9d5ca7f8 1288 c->cpuset_ncpus = ncpus;
9eba9da4 1289
94f04347
LP
1290 return 0;
1291}
1292
e8e581bf
ZJS
1293int config_parse_exec_secure_bits(const char *unit,
1294 const char *filename,
1295 unsigned line,
1296 const char *section,
71a61510 1297 unsigned section_line,
e8e581bf
ZJS
1298 const char *lvalue,
1299 int ltype,
1300 const char *rvalue,
1301 void *data,
1302 void *userdata) {
94f04347
LP
1303
1304 ExecContext *c = data;
035fe294 1305 int r;
94f04347
LP
1306
1307 assert(filename);
1308 assert(lvalue);
1309 assert(rvalue);
1310 assert(data);
1311
74051b9b
LP
1312 if (isempty(rvalue)) {
1313 /* An empty assignment resets the field */
1314 c->secure_bits = 0;
1315 return 0;
1316 }
1317
07d46372
YW
1318 r = secure_bits_from_string(rvalue);
1319 if (r == -ENOMEM)
1320 return log_oom();
1321 if (r < 0) {
1322 log_syntax(unit, LOG_WARNING, filename, line, r,
1323 "Invalid syntax, ignoring: %s", rvalue);
1324 return 0;
1325 }
035fe294 1326
07d46372 1327 c->secure_bits = r;
035fe294 1328
07d46372 1329 return 0;
94f04347
LP
1330}
1331
a103496c 1332int config_parse_capability_set(
65dce264
LP
1333 const char *unit,
1334 const char *filename,
1335 unsigned line,
1336 const char *section,
1337 unsigned section_line,
1338 const char *lvalue,
1339 int ltype,
1340 const char *rvalue,
1341 void *data,
1342 void *userdata) {
94f04347 1343
a103496c
IP
1344 uint64_t *capability_set = data;
1345 uint64_t sum = 0, initial = 0;
260abb78 1346 bool invert = false;
dd1f5bd0 1347 int r;
94f04347
LP
1348
1349 assert(filename);
1350 assert(lvalue);
1351 assert(rvalue);
1352 assert(data);
1353
260abb78
LP
1354 if (rvalue[0] == '~') {
1355 invert = true;
1356 rvalue++;
1357 }
1358
70d54d90 1359 if (streq(lvalue, "CapabilityBoundingSet"))
a103496c 1360 initial = CAP_ALL; /* initialized to all bits on */
755d4b67 1361 /* else "AmbientCapabilities" initialized to all bits off */
260abb78 1362
dd1f5bd0
YW
1363 r = capability_set_from_string(rvalue, &sum);
1364 if (r == -ENOMEM)
1365 return log_oom();
1366 if (r < 0) {
1367 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse word: %s", rvalue);
1368 return 0;
94f04347 1369 }
9eba9da4 1370
a103496c 1371 if (sum == 0 || *capability_set == initial)
c792ec2e
IP
1372 /* "", "~" or uninitialized data -> replace */
1373 *capability_set = invert ? ~sum : sum;
1374 else {
a103496c 1375 /* previous data -> merge */
c792ec2e
IP
1376 if (invert)
1377 *capability_set &= ~sum;
1378 else
1379 *capability_set |= sum;
1380 }
260abb78 1381
9eba9da4
LP
1382 return 0;
1383}
1384
91518d20 1385int config_parse_limit(
d580265e
LP
1386 const char *unit,
1387 const char *filename,
1388 unsigned line,
1389 const char *section,
1390 unsigned section_line,
1391 const char *lvalue,
1392 int ltype,
1393 const char *rvalue,
1394 void *data,
1395 void *userdata) {
412ea7a9 1396
d0a7c5f6
LP
1397 struct rlimit **rl = data, d = {};
1398 int r;
a4c18002
LP
1399
1400 assert(filename);
1401 assert(lvalue);
1402 assert(rvalue);
1403 assert(data);
1404
d0a7c5f6
LP
1405 r = rlimit_parse(ltype, rvalue, &d);
1406 if (r == -EILSEQ) {
1407 log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue);
1408 return 0;
1409 }
1410 if (r < 0) {
1411 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
1412 return 0;
1413 }
a4c18002 1414
d0a7c5f6
LP
1415 if (rl[ltype])
1416 *rl[ltype] = d;
1417 else {
1418 rl[ltype] = newdup(struct rlimit, &d, 1);
1419 if (!rl[ltype])
1420 return log_oom();
1421 }
a4c18002 1422
d0a7c5f6 1423 return 0;
91518d20 1424}
a4c18002 1425
349cc4a5 1426#if HAVE_SYSV_COMPAT
e8e581bf
ZJS
1427int config_parse_sysv_priority(const char *unit,
1428 const char *filename,
1429 unsigned line,
1430 const char *section,
71a61510 1431 unsigned section_line,
e8e581bf
ZJS
1432 const char *lvalue,
1433 int ltype,
1434 const char *rvalue,
1435 void *data,
1436 void *userdata) {
a9a1e00a
LP
1437
1438 int *priority = data;
e8e581bf 1439 int i, r;
a9a1e00a
LP
1440
1441 assert(filename);
1442 assert(lvalue);
1443 assert(rvalue);
1444 assert(data);
1445
e8e581bf
ZJS
1446 r = safe_atoi(rvalue, &i);
1447 if (r < 0 || i < 0) {
12ca818f 1448 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue);
c0b34696 1449 return 0;
a9a1e00a
LP
1450 }
1451
1452 *priority = (int) i;
1453 return 0;
1454}
07459bb6 1455#endif
a9a1e00a 1456
023a4f67 1457DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
f975e971 1458DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
50159e6a 1459
83555251
LP
1460int config_parse_exec_mount_flags(
1461 const char *unit,
1462 const char *filename,
1463 unsigned line,
1464 const char *section,
1465 unsigned section_line,
1466 const char *lvalue,
1467 int ltype,
1468 const char *rvalue,
1469 void *data,
1470 void *userdata) {
15ae422b 1471
e28bb14a 1472
e28bb14a 1473 ExecContext *c = data;
c7383828 1474 int r;
15ae422b
LP
1475
1476 assert(filename);
1477 assert(lvalue);
1478 assert(rvalue);
1479 assert(data);
1480
c7383828
ZJS
1481 r = mount_propagation_flags_from_string(rvalue, &c->mount_flags);
1482 if (r < 0)
1483 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring.", rvalue);
e28bb14a 1484
15ae422b
LP
1485 return 0;
1486}
1487
5f8640fb
LP
1488int config_parse_exec_selinux_context(
1489 const char *unit,
1490 const char *filename,
1491 unsigned line,
1492 const char *section,
1493 unsigned section_line,
1494 const char *lvalue,
1495 int ltype,
1496 const char *rvalue,
1497 void *data,
1498 void *userdata) {
1499
1500 ExecContext *c = data;
1501 Unit *u = userdata;
1502 bool ignore;
1503 char *k;
1504 int r;
1505
1506 assert(filename);
1507 assert(lvalue);
1508 assert(rvalue);
1509 assert(data);
1510
1511 if (isempty(rvalue)) {
a1e58e8e 1512 c->selinux_context = mfree(c->selinux_context);
5f8640fb
LP
1513 c->selinux_context_ignore = false;
1514 return 0;
1515 }
1516
1517 if (rvalue[0] == '-') {
1518 ignore = true;
1519 rvalue++;
1520 } else
1521 ignore = false;
1522
18913df9 1523 r = unit_full_printf(u, rvalue, &k);
5f8640fb 1524 if (r < 0) {
bb28e684
ZJS
1525 log_syntax(unit, LOG_ERR, filename, line, r,
1526 "Failed to resolve specifiers%s: %m",
1527 ignore ? ", ignoring" : "");
1528 return ignore ? 0 : -ENOEXEC;
5f8640fb
LP
1529 }
1530
1531 free(c->selinux_context);
1532 c->selinux_context = k;
1533 c->selinux_context_ignore = ignore;
1534
1535 return 0;
1536}
1537
eef65bf3
MS
1538int config_parse_exec_apparmor_profile(
1539 const char *unit,
1540 const char *filename,
1541 unsigned line,
1542 const char *section,
1543 unsigned section_line,
1544 const char *lvalue,
1545 int ltype,
1546 const char *rvalue,
1547 void *data,
1548 void *userdata) {
1549
1550 ExecContext *c = data;
1551 Unit *u = userdata;
1552 bool ignore;
1553 char *k;
1554 int r;
1555
1556 assert(filename);
1557 assert(lvalue);
1558 assert(rvalue);
1559 assert(data);
1560
1561 if (isempty(rvalue)) {
a1e58e8e 1562 c->apparmor_profile = mfree(c->apparmor_profile);
eef65bf3
MS
1563 c->apparmor_profile_ignore = false;
1564 return 0;
1565 }
1566
1567 if (rvalue[0] == '-') {
1568 ignore = true;
1569 rvalue++;
1570 } else
1571 ignore = false;
1572
18913df9 1573 r = unit_full_printf(u, rvalue, &k);
eef65bf3 1574 if (r < 0) {
bb28e684
ZJS
1575 log_syntax(unit, LOG_ERR, filename, line, r,
1576 "Failed to resolve specifiers%s: %m",
1577 ignore ? ", ignoring" : "");
1578 return ignore ? 0 : -ENOEXEC;
eef65bf3
MS
1579 }
1580
1581 free(c->apparmor_profile);
1582 c->apparmor_profile = k;
1583 c->apparmor_profile_ignore = ignore;
1584
1585 return 0;
1586}
1587
2ca620c4
WC
1588int config_parse_exec_smack_process_label(
1589 const char *unit,
1590 const char *filename,
1591 unsigned line,
1592 const char *section,
1593 unsigned section_line,
1594 const char *lvalue,
1595 int ltype,
1596 const char *rvalue,
1597 void *data,
1598 void *userdata) {
1599
1600 ExecContext *c = data;
1601 Unit *u = userdata;
1602 bool ignore;
1603 char *k;
1604 int r;
1605
1606 assert(filename);
1607 assert(lvalue);
1608 assert(rvalue);
1609 assert(data);
1610
1611 if (isempty(rvalue)) {
a1e58e8e 1612 c->smack_process_label = mfree(c->smack_process_label);
2ca620c4
WC
1613 c->smack_process_label_ignore = false;
1614 return 0;
1615 }
1616
1617 if (rvalue[0] == '-') {
1618 ignore = true;
1619 rvalue++;
1620 } else
1621 ignore = false;
1622
18913df9 1623 r = unit_full_printf(u, rvalue, &k);
2ca620c4 1624 if (r < 0) {
bb28e684
ZJS
1625 log_syntax(unit, LOG_ERR, filename, line, r,
1626 "Failed to resolve specifiers%s: %m",
1627 ignore ? ", ignoring" : "");
1628 return ignore ? 0 : -ENOEXEC;
2ca620c4
WC
1629 }
1630
1631 free(c->smack_process_label);
1632 c->smack_process_label = k;
1633 c->smack_process_label_ignore = ignore;
1634
1635 return 0;
1636}
1637
e8e581bf
ZJS
1638int config_parse_timer(const char *unit,
1639 const char *filename,
1640 unsigned line,
1641 const char *section,
71a61510 1642 unsigned section_line,
e8e581bf
ZJS
1643 const char *lvalue,
1644 int ltype,
1645 const char *rvalue,
1646 void *data,
1647 void *userdata) {
871d7de4
LP
1648
1649 Timer *t = data;
2507992f 1650 usec_t usec = 0;
871d7de4
LP
1651 TimerValue *v;
1652 TimerBase b;
36697dc0 1653 CalendarSpec *c = NULL;
2507992f
DC
1654 Unit *u = userdata;
1655 _cleanup_free_ char *k = NULL;
1656 int r;
871d7de4
LP
1657
1658 assert(filename);
1659 assert(lvalue);
1660 assert(rvalue);
1661 assert(data);
1662
74051b9b
LP
1663 if (isempty(rvalue)) {
1664 /* Empty assignment resets list */
1665 timer_free_values(t);
1666 return 0;
1667 }
1668
36697dc0
LP
1669 b = timer_base_from_string(lvalue);
1670 if (b < 0) {
12ca818f 1671 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer base, ignoring: %s", lvalue);
c0b34696 1672 return 0;
871d7de4
LP
1673 }
1674
2507992f
DC
1675 r = unit_full_printf(u, rvalue, &k);
1676 if (r < 0) {
1677 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
1678 return 0;
1679 }
1680
36697dc0 1681 if (b == TIMER_CALENDAR) {
2507992f
DC
1682 if (calendar_spec_from_string(k, &c) < 0) {
1683 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", k);
36697dc0
LP
1684 return 0;
1685 }
36697dc0 1686 } else {
2507992f
DC
1687 if (parse_sec(k, &usec) < 0) {
1688 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", k);
36697dc0
LP
1689 return 0;
1690 }
871d7de4
LP
1691 }
1692
36697dc0 1693 v = new0(TimerValue, 1);
4d5e13a1 1694 if (!v) {
0b76b4d8 1695 calendar_spec_free(c);
74051b9b 1696 return log_oom();
4d5e13a1 1697 }
871d7de4
LP
1698
1699 v->base = b;
2507992f 1700 v->value = usec;
36697dc0 1701 v->calendar_spec = c;
871d7de4 1702
71fda00f 1703 LIST_PREPEND(value, t->values, v);
871d7de4
LP
1704
1705 return 0;
1706}
1707
3ecaa09b
LP
1708int config_parse_trigger_unit(
1709 const char *unit,
1710 const char *filename,
1711 unsigned line,
1712 const char *section,
71a61510 1713 unsigned section_line,
3ecaa09b
LP
1714 const char *lvalue,
1715 int ltype,
1716 const char *rvalue,
1717 void *data,
1718 void *userdata) {
871d7de4 1719
74051b9b 1720 _cleanup_free_ char *p = NULL;
3ecaa09b
LP
1721 Unit *u = data;
1722 UnitType type;
1723 int r;
398ef8ba
LP
1724
1725 assert(filename);
1726 assert(lvalue);
1727 assert(rvalue);
1728 assert(data);
1729
eef85c4a 1730 if (!hashmap_isempty(u->dependencies[UNIT_TRIGGERS])) {
12ca818f 1731 log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
3ecaa09b
LP
1732 return 0;
1733 }
871d7de4 1734
19f6d710 1735 r = unit_name_printf(u, rvalue, &p);
12ca818f
LP
1736 if (r < 0) {
1737 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1738 return 0;
1739 }
74051b9b 1740
12ca818f 1741 type = unit_name_to_type(p);
3ecaa09b 1742 if (type < 0) {
12ca818f 1743 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue);
c0b34696 1744 return 0;
871d7de4
LP
1745 }
1746
3ecaa09b 1747 if (type == u->type) {
12ca818f 1748 log_syntax(unit, LOG_ERR, filename, line, 0, "Trigger cannot be of same type, ignoring: %s", rvalue);
3ecaa09b
LP
1749 return 0;
1750 }
1751
eef85c4a 1752 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true, UNIT_DEPENDENCY_FILE);
57020a3a 1753 if (r < 0) {
12ca818f 1754 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
c0b34696 1755 return 0;
871d7de4
LP
1756 }
1757
1758 return 0;
1759}
1760
e8e581bf
ZJS
1761int config_parse_path_spec(const char *unit,
1762 const char *filename,
1763 unsigned line,
1764 const char *section,
71a61510 1765 unsigned section_line,
e8e581bf
ZJS
1766 const char *lvalue,
1767 int ltype,
1768 const char *rvalue,
1769 void *data,
1770 void *userdata) {
01f78473
LP
1771
1772 Path *p = data;
1773 PathSpec *s;
1774 PathType b;
7fd1b19b 1775 _cleanup_free_ char *k = NULL;
19f6d710 1776 int r;
01f78473
LP
1777
1778 assert(filename);
1779 assert(lvalue);
1780 assert(rvalue);
1781 assert(data);
1782
74051b9b
LP
1783 if (isempty(rvalue)) {
1784 /* Empty assignment clears list */
1785 path_free_specs(p);
1786 return 0;
1787 }
1788
93e4c84b
LP
1789 b = path_type_from_string(lvalue);
1790 if (b < 0) {
12ca818f 1791 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue);
c0b34696 1792 return 0;
01f78473
LP
1793 }
1794
19f6d710
LP
1795 r = unit_full_printf(UNIT(p), rvalue, &k);
1796 if (r < 0) {
12ca818f
LP
1797 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
1798 return 0;
487060c2 1799 }
93e4c84b
LP
1800
1801 if (!path_is_absolute(k)) {
12ca818f 1802 log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k);
c0b34696 1803 return 0;
01f78473
LP
1804 }
1805
93e4c84b 1806 s = new0(PathSpec, 1);
543295ad 1807 if (!s)
93e4c84b 1808 return log_oom();
01f78473 1809
718db961 1810 s->unit = UNIT(p);
93e4c84b 1811 s->path = path_kill_slashes(k);
543295ad 1812 k = NULL;
01f78473
LP
1813 s->type = b;
1814 s->inotify_fd = -1;
1815
71fda00f 1816 LIST_PREPEND(spec, p->specs, s);
01f78473
LP
1817
1818 return 0;
1819}
1820
b02cb41c
LP
1821int config_parse_socket_service(
1822 const char *unit,
1823 const char *filename,
1824 unsigned line,
1825 const char *section,
1826 unsigned section_line,
1827 const char *lvalue,
1828 int ltype,
1829 const char *rvalue,
1830 void *data,
1831 void *userdata) {
d9ff321a 1832
4afd3348 1833 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
8dd4c05b 1834 _cleanup_free_ char *p = NULL;
d9ff321a 1835 Socket *s = data;
4ff77f66 1836 Unit *x;
8dd4c05b 1837 int r;
d9ff321a
LP
1838
1839 assert(filename);
1840 assert(lvalue);
1841 assert(rvalue);
1842 assert(data);
1843
19f6d710 1844 r = unit_name_printf(UNIT(s), rvalue, &p);
613b411c 1845 if (r < 0) {
bb28e684
ZJS
1846 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", rvalue);
1847 return -ENOEXEC;
613b411c 1848 }
74051b9b 1849
613b411c 1850 if (!endswith(p, ".service")) {
bb28e684
ZJS
1851 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service: %s", rvalue);
1852 return -ENOEXEC;
d9ff321a
LP
1853 }
1854
613b411c 1855 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
4ff77f66 1856 if (r < 0) {
bb28e684
ZJS
1857 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s: %s", rvalue, bus_error_message(&error, r));
1858 return -ENOEXEC;
d9ff321a
LP
1859 }
1860
4ff77f66
LP
1861 unit_ref_set(&s->service, x);
1862
d9ff321a
LP
1863 return 0;
1864}
1865
8dd4c05b
LP
1866int config_parse_fdname(
1867 const char *unit,
1868 const char *filename,
1869 unsigned line,
1870 const char *section,
1871 unsigned section_line,
1872 const char *lvalue,
1873 int ltype,
1874 const char *rvalue,
1875 void *data,
1876 void *userdata) {
1877
1878 _cleanup_free_ char *p = NULL;
1879 Socket *s = data;
1880 int r;
1881
1882 assert(filename);
1883 assert(lvalue);
1884 assert(rvalue);
1885 assert(data);
1886
1887 if (isempty(rvalue)) {
1888 s->fdname = mfree(s->fdname);
1889 return 0;
1890 }
1891
18913df9 1892 r = unit_full_printf(UNIT(s), rvalue, &p);
8dd4c05b
LP
1893 if (r < 0) {
1894 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1895 return 0;
1896 }
1897
1898 if (!fdname_is_valid(p)) {
1899 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
1900 return 0;
1901 }
1902
3b319885 1903 return free_and_replace(s->fdname, p);
8dd4c05b
LP
1904}
1905
b02cb41c
LP
1906int config_parse_service_sockets(
1907 const char *unit,
1908 const char *filename,
1909 unsigned line,
1910 const char *section,
1911 unsigned section_line,
1912 const char *lvalue,
1913 int ltype,
1914 const char *rvalue,
1915 void *data,
1916 void *userdata) {
f976f3f6
LP
1917
1918 Service *s = data;
7b2313f5 1919 const char *p;
b02cb41c 1920 int r;
f976f3f6
LP
1921
1922 assert(filename);
1923 assert(lvalue);
1924 assert(rvalue);
1925 assert(data);
1926
7b2313f5 1927 p = rvalue;
9ed794a3 1928 for (;;) {
6a0f3175 1929 _cleanup_free_ char *word = NULL, *k = NULL;
f976f3f6 1930
7b2313f5
SS
1931 r = extract_first_word(&p, &word, NULL, 0);
1932 if (r == 0)
1933 break;
1934 if (r == -ENOMEM)
74051b9b 1935 return log_oom();
7b2313f5
SS
1936 if (r < 0) {
1937 log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
1938 break;
1939 }
f976f3f6 1940
7b2313f5 1941 r = unit_name_printf(UNIT(s), word, &k);
b02cb41c
LP
1942 if (r < 0) {
1943 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1944 continue;
1945 }
57020a3a 1946
b02cb41c 1947 if (!endswith(k, ".socket")) {
12ca818f 1948 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
f976f3f6
LP
1949 continue;
1950 }
1951
eef85c4a 1952 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true, UNIT_DEPENDENCY_FILE);
57020a3a 1953 if (r < 0)
b02cb41c 1954 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
f976f3f6 1955
eef85c4a 1956 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true, UNIT_DEPENDENCY_FILE);
57020a3a 1957 if (r < 0)
b02cb41c 1958 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
f976f3f6
LP
1959 }
1960
1961 return 0;
1962}
1963
b02cb41c
LP
1964int config_parse_bus_name(
1965 const char *unit,
1966 const char *filename,
1967 unsigned line,
1968 const char *section,
1969 unsigned section_line,
1970 const char *lvalue,
1971 int ltype,
1972 const char *rvalue,
1973 void *data,
1974 void *userdata) {
1975
1976 _cleanup_free_ char *k = NULL;
1977 Unit *u = userdata;
1978 int r;
1979
1980 assert(filename);
1981 assert(lvalue);
1982 assert(rvalue);
1983 assert(u);
1984
1985 r = unit_full_printf(u, rvalue, &k);
1986 if (r < 0) {
1987 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
1988 return 0;
1989 }
1990
1991 if (!service_name_is_valid(k)) {
12ca818f 1992 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k);
b02cb41c
LP
1993 return 0;
1994 }
1995
1996 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
1997}
1998
aad41f08
LP
1999int config_parse_service_timeout(
2000 const char *unit,
2001 const char *filename,
2002 unsigned line,
2003 const char *section,
2004 unsigned section_line,
2005 const char *lvalue,
2006 int ltype,
2007 const char *rvalue,
2008 void *data,
2009 void *userdata) {
98709151
LN
2010
2011 Service *s = userdata;
aad41f08 2012 usec_t usec;
98709151
LN
2013 int r;
2014
2015 assert(filename);
2016 assert(lvalue);
2017 assert(rvalue);
2018 assert(s);
2019
aad41f08 2020 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
98709151 2021
aad41f08
LP
2022 r = parse_sec(rvalue, &usec);
2023 if (r < 0) {
2024 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
2025 return 0;
2026 }
d568a335 2027
36c16a7c
LP
2028 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
2029 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
2030 * all other timeouts. */
aad41f08
LP
2031 if (usec <= 0)
2032 usec = USEC_INFINITY;
2033
2034 if (!streq(lvalue, "TimeoutStopSec")) {
2035 s->start_timeout_defined = true;
2036 s->timeout_start_usec = usec;
2037 }
36c16a7c 2038
aad41f08
LP
2039 if (!streq(lvalue, "TimeoutStartSec"))
2040 s->timeout_stop_usec = usec;
36c16a7c 2041
d568a335 2042 return 0;
98709151
LN
2043}
2044
89beff89
LP
2045int config_parse_sec_fix_0(
2046 const char *unit,
2047 const char *filename,
2048 unsigned line,
2049 const char *section,
2050 unsigned section_line,
2051 const char *lvalue,
2052 int ltype,
2053 const char *rvalue,
2054 void *data,
2055 void *userdata) {
2056
2057 usec_t *usec = data;
2058 int r;
2059
2060 assert(filename);
2061 assert(lvalue);
2062 assert(rvalue);
2063 assert(usec);
2064
2065 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
2066 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
2067 * timeout. */
2068
0004f698 2069 r = parse_sec_fix_0(rvalue, usec);
89beff89
LP
2070 if (r < 0) {
2071 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
2072 return 0;
2073 }
2074
89beff89
LP
2075 return 0;
2076}
2077
66dccd8d
LP
2078int config_parse_user_group(
2079 const char *unit,
2080 const char *filename,
2081 unsigned line,
2082 const char *section,
2083 unsigned section_line,
2084 const char *lvalue,
2085 int ltype,
2086 const char *rvalue,
2087 void *data,
2088 void *userdata) {
2089
2090 char **user = data, *n;
2091 Unit *u = userdata;
2092 int r;
2093
2094 assert(filename);
2095 assert(lvalue);
2096 assert(rvalue);
2097 assert(u);
2098
2099 if (isempty(rvalue))
2100 n = NULL;
2101 else {
2102 _cleanup_free_ char *k = NULL;
2103
2104 r = unit_full_printf(u, rvalue, &k);
2105 if (r < 0) {
bb28e684
ZJS
2106 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue);
2107 return -ENOEXEC;
66dccd8d
LP
2108 }
2109
2110 if (!valid_user_group_name_or_id(k)) {
bb28e684
ZJS
2111 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2112 return -ENOEXEC;
66dccd8d
LP
2113 }
2114
2115 n = k;
2116 k = NULL;
2117 }
2118
2119 free(*user);
2120 *user = n;
2121
2122 return 0;
2123}
2124
2125int config_parse_user_group_strv(
2126 const char *unit,
2127 const char *filename,
2128 unsigned line,
2129 const char *section,
2130 unsigned section_line,
2131 const char *lvalue,
2132 int ltype,
2133 const char *rvalue,
2134 void *data,
2135 void *userdata) {
2136
2137 char ***users = data;
2138 Unit *u = userdata;
2139 const char *p;
2140 int r;
2141
2142 assert(filename);
2143 assert(lvalue);
2144 assert(rvalue);
2145 assert(u);
2146
2147 if (isempty(rvalue)) {
9f2d41a6 2148 *users = strv_free(*users);
66dccd8d
LP
2149 return 0;
2150 }
2151
2152 p = rvalue;
2153 for (;;) {
2154 _cleanup_free_ char *word = NULL, *k = NULL;
2155
9a82ab95 2156 r = extract_first_word(&p, &word, NULL, 0);
66dccd8d
LP
2157 if (r == 0)
2158 break;
2159 if (r == -ENOMEM)
2160 return log_oom();
2161 if (r < 0) {
bb28e684
ZJS
2162 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue);
2163 return -ENOEXEC;
66dccd8d
LP
2164 }
2165
2166 r = unit_full_printf(u, word, &k);
2167 if (r < 0) {
bb28e684
ZJS
2168 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word);
2169 return -ENOEXEC;
66dccd8d
LP
2170 }
2171
2172 if (!valid_user_group_name_or_id(k)) {
bb28e684
ZJS
2173 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2174 return -ENOEXEC;
66dccd8d
LP
2175 }
2176
2177 r = strv_push(users, k);
2178 if (r < 0)
2179 return log_oom();
2180
2181 k = NULL;
2182 }
2183
2184 return 0;
2185}
2186
5f5d8eab
LP
2187int config_parse_working_directory(
2188 const char *unit,
2189 const char *filename,
2190 unsigned line,
2191 const char *section,
2192 unsigned section_line,
2193 const char *lvalue,
2194 int ltype,
2195 const char *rvalue,
2196 void *data,
2197 void *userdata) {
2198
2199 ExecContext *c = data;
2200 Unit *u = userdata;
2201 bool missing_ok;
2202 int r;
2203
2204 assert(filename);
2205 assert(lvalue);
2206 assert(rvalue);
2207 assert(c);
2208 assert(u);
2209
2210 if (rvalue[0] == '-') {
2211 missing_ok = true;
2212 rvalue++;
2213 } else
2214 missing_ok = false;
2215
2216 if (streq(rvalue, "~")) {
2217 c->working_directory_home = true;
2218 c->working_directory = mfree(c->working_directory);
2219 } else {
2220 _cleanup_free_ char *k = NULL;
2221
2222 r = unit_full_printf(u, rvalue, &k);
2223 if (r < 0) {
bb28e684
ZJS
2224 log_syntax(unit, LOG_ERR, filename, line, r,
2225 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2226 rvalue, missing_ok ? ", ignoring" : "");
2227 return missing_ok ? 0 : -ENOEXEC;
5f5d8eab
LP
2228 }
2229
2230 path_kill_slashes(k);
2231
2232 if (!utf8_is_valid(k)) {
0e05ee04 2233 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
bb28e684 2234 return missing_ok ? 0 : -ENOEXEC;
5f5d8eab
LP
2235 }
2236
2237 if (!path_is_absolute(k)) {
bb28e684
ZJS
2238 log_syntax(unit, LOG_ERR, filename, line, 0,
2239 "Working directory path '%s' is not absolute%s.",
2240 rvalue, missing_ok ? ", ignoring" : "");
2241 return missing_ok ? 0 : -ENOEXEC;
5f5d8eab
LP
2242 }
2243
5f5d8eab 2244 c->working_directory_home = false;
bb28e684 2245 free_and_replace(c->working_directory, k);
5f5d8eab
LP
2246 }
2247
2248 c->working_directory_missing_ok = missing_ok;
2249 return 0;
2250}
2251
e8e581bf
ZJS
2252int config_parse_unit_env_file(const char *unit,
2253 const char *filename,
2254 unsigned line,
2255 const char *section,
71a61510 2256 unsigned section_line,
e8e581bf
ZJS
2257 const char *lvalue,
2258 int ltype,
2259 const char *rvalue,
2260 void *data,
2261 void *userdata) {
ddb26e18 2262
853b8397 2263 char ***env = data;
8fef7659 2264 Unit *u = userdata;
19f6d710 2265 _cleanup_free_ char *n = NULL;
853b8397 2266 int r;
ddb26e18
LP
2267
2268 assert(filename);
2269 assert(lvalue);
2270 assert(rvalue);
2271 assert(data);
2272
74051b9b
LP
2273 if (isempty(rvalue)) {
2274 /* Empty assignment frees the list */
6796073e 2275 *env = strv_free(*env);
74051b9b
LP
2276 return 0;
2277 }
2278
19f6d710 2279 r = unit_full_printf(u, rvalue, &n);
12ca818f
LP
2280 if (r < 0) {
2281 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2282 return 0;
2283 }
8fef7659 2284
12ca818f
LP
2285 if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) {
2286 log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n);
afe4bfe2
LP
2287 return 0;
2288 }
2289
12ca818f 2290 r = strv_extend(env, n);
853b8397
LP
2291 if (r < 0)
2292 return log_oom();
2293
2294 return 0;
2295}
2296
f7f3f5c3
LP
2297int config_parse_environ(
2298 const char *unit,
2299 const char *filename,
2300 unsigned line,
2301 const char *section,
2302 unsigned section_line,
2303 const char *lvalue,
2304 int ltype,
2305 const char *rvalue,
2306 void *data,
2307 void *userdata) {
853b8397
LP
2308
2309 Unit *u = userdata;
035fe294
ZJS
2310 char ***env = data;
2311 const char *p;
19f6d710 2312 int r;
853b8397
LP
2313
2314 assert(filename);
2315 assert(lvalue);
2316 assert(rvalue);
97d0e5f8 2317 assert(data);
853b8397
LP
2318
2319 if (isempty(rvalue)) {
2320 /* Empty assignment resets the list */
6796073e 2321 *env = strv_free(*env);
853b8397
LP
2322 return 0;
2323 }
2324
035fe294
ZJS
2325 for (p = rvalue;; ) {
2326 _cleanup_free_ char *word = NULL, *k = NULL;
035fe294
ZJS
2327
2328 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
2329 if (r == 0)
2330 return 0;
2331 if (r == -ENOMEM)
2332 return log_oom();
12ca818f 2333 if (r < 0) {
035fe294
ZJS
2334 log_syntax(unit, LOG_WARNING, filename, line, r,
2335 "Invalid syntax, ignoring: %s", rvalue);
12ca818f
LP
2336 return 0;
2337 }
97d0e5f8 2338
035fe294
ZJS
2339 if (u) {
2340 r = unit_full_printf(u, word, &k);
2341 if (r < 0) {
2342 log_syntax(unit, LOG_ERR, filename, line, r,
f7f3f5c3 2343 "Failed to resolve specifiers, ignoring: %s", word);
035fe294
ZJS
2344 continue;
2345 }
2346 } else {
2347 k = word;
2348 word = NULL;
527b7a42 2349 }
853b8397 2350
035fe294
ZJS
2351 if (!env_assignment_is_valid(k)) {
2352 log_syntax(unit, LOG_ERR, filename, line, 0,
2353 "Invalid environment assignment, ignoring: %s", k);
853b8397
LP
2354 continue;
2355 }
2356
54ac3494
ZJS
2357 r = strv_env_replace(env, k);
2358 if (r < 0)
853b8397 2359 return log_oom();
f7f3f5c3 2360
54ac3494 2361 k = NULL;
853b8397 2362 }
ddb26e18
LP
2363}
2364
00819cc1
LP
2365int config_parse_pass_environ(
2366 const char *unit,
2367 const char *filename,
2368 unsigned line,
2369 const char *section,
2370 unsigned section_line,
2371 const char *lvalue,
2372 int ltype,
2373 const char *rvalue,
2374 void *data,
2375 void *userdata) {
b4c14404
FB
2376
2377 const char *whole_rvalue = rvalue;
b4c14404
FB
2378 _cleanup_strv_free_ char **n = NULL;
2379 size_t nlen = 0, nbufsize = 0;
41de9cc2
LP
2380 char*** passenv = data;
2381 Unit *u = userdata;
b4c14404
FB
2382 int r;
2383
2384 assert(filename);
2385 assert(lvalue);
2386 assert(rvalue);
2387 assert(data);
2388
2389 if (isempty(rvalue)) {
2390 /* Empty assignment resets the list */
2391 *passenv = strv_free(*passenv);
2392 return 0;
2393 }
2394
2395 for (;;) {
41de9cc2 2396 _cleanup_free_ char *word = NULL, *k = NULL;
b4c14404 2397
9a82ab95 2398 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
b4c14404
FB
2399 if (r == 0)
2400 break;
2401 if (r == -ENOMEM)
2402 return log_oom();
2403 if (r < 0) {
2404 log_syntax(unit, LOG_ERR, filename, line, r,
2405 "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
2406 break;
2407 }
2408
41de9cc2
LP
2409 if (u) {
2410 r = unit_full_printf(u, word, &k);
2411 if (r < 0) {
2412 log_syntax(unit, LOG_ERR, filename, line, r,
2413 "Failed to resolve specifiers, ignoring: %s", word);
2414 continue;
2415 }
2416 } else {
2417 k = word;
2418 word = NULL;
2419 }
2420
2421 if (!env_name_is_valid(k)) {
2422 log_syntax(unit, LOG_ERR, filename, line, 0,
2423 "Invalid environment name for %s, ignoring: %s", lvalue, k);
b4c14404
FB
2424 continue;
2425 }
2426
2427 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2428 return log_oom();
41de9cc2
LP
2429
2430 n[nlen++] = k;
b4c14404 2431 n[nlen] = NULL;
41de9cc2 2432 k = NULL;
b4c14404
FB
2433 }
2434
2435 if (n) {
2436 r = strv_extend_strv(passenv, n, true);
2437 if (r < 0)
2438 return r;
2439 }
2440
2441 return 0;
2442}
2443
00819cc1
LP
2444int config_parse_unset_environ(
2445 const char *unit,
2446 const char *filename,
2447 unsigned line,
2448 const char *section,
2449 unsigned section_line,
2450 const char *lvalue,
2451 int ltype,
2452 const char *rvalue,
2453 void *data,
2454 void *userdata) {
2455
2456 _cleanup_strv_free_ char **n = NULL;
2457 const char *whole_rvalue = rvalue;
2458 size_t nlen = 0, nbufsize = 0;
2459 char*** unsetenv = data;
2460 Unit *u = userdata;
2461 int r;
2462
2463 assert(filename);
2464 assert(lvalue);
2465 assert(rvalue);
2466 assert(data);
2467
2468 if (isempty(rvalue)) {
2469 /* Empty assignment resets the list */
2470 *unsetenv = strv_free(*unsetenv);
2471 return 0;
2472 }
2473
2474 for (;;) {
2475 _cleanup_free_ char *word = NULL, *k = NULL;
2476
6c5def2f 2477 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
00819cc1
LP
2478 if (r == 0)
2479 break;
2480 if (r == -ENOMEM)
2481 return log_oom();
2482 if (r < 0) {
2483 log_syntax(unit, LOG_ERR, filename, line, r,
2484 "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
2485 break;
2486 }
2487
2488 if (u) {
2489 r = unit_full_printf(u, word, &k);
2490 if (r < 0) {
2491 log_syntax(unit, LOG_ERR, filename, line, r,
2492 "Failed to resolve specifiers, ignoring: %s", word);
2493 continue;
2494 }
2495 } else {
2496 k = word;
2497 word = NULL;
2498 }
2499
2500 if (!env_assignment_is_valid(k) && !env_name_is_valid(k)) {
2501 log_syntax(unit, LOG_ERR, filename, line, 0,
2502 "Invalid environment name or assignment %s, ignoring: %s", lvalue, k);
2503 continue;
2504 }
2505
2506 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2507 return log_oom();
2508
2509 n[nlen++] = k;
2510 n[nlen] = NULL;
2511 k = NULL;
2512 }
2513
2514 if (n) {
2515 r = strv_extend_strv(unsetenv, n, true);
2516 if (r < 0)
2517 return r;
2518 }
2519
2520 return 0;
2521}
2522
d3070fbd
LP
2523int config_parse_log_extra_fields(
2524 const char *unit,
2525 const char *filename,
2526 unsigned line,
2527 const char *section,
2528 unsigned section_line,
2529 const char *lvalue,
2530 int ltype,
2531 const char *rvalue,
2532 void *data,
2533 void *userdata) {
2534
2535 ExecContext *c = data;
2536 Unit *u = userdata;
2537 const char *p;
2538 int r;
2539
2540 assert(filename);
2541 assert(lvalue);
2542 assert(rvalue);
2543 assert(c);
2544
2545 if (isempty(rvalue)) {
2546 exec_context_free_log_extra_fields(c);
2547 return 0;
2548 }
2549
2550 for (p = rvalue;; ) {
2551 _cleanup_free_ char *word = NULL, *k = NULL;
2552 struct iovec *t;
2553 const char *eq;
2554
2555 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
2556 if (r == 0)
2557 break;
2558 if (r == -ENOMEM)
2559 return log_oom();
2560 if (r < 0) {
2561 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
2562 return 0;
2563 }
2564
2565 r = unit_full_printf(u, word, &k);
2566 if (r < 0) {
2567 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring field: %m", word);
2568 continue;
2569 }
2570
2571 eq = strchr(k, '=');
2572 if (!eq) {
2573 log_syntax(unit, LOG_ERR, filename, line, 0, "Log field lacks '=' character, ignoring field: %s", k);
2574 continue;
2575 }
2576
2577 if (!journal_field_valid(k, eq-k, false)) {
2578 log_syntax(unit, LOG_ERR, filename, line, 0, "Log field name is invalid, ignoring field: %s", k);
2579 continue;
2580 }
2581
2582 t = realloc_multiply(c->log_extra_fields, sizeof(struct iovec), c->n_log_extra_fields+1);
2583 if (!t)
2584 return log_oom();
2585
2586 c->log_extra_fields = t;
2587 c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE_STRING(k);
2588
2589 k = NULL;
2590 }
2591
2592 return 0;
2593}
2594
e8e581bf
ZJS
2595int config_parse_ip_tos(const char *unit,
2596 const char *filename,
2597 unsigned line,
2598 const char *section,
71a61510 2599 unsigned section_line,
e8e581bf
ZJS
2600 const char *lvalue,
2601 int ltype,
2602 const char *rvalue,
2603 void *data,
2604 void *userdata) {
4fd5948e
LP
2605
2606 int *ip_tos = data, x;
4fd5948e
LP
2607
2608 assert(filename);
2609 assert(lvalue);
2610 assert(rvalue);
2611 assert(data);
2612
f8b69d1d
MS
2613 x = ip_tos_from_string(rvalue);
2614 if (x < 0) {
12ca818f 2615 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue);
f8b69d1d
MS
2616 return 0;
2617 }
4fd5948e
LP
2618
2619 *ip_tos = x;
2620 return 0;
2621}
2622
59fccdc5
LP
2623int config_parse_unit_condition_path(
2624 const char *unit,
2625 const char *filename,
2626 unsigned line,
2627 const char *section,
2628 unsigned section_line,
2629 const char *lvalue,
2630 int ltype,
2631 const char *rvalue,
2632 void *data,
2633 void *userdata) {
52661efd 2634
2fbe635a 2635 _cleanup_free_ char *p = NULL;
59fccdc5
LP
2636 Condition **list = data, *c;
2637 ConditionType t = ltype;
2638 bool trigger, negate;
2639 Unit *u = userdata;
19f6d710 2640 int r;
52661efd
LP
2641
2642 assert(filename);
2643 assert(lvalue);
2644 assert(rvalue);
2645 assert(data);
2646
74051b9b
LP
2647 if (isempty(rvalue)) {
2648 /* Empty assignment resets the list */
447021aa 2649 *list = condition_free_list(*list);
74051b9b
LP
2650 return 0;
2651 }
2652
ab7f148f
LP
2653 trigger = rvalue[0] == '|';
2654 if (trigger)
267632f0
LP
2655 rvalue++;
2656
ab7f148f
LP
2657 negate = rvalue[0] == '!';
2658 if (negate)
52661efd
LP
2659 rvalue++;
2660
19f6d710 2661 r = unit_full_printf(u, rvalue, &p);
59fccdc5 2662 if (r < 0) {
12ca818f 2663 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
59fccdc5 2664 return 0;
19f6d710 2665 }
095b2d7a
AK
2666
2667 if (!path_is_absolute(p)) {
12ca818f 2668 log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p);
52661efd
LP
2669 return 0;
2670 }
2671
59fccdc5 2672 c = condition_new(t, p, trigger, negate);
ab7f148f 2673 if (!c)
74051b9b 2674 return log_oom();
52661efd 2675
59fccdc5 2676 LIST_PREPEND(conditions, *list, c);
52661efd
LP
2677 return 0;
2678}
2679
59fccdc5
LP
2680int config_parse_unit_condition_string(
2681 const char *unit,
2682 const char *filename,
2683 unsigned line,
2684 const char *section,
2685 unsigned section_line,
2686 const char *lvalue,
2687 int ltype,
2688 const char *rvalue,
2689 void *data,
2690 void *userdata) {
039655a4 2691
2fbe635a 2692 _cleanup_free_ char *s = NULL;
59fccdc5
LP
2693 Condition **list = data, *c;
2694 ConditionType t = ltype;
2695 bool trigger, negate;
2696 Unit *u = userdata;
19f6d710 2697 int r;
039655a4
LP
2698
2699 assert(filename);
2700 assert(lvalue);
2701 assert(rvalue);
2702 assert(data);
2703
74051b9b
LP
2704 if (isempty(rvalue)) {
2705 /* Empty assignment resets the list */
447021aa 2706 *list = condition_free_list(*list);
74051b9b
LP
2707 return 0;
2708 }
2709
c0d6e764
LP
2710 trigger = rvalue[0] == '|';
2711 if (trigger)
267632f0
LP
2712 rvalue++;
2713
c0d6e764
LP
2714 negate = rvalue[0] == '!';
2715 if (negate)
039655a4
LP
2716 rvalue++;
2717
19f6d710 2718 r = unit_full_printf(u, rvalue, &s);
59fccdc5 2719 if (r < 0) {
12ca818f 2720 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
59fccdc5 2721 return 0;
19f6d710 2722 }
095b2d7a 2723
59fccdc5 2724 c = condition_new(t, s, trigger, negate);
c0d6e764
LP
2725 if (!c)
2726 return log_oom();
039655a4 2727
59fccdc5 2728 LIST_PREPEND(conditions, *list, c);
039655a4
LP
2729 return 0;
2730}
2731
59fccdc5
LP
2732int config_parse_unit_condition_null(
2733 const char *unit,
2734 const char *filename,
2735 unsigned line,
2736 const char *section,
2737 unsigned section_line,
2738 const char *lvalue,
2739 int ltype,
2740 const char *rvalue,
2741 void *data,
2742 void *userdata) {
d257ddef 2743
59fccdc5 2744 Condition **list = data, *c;
267632f0 2745 bool trigger, negate;
d257ddef
LP
2746 int b;
2747
2748 assert(filename);
2749 assert(lvalue);
2750 assert(rvalue);
2751 assert(data);
2752
74051b9b
LP
2753 if (isempty(rvalue)) {
2754 /* Empty assignment resets the list */
447021aa 2755 *list = condition_free_list(*list);
74051b9b
LP
2756 return 0;
2757 }
2758
2759 trigger = rvalue[0] == '|';
2760 if (trigger)
267632f0
LP
2761 rvalue++;
2762
74051b9b
LP
2763 negate = rvalue[0] == '!';
2764 if (negate)
d257ddef
LP
2765 rvalue++;
2766
74051b9b
LP
2767 b = parse_boolean(rvalue);
2768 if (b < 0) {
12ca818f 2769 log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
d257ddef
LP
2770 return 0;
2771 }
2772
2773 if (!b)
2774 negate = !negate;
2775
74051b9b
LP
2776 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2777 if (!c)
2778 return log_oom();
d257ddef 2779
59fccdc5 2780 LIST_PREPEND(conditions, *list, c);
d257ddef
LP
2781 return 0;
2782}
2783
f975e971 2784DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
87a47f99 2785DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier");
c952c6ec 2786
a57f7e2c
LP
2787int config_parse_unit_requires_mounts_for(
2788 const char *unit,
2789 const char *filename,
2790 unsigned line,
2791 const char *section,
71a61510 2792 unsigned section_line,
a57f7e2c
LP
2793 const char *lvalue,
2794 int ltype,
2795 const char *rvalue,
2796 void *data,
2797 void *userdata) {
7c8fa05c
LP
2798
2799 Unit *u = userdata;
035fe294
ZJS
2800 const char *p;
2801 int r;
7c8fa05c
LP
2802
2803 assert(filename);
2804 assert(lvalue);
2805 assert(rvalue);
2806 assert(data);
2807
035fe294 2808 for (p = rvalue;; ) {
744bb5b1 2809 _cleanup_free_ char *word = NULL, *resolved = NULL;
a57f7e2c 2810
035fe294
ZJS
2811 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2812 if (r == 0)
2813 return 0;
2814 if (r == -ENOMEM)
a57f7e2c 2815 return log_oom();
035fe294
ZJS
2816 if (r < 0) {
2817 log_syntax(unit, LOG_WARNING, filename, line, r,
2818 "Invalid syntax, ignoring: %s", rvalue);
2819 return 0;
2820 }
7c8fa05c 2821
035fe294 2822 if (!utf8_is_valid(word)) {
0e05ee04 2823 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
a57f7e2c
LP
2824 continue;
2825 }
7c8fa05c 2826
744bb5b1
LP
2827 r = unit_full_printf(u, word, &resolved);
2828 if (r < 0) {
2829 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit name \"%s\", ignoring: %m", word);
2830 continue;
2831 }
2832
eef85c4a 2833 r = unit_require_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE);
a57f7e2c 2834 if (r < 0) {
744bb5b1 2835 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount \"%s\", ignoring: %m", resolved);
a57f7e2c
LP
2836 continue;
2837 }
2838 }
7c8fa05c 2839}
9e372868 2840
e8e581bf
ZJS
2841int config_parse_documentation(const char *unit,
2842 const char *filename,
2843 unsigned line,
2844 const char *section,
71a61510 2845 unsigned section_line,
e8e581bf
ZJS
2846 const char *lvalue,
2847 int ltype,
2848 const char *rvalue,
2849 void *data,
2850 void *userdata) {
49dbfa7b
LP
2851
2852 Unit *u = userdata;
2853 int r;
2854 char **a, **b;
2855
2856 assert(filename);
2857 assert(lvalue);
2858 assert(rvalue);
2859 assert(u);
2860
74051b9b
LP
2861 if (isempty(rvalue)) {
2862 /* Empty assignment resets the list */
6796073e 2863 u->documentation = strv_free(u->documentation);
74051b9b
LP
2864 return 0;
2865 }
2866
71a61510 2867 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 2868 rvalue, data, userdata);
49dbfa7b
LP
2869 if (r < 0)
2870 return r;
2871
2872 for (a = b = u->documentation; a && *a; a++) {
2873
a2e03378 2874 if (documentation_url_is_valid(*a))
49dbfa7b
LP
2875 *(b++) = *a;
2876 else {
12ca818f 2877 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a);
49dbfa7b
LP
2878 free(*a);
2879 }
2880 }
f6d2d421
ZJS
2881 if (b)
2882 *b = NULL;
49dbfa7b
LP
2883
2884 return r;
2885}
2886
349cc4a5 2887#if HAVE_SECCOMP
8130926d 2888
201c1cc2
TM
2889static int syscall_filter_parse_one(
2890 const char *unit,
2891 const char *filename,
2892 unsigned line,
2893 ExecContext *c,
2894 bool invert,
2895 const char *t,
8cfa775f
YW
2896 bool warn,
2897 int errno_num) {
201c1cc2
TM
2898 int r;
2899
8130926d
LP
2900 if (t[0] == '@') {
2901 const SyscallFilterSet *set;
2902 const char *i;
201c1cc2 2903
8130926d
LP
2904 set = syscall_filter_set_find(t);
2905 if (!set) {
2906 if (warn)
ba77dac2 2907 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown system call group, ignoring: %s", t);
8130926d
LP
2908 return 0;
2909 }
201c1cc2 2910
8130926d 2911 NULSTR_FOREACH(i, set->value) {
8cfa775f 2912 r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false, errno_num);
8130926d
LP
2913 if (r < 0)
2914 return r;
2915 }
201c1cc2
TM
2916 } else {
2917 int id;
2918
2919 id = seccomp_syscall_resolve_name(t);
391b81cd 2920 if (id == __NR_SCMP_ERROR) {
201c1cc2 2921 if (warn)
8130926d 2922 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
201c1cc2
TM
2923 return 0;
2924 }
2925
2926 /* If we previously wanted to forbid a syscall and now
ba77dac2 2927 * we want to allow it, then remove it from the list.
201c1cc2
TM
2928 */
2929 if (!invert == c->syscall_whitelist) {
8cfa775f 2930 r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num));
201c1cc2
TM
2931 if (r == 0)
2932 return 0;
2933 if (r < 0)
2934 return log_oom();
2935 } else
8cfa775f 2936 (void) hashmap_remove(c->syscall_filter, INT_TO_PTR(id + 1));
201c1cc2 2937 }
8130926d 2938
201c1cc2
TM
2939 return 0;
2940}
2941
17df7223
LP
2942int config_parse_syscall_filter(
2943 const char *unit,
2944 const char *filename,
2945 unsigned line,
2946 const char *section,
2947 unsigned section_line,
2948 const char *lvalue,
2949 int ltype,
2950 const char *rvalue,
2951 void *data,
2952 void *userdata) {
2953
8351ceae
LP
2954 ExecContext *c = data;
2955 Unit *u = userdata;
b5fb3789 2956 bool invert = false;
8130926d 2957 const char *p;
17df7223 2958 int r;
8351ceae
LP
2959
2960 assert(filename);
2961 assert(lvalue);
2962 assert(rvalue);
2963 assert(u);
2964
74051b9b
LP
2965 if (isempty(rvalue)) {
2966 /* Empty assignment resets the list */
8cfa775f 2967 c->syscall_filter = hashmap_free(c->syscall_filter);
17df7223 2968 c->syscall_whitelist = false;
74051b9b
LP
2969 return 0;
2970 }
2971
8351ceae
LP
2972 if (rvalue[0] == '~') {
2973 invert = true;
2974 rvalue++;
2975 }
2976
17df7223 2977 if (!c->syscall_filter) {
8cfa775f 2978 c->syscall_filter = hashmap_new(NULL);
17df7223
LP
2979 if (!c->syscall_filter)
2980 return log_oom();
2981
c0467cf3 2982 if (invert)
17df7223
LP
2983 /* Allow everything but the ones listed */
2984 c->syscall_whitelist = false;
c0467cf3 2985 else {
17df7223
LP
2986 /* Allow nothing but the ones listed */
2987 c->syscall_whitelist = true;
8351ceae 2988
17df7223 2989 /* Accept default syscalls if we are on a whitelist */
8cfa775f 2990 r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false, -1);
201c1cc2
TM
2991 if (r < 0)
2992 return r;
c0467cf3 2993 }
8351ceae
LP
2994 }
2995
8130926d
LP
2996 p = rvalue;
2997 for (;;) {
8cfa775f
YW
2998 _cleanup_free_ char *word = NULL, *name = NULL;
2999 int num;
8351ceae 3000
8130926d
LP
3001 r = extract_first_word(&p, &word, NULL, 0);
3002 if (r == 0)
3003 break;
3004 if (r == -ENOMEM)
74051b9b 3005 return log_oom();
8130926d
LP
3006 if (r < 0) {
3007 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
3008 break;
3009 }
8351ceae 3010
8cfa775f
YW
3011 r = parse_syscall_and_errno(word, &name, &num);
3012 if (r < 0) {
3013 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse syscall:errno, ignoring: %s", word);
3014 continue;
3015 }
3016
3017 r = syscall_filter_parse_one(unit, filename, line, c, invert, name, true, num);
201c1cc2
TM
3018 if (r < 0)
3019 return r;
c0467cf3
RC
3020 }
3021
17df7223
LP
3022 return 0;
3023}
3024
57183d11
LP
3025int config_parse_syscall_archs(
3026 const char *unit,
3027 const char *filename,
3028 unsigned line,
3029 const char *section,
3030 unsigned section_line,
3031 const char *lvalue,
3032 int ltype,
3033 const char *rvalue,
3034 void *data,
3035 void *userdata) {
3036
d3b1c508 3037 Set **archs = data;
035fe294 3038 const char *p;
57183d11
LP
3039 int r;
3040
3041 if (isempty(rvalue)) {
525d3cc7 3042 *archs = set_free(*archs);
57183d11
LP
3043 return 0;
3044 }
3045
d5099efc 3046 r = set_ensure_allocated(archs, NULL);
57183d11
LP
3047 if (r < 0)
3048 return log_oom();
3049
035fe294
ZJS
3050 for (p = rvalue;;) {
3051 _cleanup_free_ char *word = NULL;
57183d11
LP
3052 uint32_t a;
3053
035fe294
ZJS
3054 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
3055 if (r == 0)
3056 return 0;
3057 if (r == -ENOMEM)
57183d11 3058 return log_oom();
035fe294
ZJS
3059 if (r < 0) {
3060 log_syntax(unit, LOG_WARNING, filename, line, r,
3061 "Invalid syntax, ignoring: %s", rvalue);
3062 return 0;
3063 }
57183d11 3064
035fe294 3065 r = seccomp_arch_from_string(word, &a);
57183d11 3066 if (r < 0) {
035fe294
ZJS
3067 log_syntax(unit, LOG_ERR, filename, line, r,
3068 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
57183d11
LP
3069 continue;
3070 }
3071
d3b1c508 3072 r = set_put(*archs, UINT32_TO_PTR(a + 1));
57183d11
LP
3073 if (r < 0)
3074 return log_oom();
3075 }
57183d11
LP
3076}
3077
17df7223
LP
3078int config_parse_syscall_errno(
3079 const char *unit,
3080 const char *filename,
3081 unsigned line,
3082 const char *section,
3083 unsigned section_line,
3084 const char *lvalue,
3085 int ltype,
3086 const char *rvalue,
3087 void *data,
3088 void *userdata) {
3089
3090 ExecContext *c = data;
3091 int e;
3092
3093 assert(filename);
3094 assert(lvalue);
3095 assert(rvalue);
3096
3097 if (isempty(rvalue)) {
3098 /* Empty assignment resets to KILL */
3099 c->syscall_errno = 0;
3100 return 0;
8351ceae
LP
3101 }
3102
3df90f24
YW
3103 e = parse_errno(rvalue);
3104 if (e <= 0) {
12ca818f 3105 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
17df7223
LP
3106 return 0;
3107 }
8351ceae 3108
17df7223 3109 c->syscall_errno = e;
8351ceae
LP
3110 return 0;
3111}
4298d0b5
LP
3112
3113int config_parse_address_families(
3114 const char *unit,
3115 const char *filename,
3116 unsigned line,
3117 const char *section,
3118 unsigned section_line,
3119 const char *lvalue,
3120 int ltype,
3121 const char *rvalue,
3122 void *data,
3123 void *userdata) {
3124
3125 ExecContext *c = data;
4298d0b5 3126 bool invert = false;
035fe294 3127 const char *p;
4298d0b5
LP
3128 int r;
3129
3130 assert(filename);
3131 assert(lvalue);
3132 assert(rvalue);
4298d0b5
LP
3133
3134 if (isempty(rvalue)) {
3135 /* Empty assignment resets the list */
525d3cc7 3136 c->address_families = set_free(c->address_families);
4298d0b5
LP
3137 c->address_families_whitelist = false;
3138 return 0;
3139 }
3140
3141 if (rvalue[0] == '~') {
3142 invert = true;
3143 rvalue++;
3144 }
3145
3146 if (!c->address_families) {
d5099efc 3147 c->address_families = set_new(NULL);
4298d0b5
LP
3148 if (!c->address_families)
3149 return log_oom();
3150
3151 c->address_families_whitelist = !invert;
3152 }
3153
035fe294
ZJS
3154 for (p = rvalue;;) {
3155 _cleanup_free_ char *word = NULL;
4298d0b5
LP
3156 int af;
3157
035fe294
ZJS
3158 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
3159 if (r == 0)
3160 return 0;
3161 if (r == -ENOMEM)
4298d0b5 3162 return log_oom();
035fe294
ZJS
3163 if (r < 0) {
3164 log_syntax(unit, LOG_WARNING, filename, line, r,
3165 "Invalid syntax, ignoring: %s", rvalue);
3166 return 0;
3167 }
4298d0b5 3168
035fe294 3169 af = af_from_name(word);
4298d0b5 3170 if (af <= 0) {
035fe294
ZJS
3171 log_syntax(unit, LOG_ERR, filename, line, 0,
3172 "Failed to parse address family \"%s\", ignoring: %m", word);
4298d0b5
LP
3173 continue;
3174 }
3175
3176 /* If we previously wanted to forbid an address family and now
035fe294 3177 * we want to allow it, then just remove it from the list.
4298d0b5
LP
3178 */
3179 if (!invert == c->address_families_whitelist) {
3180 r = set_put(c->address_families, INT_TO_PTR(af));
4298d0b5
LP
3181 if (r < 0)
3182 return log_oom();
3183 } else
3184 set_remove(c->address_families, INT_TO_PTR(af));
3185 }
4298d0b5 3186}
add00535
LP
3187
3188int config_parse_restrict_namespaces(
3189 const char *unit,
3190 const char *filename,
3191 unsigned line,
3192 const char *section,
3193 unsigned section_line,
3194 const char *lvalue,
3195 int ltype,
3196 const char *rvalue,
3197 void *data,
3198 void *userdata) {
3199
3200 ExecContext *c = data;
3201 bool invert = false;
3202 int r;
3203
3204 if (isempty(rvalue)) {
3205 /* Reset to the default. */
3206 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
3207 return 0;
3208 }
3209
3210 if (rvalue[0] == '~') {
3211 invert = true;
3212 rvalue++;
3213 }
3214
3215 r = parse_boolean(rvalue);
3216 if (r > 0)
3217 c->restrict_namespaces = 0;
3218 else if (r == 0)
3219 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
3220 else {
3221 /* Not a boolean argument, in this case it's a list of namespace types. */
3222
3223 r = namespace_flag_from_string_many(rvalue, &c->restrict_namespaces);
3224 if (r < 0) {
3225 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
3226 return 0;
3227 }
3228 }
3229
3230 if (invert)
3231 c->restrict_namespaces = (~c->restrict_namespaces) & NAMESPACE_FLAGS_ALL;
3232
3233 return 0;
3234}
c0467cf3 3235#endif
8351ceae 3236
a016b922
LP
3237int config_parse_unit_slice(
3238 const char *unit,
3239 const char *filename,
3240 unsigned line,
3241 const char *section,
71a61510 3242 unsigned section_line,
a016b922
LP
3243 const char *lvalue,
3244 int ltype,
3245 const char *rvalue,
3246 void *data,
3247 void *userdata) {
3248
3249 _cleanup_free_ char *k = NULL;
d79200e2 3250 Unit *u = userdata, *slice = NULL;
a016b922
LP
3251 int r;
3252
3253 assert(filename);
3254 assert(lvalue);
3255 assert(rvalue);
3256 assert(u);
3257
19f6d710 3258 r = unit_name_printf(u, rvalue, &k);
d79200e2
LP
3259 if (r < 0) {
3260 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
3261 return 0;
19f6d710 3262 }
a016b922 3263
19f6d710 3264 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
a016b922 3265 if (r < 0) {
d79200e2 3266 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k);
a016b922
LP
3267 return 0;
3268 }
3269
d79200e2
LP
3270 r = unit_set_slice(u, slice);
3271 if (r < 0) {
3272 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id);
a016b922
LP
3273 return 0;
3274 }
3275
a016b922
LP
3276 return 0;
3277}
3278
4ad49000
LP
3279DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
3280
66ebf6c0
TH
3281int config_parse_cpu_weight(
3282 const char *unit,
3283 const char *filename,
3284 unsigned line,
3285 const char *section,
3286 unsigned section_line,
3287 const char *lvalue,
3288 int ltype,
3289 const char *rvalue,
3290 void *data,
3291 void *userdata) {
3292
3293 uint64_t *weight = data;
3294 int r;
3295
3296 assert(filename);
3297 assert(lvalue);
3298 assert(rvalue);
3299
3300 r = cg_weight_parse(rvalue, weight);
3301 if (r < 0) {
3302 log_syntax(unit, LOG_ERR, filename, line, r, "CPU weight '%s' invalid. Ignoring.", rvalue);
3303 return 0;
3304 }
3305
3306 return 0;
3307}
3308
4ad49000
LP
3309int config_parse_cpu_shares(
3310 const char *unit,
3311 const char *filename,
3312 unsigned line,
3313 const char *section,
71a61510 3314 unsigned section_line,
4ad49000
LP
3315 const char *lvalue,
3316 int ltype,
3317 const char *rvalue,
3318 void *data,
3319 void *userdata) {
3320
d53d9474 3321 uint64_t *shares = data;
95ae05c0
WC
3322 int r;
3323
3324 assert(filename);
3325 assert(lvalue);
3326 assert(rvalue);
3327
d53d9474
LP
3328 r = cg_cpu_shares_parse(rvalue, shares);
3329 if (r < 0) {
3330 log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
3331 return 0;
3332 }
3333
4ad49000
LP
3334 return 0;
3335}
3336
b2f8b02e
LP
3337int config_parse_cpu_quota(
3338 const char *unit,
3339 const char *filename,
3340 unsigned line,
3341 const char *section,
3342 unsigned section_line,
3343 const char *lvalue,
3344 int ltype,
3345 const char *rvalue,
3346 void *data,
3347 void *userdata) {
3348
3349 CGroupContext *c = data;
9184ca48 3350 int r;
b2f8b02e
LP
3351
3352 assert(filename);
3353 assert(lvalue);
3354 assert(rvalue);
3355
3356 if (isempty(rvalue)) {
3a43da28 3357 c->cpu_quota_per_sec_usec = USEC_INFINITY;
b2f8b02e
LP
3358 return 0;
3359 }
3360
5124866d 3361 r = parse_percent_unbounded(rvalue);
9184ca48
LP
3362 if (r <= 0) {
3363 log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
9a054909 3364 return 0;
b2f8b02e
LP
3365 }
3366
9184ca48 3367 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
b2f8b02e
LP
3368 return 0;
3369}
3370
4ad49000
LP
3371int config_parse_memory_limit(
3372 const char *unit,
3373 const char *filename,
3374 unsigned line,
3375 const char *section,
71a61510 3376 unsigned section_line,
4ad49000
LP
3377 const char *lvalue,
3378 int ltype,
3379 const char *rvalue,
3380 void *data,
3381 void *userdata) {
3382
3383 CGroupContext *c = data;
da4d897e 3384 uint64_t bytes = CGROUP_LIMIT_MAX;
4ad49000
LP
3385 int r;
3386
e57c9ce1 3387 if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
875ae566
LP
3388
3389 r = parse_percent(rvalue);
3390 if (r < 0) {
3391 r = parse_size(rvalue, 1024, &bytes);
3392 if (r < 0) {
3393 log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
3394 return 0;
3395 }
3396 } else
d8cf2ac7 3397 bytes = physical_memory_scale(r, 100U);
875ae566 3398
b3785cd5
LP
3399 if (bytes <= 0 || bytes >= UINT64_MAX) {
3400 log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range. Ignoring.", rvalue);
da4d897e
TH
3401 return 0;
3402 }
4ad49000
LP
3403 }
3404
da4d897e
TH
3405 if (streq(lvalue, "MemoryLow"))
3406 c->memory_low = bytes;
3407 else if (streq(lvalue, "MemoryHigh"))
3408 c->memory_high = bytes;
3409 else if (streq(lvalue, "MemoryMax"))
3410 c->memory_max = bytes;
96e131ea
WC
3411 else if (streq(lvalue, "MemorySwapMax"))
3412 c->memory_swap_max = bytes;
3413 else if (streq(lvalue, "MemoryLimit"))
da4d897e 3414 c->memory_limit = bytes;
96e131ea
WC
3415 else
3416 return -EINVAL;
4ad49000 3417
4ad49000
LP
3418 return 0;
3419}
3420
03a7b521
LP
3421int config_parse_tasks_max(
3422 const char *unit,
3423 const char *filename,
3424 unsigned line,
3425 const char *section,
3426 unsigned section_line,
3427 const char *lvalue,
3428 int ltype,
3429 const char *rvalue,
3430 void *data,
3431 void *userdata) {
3432
f5058264
TH
3433 uint64_t *tasks_max = data, v;
3434 Unit *u = userdata;
03a7b521
LP
3435 int r;
3436
f5058264
TH
3437 if (isempty(rvalue)) {
3438 *tasks_max = u->manager->default_tasks_max;
3439 return 0;
3440 }
3441
3442 if (streq(rvalue, "infinity")) {
3443 *tasks_max = CGROUP_LIMIT_MAX;
03a7b521
LP
3444 return 0;
3445 }
3446
83f8e808
LP
3447 r = parse_percent(rvalue);
3448 if (r < 0) {
f5058264 3449 r = safe_atou64(rvalue, &v);
83f8e808
LP
3450 if (r < 0) {
3451 log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
3452 return 0;
3453 }
3454 } else
f5058264 3455 v = system_tasks_max_scale(r, 100U);
83f8e808 3456
f5058264 3457 if (v <= 0 || v >= UINT64_MAX) {
83f8e808 3458 log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue);
03a7b521
LP
3459 return 0;
3460 }
3461
f5058264 3462 *tasks_max = v;
03a7b521
LP
3463 return 0;
3464}
3465
02638280
LP
3466int config_parse_delegate(
3467 const char *unit,
3468 const char *filename,
3469 unsigned line,
3470 const char *section,
3471 unsigned section_line,
3472 const char *lvalue,
3473 int ltype,
3474 const char *rvalue,
3475 void *data,
3476 void *userdata) {
3477
3478 CGroupContext *c = data;
3479 int r;
3480
3481 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3482 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3483 * mask to delegate. */
3484
1bdfc7b9
YW
3485 if (isempty(rvalue)) {
3486 c->delegate = true;
3487 c->delegate_controllers = 0;
3488 return 0;
3489 }
3490
02638280
LP
3491 r = parse_boolean(rvalue);
3492 if (r < 0) {
3493 const char *p = rvalue;
3494 CGroupMask mask = 0;
3495
3496 for (;;) {
3497 _cleanup_free_ char *word = NULL;
3498 CGroupController cc;
3499
3500 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
3501 if (r == 0)
3502 break;
3503 if (r == -ENOMEM)
3504 return log_oom();
3505 if (r < 0) {
3506 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
3507 return r;
3508 }
3509
3510 cc = cgroup_controller_from_string(word);
3511 if (cc < 0) {
3512 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid controller name '%s', ignoring", rvalue);
3513 continue;
3514 }
3515
3516 mask |= CGROUP_CONTROLLER_TO_MASK(cc);
3517 }
3518
3519 c->delegate = true;
3520 c->delegate_controllers |= mask;
3521
3522 } else if (r > 0) {
3523 c->delegate = true;
3524 c->delegate_controllers = _CGROUP_MASK_ALL;
3525 } else {
3526 c->delegate = false;
3527 c->delegate_controllers = 0;
3528 }
3529
3530 return 0;
3531}
3532
4ad49000
LP
3533int config_parse_device_allow(
3534 const char *unit,
3535 const char *filename,
3536 unsigned line,
3537 const char *section,
71a61510 3538 unsigned section_line,
4ad49000
LP
3539 const char *lvalue,
3540 int ltype,
3541 const char *rvalue,
3542 void *data,
3543 void *userdata) {
3544
1116e14c 3545 _cleanup_free_ char *path = NULL, *t = NULL;
4ad49000
LP
3546 CGroupContext *c = data;
3547 CGroupDeviceAllow *a;
1116e14c 3548 const char *m = NULL;
4ad49000 3549 size_t n;
1116e14c 3550 int r;
4ad49000
LP
3551
3552 if (isempty(rvalue)) {
3553 while (c->device_allow)
3554 cgroup_context_free_device_allow(c, c->device_allow);
3555
3556 return 0;
3557 }
3558
1116e14c 3559 r = unit_full_printf(userdata, rvalue, &t);
508f63b4 3560 if (r < 0) {
1116e14c
NBS
3561 log_syntax(unit, LOG_WARNING, filename, line, r,
3562 "Failed to resolve specifiers in %s, ignoring: %m",
3563 rvalue);
3564 }
3565
3566 n = strcspn(t, WHITESPACE);
3567
3568 path = strndup(t, n);
4ad49000
LP
3569 if (!path)
3570 return log_oom();
3571
3ccb8862 3572 if (!is_deviceallow_pattern(path)) {
12ca818f 3573 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
3574 return 0;
3575 }
3576
1116e14c 3577 m = t + n + strspn(t + n, WHITESPACE);
4ad49000
LP
3578 if (isempty(m))
3579 m = "rwm";
3580
3581 if (!in_charset(m, "rwm")) {
12ca818f 3582 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
4ad49000
LP
3583 return 0;
3584 }
3585
3586 a = new0(CGroupDeviceAllow, 1);
3587 if (!a)
3588 return log_oom();
3589
3590 a->path = path;
3591 path = NULL;
3592 a->r = !!strchr(m, 'r');
3593 a->w = !!strchr(m, 'w');
3594 a->m = !!strchr(m, 'm');
3595
71fda00f 3596 LIST_PREPEND(device_allow, c->device_allow, a);
4ad49000
LP
3597 return 0;
3598}
3599
13c31542
TH
3600int config_parse_io_weight(
3601 const char *unit,
3602 const char *filename,
3603 unsigned line,
3604 const char *section,
3605 unsigned section_line,
3606 const char *lvalue,
3607 int ltype,
3608 const char *rvalue,
3609 void *data,
3610 void *userdata) {
3611
3612 uint64_t *weight = data;
3613 int r;
3614
3615 assert(filename);
3616 assert(lvalue);
3617 assert(rvalue);
3618
3619 r = cg_weight_parse(rvalue, weight);
3620 if (r < 0) {
3621 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue);
3622 return 0;
3623 }
3624
3625 return 0;
3626}
3627
3628int config_parse_io_device_weight(
3629 const char *unit,
3630 const char *filename,
3631 unsigned line,
3632 const char *section,
3633 unsigned section_line,
3634 const char *lvalue,
3635 int ltype,
3636 const char *rvalue,
3637 void *data,
3638 void *userdata) {
3639
3640 _cleanup_free_ char *path = NULL;
3641 CGroupIODeviceWeight *w;
3642 CGroupContext *c = data;
3643 const char *weight;
3644 uint64_t u;
3645 size_t n;
3646 int r;
3647
3648 assert(filename);
3649 assert(lvalue);
3650 assert(rvalue);
3651
3652 if (isempty(rvalue)) {
3653 while (c->io_device_weights)
3654 cgroup_context_free_io_device_weight(c, c->io_device_weights);
3655
3656 return 0;
3657 }
3658
3659 n = strcspn(rvalue, WHITESPACE);
3660 weight = rvalue + n;
3661 weight += strspn(weight, WHITESPACE);
3662
3663 if (isempty(weight)) {
3664 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
3665 return 0;
3666 }
3667
3668 path = strndup(rvalue, n);
3669 if (!path)
3670 return log_oom();
3671
3672 if (!path_startswith(path, "/dev")) {
3673 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3674 return 0;
3675 }
3676
3677 r = cg_weight_parse(weight, &u);
3678 if (r < 0) {
3679 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight);
3680 return 0;
3681 }
3682
3683 assert(u != CGROUP_WEIGHT_INVALID);
3684
3685 w = new0(CGroupIODeviceWeight, 1);
3686 if (!w)
3687 return log_oom();
3688
3689 w->path = path;
3690 path = NULL;
3691
3692 w->weight = u;
3693
3694 LIST_PREPEND(device_weights, c->io_device_weights, w);
3695 return 0;
3696}
3697
3698int config_parse_io_limit(
3699 const char *unit,
3700 const char *filename,
3701 unsigned line,
3702 const char *section,
3703 unsigned section_line,
3704 const char *lvalue,
3705 int ltype,
3706 const char *rvalue,
3707 void *data,
3708 void *userdata) {
3709
3710 _cleanup_free_ char *path = NULL;
3711 CGroupIODeviceLimit *l = NULL, *t;
3712 CGroupContext *c = data;
9be57249 3713 CGroupIOLimitType type;
13c31542
TH
3714 const char *limit;
3715 uint64_t num;
13c31542
TH
3716 size_t n;
3717 int r;
3718
3719 assert(filename);
3720 assert(lvalue);
3721 assert(rvalue);
3722
9be57249
TH
3723 type = cgroup_io_limit_type_from_string(lvalue);
3724 assert(type >= 0);
13c31542
TH
3725
3726 if (isempty(rvalue)) {
3727 LIST_FOREACH(device_limits, l, c->io_device_limits)
9be57249 3728 l->limits[type] = cgroup_io_limit_defaults[type];
13c31542
TH
3729 return 0;
3730 }
3731
3732 n = strcspn(rvalue, WHITESPACE);
3733 limit = rvalue + n;
3734 limit += strspn(limit, WHITESPACE);
3735
3736 if (!*limit) {
3737 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3738 return 0;
3739 }
3740
3741 path = strndup(rvalue, n);
3742 if (!path)
3743 return log_oom();
3744
3745 if (!path_startswith(path, "/dev")) {
3746 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3747 return 0;
3748 }
3749
e57c9ce1 3750 if (streq("infinity", limit)) {
13c31542
TH
3751 num = CGROUP_LIMIT_MAX;
3752 } else {
3753 r = parse_size(limit, 1000, &num);
3754 if (r < 0 || num <= 0) {
3755 log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue);
3756 return 0;
3757 }
3758 }
3759
3760 LIST_FOREACH(device_limits, t, c->io_device_limits) {
3761 if (path_equal(path, t->path)) {
3762 l = t;
3763 break;
3764 }
3765 }
3766
3767 if (!l) {
9be57249
TH
3768 CGroupIOLimitType ttype;
3769
13c31542
TH
3770 l = new0(CGroupIODeviceLimit, 1);
3771 if (!l)
3772 return log_oom();
3773
3774 l->path = path;
3775 path = NULL;
9be57249
TH
3776 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
3777 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
13c31542
TH
3778
3779 LIST_PREPEND(device_limits, c->io_device_limits, l);
3780 }
3781
9be57249 3782 l->limits[type] = num;
13c31542
TH
3783
3784 return 0;
3785}
3786
4ad49000
LP
3787int config_parse_blockio_weight(
3788 const char *unit,
3789 const char *filename,
3790 unsigned line,
3791 const char *section,
71a61510 3792 unsigned section_line,
4ad49000
LP
3793 const char *lvalue,
3794 int ltype,
3795 const char *rvalue,
3796 void *data,
3797 void *userdata) {
3798
d53d9474 3799 uint64_t *weight = data;
95ae05c0
WC
3800 int r;
3801
3802 assert(filename);
3803 assert(lvalue);
3804 assert(rvalue);
3805
d53d9474
LP
3806 r = cg_blkio_weight_parse(rvalue, weight);
3807 if (r < 0) {
3808 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
3809 return 0;
3810 }
3811
8e7076ca
LP
3812 return 0;
3813}
3814
3815int config_parse_blockio_device_weight(
3816 const char *unit,
3817 const char *filename,
3818 unsigned line,
3819 const char *section,
71a61510 3820 unsigned section_line,
8e7076ca
LP
3821 const char *lvalue,
3822 int ltype,
3823 const char *rvalue,
3824 void *data,
3825 void *userdata) {
3826
4ad49000 3827 _cleanup_free_ char *path = NULL;
8e7076ca 3828 CGroupBlockIODeviceWeight *w;
4ad49000 3829 CGroupContext *c = data;
4ad49000 3830 const char *weight;
d53d9474 3831 uint64_t u;
4ad49000
LP
3832 size_t n;
3833 int r;
3834
3835 assert(filename);
3836 assert(lvalue);
3837 assert(rvalue);
3838
3839 if (isempty(rvalue)) {
4ad49000
LP
3840 while (c->blockio_device_weights)
3841 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
3842
3843 return 0;
3844 }
3845
3846 n = strcspn(rvalue, WHITESPACE);
3847 weight = rvalue + n;
d53d9474
LP
3848 weight += strspn(weight, WHITESPACE);
3849
3850 if (isempty(weight)) {
12ca818f 3851 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
8e7076ca
LP
3852 return 0;
3853 }
4ad49000 3854
8e7076ca
LP
3855 path = strndup(rvalue, n);
3856 if (!path)
3857 return log_oom();
4ad49000 3858
8e7076ca 3859 if (!path_startswith(path, "/dev")) {
12ca818f 3860 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
8e7076ca
LP
3861 return 0;
3862 }
4ad49000 3863
d53d9474
LP
3864 r = cg_blkio_weight_parse(weight, &u);
3865 if (r < 0) {
3866 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
4ad49000
LP
3867 return 0;
3868 }
3869
d53d9474
LP
3870 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
3871
8e7076ca
LP
3872 w = new0(CGroupBlockIODeviceWeight, 1);
3873 if (!w)
3874 return log_oom();
4ad49000 3875
8e7076ca
LP
3876 w->path = path;
3877 path = NULL;
4ad49000 3878
d53d9474 3879 w->weight = u;
4ad49000 3880
71fda00f 3881 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
3882 return 0;
3883}
3884
3885int config_parse_blockio_bandwidth(
3886 const char *unit,
3887 const char *filename,
3888 unsigned line,
3889 const char *section,
71a61510 3890 unsigned section_line,
4ad49000
LP
3891 const char *lvalue,
3892 int ltype,
3893 const char *rvalue,
3894 void *data,
3895 void *userdata) {
3896
3897 _cleanup_free_ char *path = NULL;
979d0311 3898 CGroupBlockIODeviceBandwidth *b = NULL, *t;
4ad49000
LP
3899 CGroupContext *c = data;
3900 const char *bandwidth;
59f448cf 3901 uint64_t bytes;
47c0980d 3902 bool read;
4ad49000
LP
3903 size_t n;
3904 int r;
3905
3906 assert(filename);
3907 assert(lvalue);
3908 assert(rvalue);
3909
47c0980d
G
3910 read = streq("BlockIOReadBandwidth", lvalue);
3911
4ad49000 3912 if (isempty(rvalue)) {
979d0311
TH
3913 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
3914 b->rbps = CGROUP_LIMIT_MAX;
3915 b->wbps = CGROUP_LIMIT_MAX;
3916 }
4ad49000
LP
3917 return 0;
3918 }
3919
3920 n = strcspn(rvalue, WHITESPACE);
3921 bandwidth = rvalue + n;
3922 bandwidth += strspn(bandwidth, WHITESPACE);
3923
3924 if (!*bandwidth) {
12ca818f 3925 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
4ad49000
LP
3926 return 0;
3927 }
3928
3929 path = strndup(rvalue, n);
3930 if (!path)
3931 return log_oom();
3932
3933 if (!path_startswith(path, "/dev")) {
12ca818f 3934 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
3935 return 0;
3936 }
3937
5556b5fe 3938 r = parse_size(bandwidth, 1000, &bytes);
4ad49000 3939 if (r < 0 || bytes <= 0) {
12ca818f 3940 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
3941 return 0;
3942 }
3943
979d0311
TH
3944 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
3945 if (path_equal(path, t->path)) {
3946 b = t;
3947 break;
3948 }
3949 }
4ad49000 3950
979d0311
TH
3951 if (!t) {
3952 b = new0(CGroupBlockIODeviceBandwidth, 1);
3953 if (!b)
3954 return log_oom();
3955
3956 b->path = path;
3957 path = NULL;
3958 b->rbps = CGROUP_LIMIT_MAX;
3959 b->wbps = CGROUP_LIMIT_MAX;
3960
3961 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
3962 }
4ad49000 3963
979d0311
TH
3964 if (read)
3965 b->rbps = bytes;
3966 else
3967 b->wbps = bytes;
4ad49000
LP
3968
3969 return 0;
3970}
3971
d420282b
LP
3972DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
3973
3974int config_parse_job_mode_isolate(
3975 const char *unit,
3976 const char *filename,
3977 unsigned line,
3978 const char *section,
3979 unsigned section_line,
3980 const char *lvalue,
3981 int ltype,
3982 const char *rvalue,
3983 void *data,
3984 void *userdata) {
3985
3986 JobMode *m = data;
3987 int r;
3988
3989 assert(filename);
3990 assert(lvalue);
3991 assert(rvalue);
3992
3993 r = parse_boolean(rvalue);
3994 if (r < 0) {
12ca818f 3995 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
d420282b
LP
3996 return 0;
3997 }
3998
3999 *m = r ? JOB_ISOLATE : JOB_REPLACE;
4000 return 0;
4001}
4002
53f47dfc
YW
4003DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
4004
3536f49e 4005int config_parse_exec_directories(
e66cf1a3
LP
4006 const char *unit,
4007 const char *filename,
4008 unsigned line,
4009 const char *section,
4010 unsigned section_line,
4011 const char *lvalue,
4012 int ltype,
4013 const char *rvalue,
4014 void *data,
4015 void *userdata) {
4016
a2a5291b 4017 char***rt = data;
9b5864d9 4018 Unit *u = userdata;
035fe294 4019 const char *p;
e66cf1a3
LP
4020 int r;
4021
4022 assert(filename);
4023 assert(lvalue);
4024 assert(rvalue);
4025 assert(data);
4026
4027 if (isempty(rvalue)) {
4028 /* Empty assignment resets the list */
6796073e 4029 *rt = strv_free(*rt);
e66cf1a3
LP
4030 return 0;
4031 }
4032
035fe294
ZJS
4033 for (p = rvalue;;) {
4034 _cleanup_free_ char *word = NULL, *k = NULL;
e66cf1a3 4035
035fe294 4036 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
035fe294 4037 if (r == -ENOMEM)
e66cf1a3 4038 return log_oom();
035fe294
ZJS
4039 if (r < 0) {
4040 log_syntax(unit, LOG_WARNING, filename, line, r,
4041 "Invalid syntax, ignoring: %s", rvalue);
4042 return 0;
4043 }
091e9efe
LP
4044 if (r == 0)
4045 return 0;
e66cf1a3 4046
18913df9 4047 r = unit_full_printf(u, word, &k);
9b5864d9 4048 if (r < 0) {
035fe294
ZJS
4049 log_syntax(unit, LOG_ERR, filename, line, r,
4050 "Failed to resolve specifiers in \"%s\", ignoring: %m", word);
9b5864d9
MG
4051 continue;
4052 }
4053
99be45a4 4054 if (!path_is_normalized(k) || path_is_absolute(k)) {
035fe294 4055 log_syntax(unit, LOG_ERR, filename, line, 0,
091e9efe 4056 "%s= path is not valid, ignoring assignment: %s", lvalue, rvalue);
e66cf1a3
LP
4057 continue;
4058 }
4059
035fe294 4060 r = strv_push(rt, k);
e66cf1a3
LP
4061 if (r < 0)
4062 return log_oom();
035fe294 4063 k = NULL;
e66cf1a3 4064 }
e66cf1a3
LP
4065}
4066
3af00fb8
LP
4067int config_parse_set_status(
4068 const char *unit,
4069 const char *filename,
4070 unsigned line,
4071 const char *section,
4072 unsigned section_line,
4073 const char *lvalue,
4074 int ltype,
4075 const char *rvalue,
4076 void *data,
4077 void *userdata) {
4078
3af00fb8 4079 size_t l;
a2a5291b 4080 const char *word, *state;
3af00fb8
LP
4081 int r;
4082 ExitStatusSet *status_set = data;
4083
4084 assert(filename);
4085 assert(lvalue);
4086 assert(rvalue);
4087 assert(data);
4088
3e2d435b 4089 /* Empty assignment resets the list */
3af00fb8 4090 if (isempty(rvalue)) {
3e2d435b 4091 exit_status_set_free(status_set);
3af00fb8
LP
4092 return 0;
4093 }
4094
a2a5291b 4095 FOREACH_WORD(word, l, rvalue, state) {
3af00fb8
LP
4096 _cleanup_free_ char *temp;
4097 int val;
61593865 4098 Set **set;
3af00fb8 4099
a2a5291b 4100 temp = strndup(word, l);
3af00fb8
LP
4101 if (!temp)
4102 return log_oom();
4103
4104 r = safe_atoi(temp, &val);
4105 if (r < 0) {
4106 val = signal_from_string_try_harder(temp);
4107
1e2fd62d 4108 if (val <= 0) {
12ca818f 4109 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
61593865 4110 continue;
3af00fb8 4111 }
61593865 4112 set = &status_set->signal;
3af00fb8 4113 } else {
1e2fd62d 4114 if (val < 0 || val > 255) {
12ca818f 4115 log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
1e2fd62d 4116 continue;
3af00fb8 4117 }
61593865 4118 set = &status_set->status;
3af00fb8 4119 }
1e2fd62d 4120
61593865 4121 r = set_ensure_allocated(set, NULL);
1e2fd62d
ZJS
4122 if (r < 0)
4123 return log_oom();
4124
61593865 4125 r = set_put(*set, INT_TO_PTR(val));
1e2fd62d 4126 if (r < 0) {
12ca818f 4127 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
1e2fd62d
ZJS
4128 return r;
4129 }
3af00fb8 4130 }
b2fadec6 4131 if (!isempty(state))
12ca818f 4132 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3af00fb8
LP
4133
4134 return 0;
4135}
4136
94828d2d
LP
4137int config_parse_namespace_path_strv(
4138 const char *unit,
4139 const char *filename,
4140 unsigned line,
4141 const char *section,
4142 unsigned section_line,
4143 const char *lvalue,
4144 int ltype,
4145 const char *rvalue,
4146 void *data,
4147 void *userdata) {
4148
7b07e993 4149 Unit *u = userdata;
a2a5291b 4150 char*** sv = data;
727f76d7 4151 const char *cur;
94828d2d
LP
4152 int r;
4153
4154 assert(filename);
4155 assert(lvalue);
4156 assert(rvalue);
4157 assert(data);
4158
4159 if (isempty(rvalue)) {
4160 /* Empty assignment resets the list */
6796073e 4161 *sv = strv_free(*sv);
94828d2d
LP
4162 return 0;
4163 }
4164
7b07e993 4165 cur = rvalue;
727f76d7 4166 for (;;) {
7b07e993 4167 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
20b7a007
LP
4168 const char *w;
4169 bool ignore_enoent = false, shall_prefix = false;
94828d2d 4170
727f76d7 4171 r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
0293a7a8
EV
4172 if (r == 0)
4173 break;
4174 if (r == -ENOMEM)
4175 return log_oom();
727f76d7 4176 if (r < 0) {
7b07e993 4177 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
727f76d7
EV
4178 return 0;
4179 }
94828d2d 4180
727f76d7
EV
4181 if (!utf8_is_valid(word)) {
4182 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
94828d2d
LP
4183 continue;
4184 }
4185
20b7a007
LP
4186 w = word;
4187 if (startswith(w, "-")) {
4188 ignore_enoent = true;
4189 w++;
4190 }
4191 if (startswith(w, "+")) {
4192 shall_prefix = true;
4193 w++;
4194 }
7b07e993 4195
20b7a007 4196 r = unit_full_printf(u, w, &resolved);
7b07e993
LP
4197 if (r < 0) {
4198 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word);
94828d2d
LP
4199 continue;
4200 }
4201
7b07e993
LP
4202 if (!path_is_absolute(resolved)) {
4203 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", resolved);
4204 continue;
4205 }
4206
4207 path_kill_slashes(resolved);
94828d2d 4208
20b7a007
LP
4209 joined = strjoin(ignore_enoent ? "-" : "",
4210 shall_prefix ? "+" : "",
4211 resolved);
7b07e993
LP
4212
4213 r = strv_push(sv, joined);
94828d2d
LP
4214 if (r < 0)
4215 return log_oom();
4216
7b07e993 4217 joined = NULL;
94828d2d
LP
4218 }
4219
4220 return 0;
4221}
4222
d2d6c096
LP
4223int config_parse_bind_paths(
4224 const char *unit,
4225 const char *filename,
4226 unsigned line,
4227 const char *section,
4228 unsigned section_line,
4229 const char *lvalue,
4230 int ltype,
4231 const char *rvalue,
4232 void *data,
4233 void *userdata) {
4234
4235 ExecContext *c = data;
42d43f21 4236 Unit *u = userdata;
d2d6c096
LP
4237 const char *p;
4238 int r;
4239
4240 assert(filename);
4241 assert(lvalue);
4242 assert(rvalue);
4243 assert(data);
4244
4245 if (isempty(rvalue)) {
4246 /* Empty assignment resets the list */
4247 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
4248 c->bind_mounts = NULL;
4249 c->n_bind_mounts = 0;
4250 return 0;
4251 }
4252
4253 p = rvalue;
4254 for (;;) {
4255 _cleanup_free_ char *source = NULL, *destination = NULL;
42d43f21 4256 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
d2d6c096
LP
4257 char *s = NULL, *d = NULL;
4258 bool rbind = true, ignore_enoent = false;
4259
4260 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
4261 if (r == 0)
4262 break;
4263 if (r == -ENOMEM)
4264 return log_oom();
4265 if (r < 0) {
4266 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
4267 return 0;
4268 }
4269
42d43f21
DC
4270 r = unit_full_printf(u, source, &sresolved);
4271 if (r < 0) {
4272 log_syntax(unit, LOG_ERR, filename, line, r,
4273 "Failed to resolved specifiers in \"%s\", ignoring: %m", source);
4274 return 0;
4275 }
4276
4277 s = sresolved;
d2d6c096
LP
4278 if (s[0] == '-') {
4279 ignore_enoent = true;
4280 s++;
4281 }
4282
4283 if (!utf8_is_valid(s)) {
4284 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, s);
4285 return 0;
4286 }
4287 if (!path_is_absolute(s)) {
4288 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute source path, ignoring: %s", s);
4289 return 0;
4290 }
4291
4292 path_kill_slashes(s);
4293
4294 /* Optionally, the destination is specified. */
4295 if (p && p[-1] == ':') {
4296 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
4297 if (r == -ENOMEM)
4298 return log_oom();
4299 if (r < 0) {
4300 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
4301 return 0;
4302 }
4303 if (r == 0) {
4304 log_syntax(unit, LOG_ERR, filename, line, 0, "Missing argument after ':': %s", rvalue);
4305 return 0;
4306 }
4307
42d43f21
DC
4308 r = unit_full_printf(u, destination, &dresolved);
4309 if (r < 0) {
4310 log_syntax(unit, LOG_ERR, filename, line, r,
4311 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination);
4312 return 0;
4313 }
4314
4315 if (!utf8_is_valid(dresolved)) {
4316 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, dresolved);
d2d6c096
LP
4317 return 0;
4318 }
42d43f21
DC
4319 if (!path_is_absolute(dresolved)) {
4320 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute destination path, ignoring: %s", dresolved);
d2d6c096
LP
4321 return 0;
4322 }
4323
42d43f21 4324 d = path_kill_slashes(dresolved);
d2d6c096
LP
4325
4326 /* Optionally, there's also a short option string specified */
4327 if (p && p[-1] == ':') {
4328 _cleanup_free_ char *options = NULL;
4329
4330 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
4331 if (r == -ENOMEM)
4332 return log_oom();
4333 if (r < 0) {
4334 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
4335 return 0;
4336 }
4337
4338 if (isempty(options) || streq(options, "rbind"))
4339 rbind = true;
4340 else if (streq(options, "norbind"))
4341 rbind = false;
4342 else {
4343 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
4344 return 0;
4345 }
4346 }
4347 } else
4348 d = s;
4349
4350 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
4351 &(BindMount) {
4352 .source = s,
4353 .destination = d,
4354 .read_only = !!strstr(lvalue, "ReadOnly"),
4355 .recursive = rbind,
4356 .ignore_enoent = ignore_enoent,
4357 });
4358 if (r < 0)
4359 return log_oom();
4360 }
4361
4362 return 0;
4363}
4364
f1721625 4365int config_parse_no_new_privileges(
760b9d7c
LP
4366 const char* unit,
4367 const char *filename,
4368 unsigned line,
4369 const char *section,
4370 unsigned section_line,
4371 const char *lvalue,
4372 int ltype,
4373 const char *rvalue,
4374 void *data,
4375 void *userdata) {
4376
4377 ExecContext *c = data;
4378 int k;
4379
4380 assert(filename);
4381 assert(lvalue);
4382 assert(rvalue);
4383 assert(data);
4384
4385 k = parse_boolean(rvalue);
4386 if (k < 0) {
12ca818f 4387 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
760b9d7c
LP
4388 return 0;
4389 }
4390
9b232d32 4391 c->no_new_privileges = k;
760b9d7c
LP
4392
4393 return 0;
4394}
4395
1b8689f9 4396int config_parse_protect_home(
417116f2
LP
4397 const char* unit,
4398 const char *filename,
4399 unsigned line,
4400 const char *section,
4401 unsigned section_line,
4402 const char *lvalue,
4403 int ltype,
4404 const char *rvalue,
4405 void *data,
4406 void *userdata) {
4407
4408 ExecContext *c = data;
4409 int k;
4410
4411 assert(filename);
4412 assert(lvalue);
4413 assert(rvalue);
4414 assert(data);
4415
4416 /* Our enum shall be a superset of booleans, hence first try
61233823 4417 * to parse as boolean, and then as enum */
417116f2
LP
4418
4419 k = parse_boolean(rvalue);
4420 if (k > 0)
1b8689f9 4421 c->protect_home = PROTECT_HOME_YES;
417116f2 4422 else if (k == 0)
1b8689f9 4423 c->protect_home = PROTECT_HOME_NO;
417116f2 4424 else {
1b8689f9 4425 ProtectHome h;
417116f2 4426
1b8689f9 4427 h = protect_home_from_string(rvalue);
9ed794a3 4428 if (h < 0) {
12ca818f 4429 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
417116f2
LP
4430 return 0;
4431 }
4432
1b8689f9
LP
4433 c->protect_home = h;
4434 }
4435
4436 return 0;
4437}
4438
4439int config_parse_protect_system(
4440 const char* unit,
4441 const char *filename,
4442 unsigned line,
4443 const char *section,
4444 unsigned section_line,
4445 const char *lvalue,
4446 int ltype,
4447 const char *rvalue,
4448 void *data,
4449 void *userdata) {
4450
4451 ExecContext *c = data;
4452 int k;
4453
4454 assert(filename);
4455 assert(lvalue);
4456 assert(rvalue);
4457 assert(data);
4458
4459 /* Our enum shall be a superset of booleans, hence first try
61233823 4460 * to parse as boolean, and then as enum */
1b8689f9
LP
4461
4462 k = parse_boolean(rvalue);
4463 if (k > 0)
4464 c->protect_system = PROTECT_SYSTEM_YES;
4465 else if (k == 0)
4466 c->protect_system = PROTECT_SYSTEM_NO;
4467 else {
4468 ProtectSystem s;
4469
4470 s = protect_system_from_string(rvalue);
9ed794a3 4471 if (s < 0) {
12ca818f 4472 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
1b8689f9
LP
4473 return 0;
4474 }
4475
4476 c->protect_system = s;
417116f2
LP
4477 }
4478
4479 return 0;
4480}
4481
b1edf445
LP
4482DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
4483
eae51da3
LP
4484int config_parse_job_timeout_sec(
4485 const char* unit,
4486 const char *filename,
4487 unsigned line,
4488 const char *section,
4489 unsigned section_line,
4490 const char *lvalue,
4491 int ltype,
4492 const char *rvalue,
4493 void *data,
4494 void *userdata) {
4495
4496 Unit *u = data;
4497 usec_t usec;
4498 int r;
4499
4500 assert(filename);
4501 assert(lvalue);
4502 assert(rvalue);
4503 assert(u);
4504
4505 r = parse_sec_fix_0(rvalue, &usec);
4506 if (r < 0) {
4507 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue);
4508 return 0;
4509 }
4510
4511 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
c05f3c8f 4512 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
eae51da3
LP
4513 * count. */
4514
4515 if (!u->job_running_timeout_set)
4516 u->job_running_timeout = usec;
4517
4518 u->job_timeout = usec;
4519
4520 return 0;
4521}
4522
4523int config_parse_job_running_timeout_sec(
4524 const char* unit,
4525 const char *filename,
4526 unsigned line,
4527 const char *section,
4528 unsigned section_line,
4529 const char *lvalue,
4530 int ltype,
4531 const char *rvalue,
4532 void *data,
4533 void *userdata) {
4534
4535 Unit *u = data;
4536 usec_t usec;
4537 int r;
4538
4539 assert(filename);
4540 assert(lvalue);
4541 assert(rvalue);
4542 assert(u);
4543
4544 r = parse_sec_fix_0(rvalue, &usec);
4545 if (r < 0) {
4546 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue);
4547 return 0;
4548 }
4549
4550 u->job_running_timeout = usec;
4551 u->job_running_timeout_set = true;
4552
4553 return 0;
4554}
4555
071830ff 4556#define FOLLOW_MAX 8
87f0e418 4557
9e2f7c11 4558static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
a837f088 4559 char *id = NULL;
0301abf4 4560 unsigned c = 0;
87f0e418
LP
4561 int fd, r;
4562 FILE *f;
87f0e418
LP
4563
4564 assert(filename);
4565 assert(*filename);
4566 assert(_f);
4567 assert(names);
4568
0301abf4
LP
4569 /* This will update the filename pointer if the loaded file is
4570 * reached by a symlink. The old string will be freed. */
87f0e418 4571
0301abf4 4572 for (;;) {
2c7108c4 4573 char *target, *name;
87f0e418 4574
0301abf4
LP
4575 if (c++ >= FOLLOW_MAX)
4576 return -ELOOP;
4577
b08d03ff
LP
4578 path_kill_slashes(*filename);
4579
87f0e418 4580 /* Add the file name we are currently looking at to
8f05424d
LP
4581 * the names of this unit, but only if it is a valid
4582 * unit name. */
2b6bf07d 4583 name = basename(*filename);
7410616c 4584 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
8f05424d 4585
15e11d81
LP
4586 id = set_get(names, name);
4587 if (!id) {
4588 id = strdup(name);
4589 if (!id)
8f05424d 4590 return -ENOMEM;
87f0e418 4591
ef42202a
ZJS
4592 r = set_consume(names, id);
4593 if (r < 0)
8f05424d 4594 return r;
87f0e418 4595 }
87f0e418
LP
4596 }
4597
0301abf4 4598 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
4599 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
4600 if (fd >= 0)
87f0e418
LP
4601 break;
4602
0301abf4
LP
4603 if (errno != ELOOP)
4604 return -errno;
4605
87f0e418 4606 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
4607 r = readlink_and_make_absolute(*filename, &target);
4608 if (r < 0)
0301abf4 4609 return r;
87f0e418 4610
0301abf4 4611 free(*filename);
2c7108c4 4612 *filename = target;
87f0e418
LP
4613 }
4614
9946996c
LP
4615 f = fdopen(fd, "re");
4616 if (!f) {
03e334a1 4617 safe_close(fd);
d4ad27a1 4618 return -errno;
87f0e418
LP
4619 }
4620
4621 *_f = f;
9e2f7c11 4622 *_final = id;
a837f088 4623
0301abf4 4624 return 0;
87f0e418
LP
4625}
4626
23a177ef
LP
4627static int merge_by_names(Unit **u, Set *names, const char *id) {
4628 char *k;
4629 int r;
4630
4631 assert(u);
4632 assert(*u);
4633 assert(names);
4634
4635 /* Let's try to add in all symlink names we found */
4636 while ((k = set_steal_first(names))) {
4637
4638 /* First try to merge in the other name into our
4639 * unit */
9946996c
LP
4640 r = unit_merge_by_name(*u, k);
4641 if (r < 0) {
23a177ef
LP
4642 Unit *other;
4643
4644 /* Hmm, we couldn't merge the other unit into
4645 * ours? Then let's try it the other way
4646 * round */
4647
7aad67e7
MS
4648 /* If the symlink name we are looking at is unit template, then
4649 we must search for instance of this template */
9d3e3406 4650 if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) {
7aad67e7
MS
4651 _cleanup_free_ char *instance = NULL;
4652
4653 r = unit_name_replace_instance(k, (*u)->instance, &instance);
4654 if (r < 0)
4655 return r;
4656
4657 other = manager_get_unit((*u)->manager, instance);
4658 } else
4659 other = manager_get_unit((*u)->manager, k);
4660
23a177ef
LP
4661 free(k);
4662
9946996c
LP
4663 if (other) {
4664 r = unit_merge(other, *u);
4665 if (r >= 0) {
23a177ef
LP
4666 *u = other;
4667 return merge_by_names(u, names, NULL);
4668 }
9946996c 4669 }
23a177ef
LP
4670
4671 return r;
4672 }
4673
4674 if (id == k)
4675 unit_choose_id(*u, id);
4676
4677 free(k);
4678 }
4679
4680 return 0;
4681}
4682
e537352b 4683static int load_from_path(Unit *u, const char *path) {
e48614c4
ZJS
4684 _cleanup_set_free_free_ Set *symlink_names = NULL;
4685 _cleanup_fclose_ FILE *f = NULL;
4686 _cleanup_free_ char *filename = NULL;
4687 char *id = NULL;
23a177ef 4688 Unit *merged;
45fb0699 4689 struct stat st;
a837f088 4690 int r;
23a177ef
LP
4691
4692 assert(u);
e537352b 4693 assert(path);
3efd4195 4694
d5099efc 4695 symlink_names = set_new(&string_hash_ops);
f975e971 4696 if (!symlink_names)
87f0e418 4697 return -ENOMEM;
3efd4195 4698
036643a2
LP
4699 if (path_is_absolute(path)) {
4700
9946996c 4701 filename = strdup(path);
e48614c4
ZJS
4702 if (!filename)
4703 return -ENOMEM;
036643a2 4704
9946996c
LP
4705 r = open_follow(&filename, &f, symlink_names, &id);
4706 if (r < 0) {
97b11eed 4707 filename = mfree(filename);
036643a2 4708 if (r != -ENOENT)
e48614c4 4709 return r;
036643a2
LP
4710 }
4711
4712 } else {
4713 char **p;
4714
a3c4eb07 4715 STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
036643a2
LP
4716
4717 /* Instead of opening the path right away, we manually
4718 * follow all symlinks and add their name to our unit
4719 * name set while doing so */
9946996c 4720 filename = path_make_absolute(path, *p);
e48614c4
ZJS
4721 if (!filename)
4722 return -ENOMEM;
036643a2 4723
ac155bb8
MS
4724 if (u->manager->unit_path_cache &&
4725 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
4726 r = -ENOENT;
4727 else
4728 r = open_follow(&filename, &f, symlink_names, &id);
a837f088
LP
4729 if (r >= 0)
4730 break;
4731 filename = mfree(filename);
a1feacf7
ZJS
4732
4733 /* ENOENT means that the file is missing or is a dangling symlink.
4734 * ENOTDIR means that one of paths we expect to be is a directory
4735 * is not a directory, we should just ignore that.
4736 * EACCES means that the directory or file permissions are wrong.
4737 */
4738 if (r == -EACCES)
4739 log_debug_errno(r, "Cannot access \"%s\": %m", filename);
4740 else if (!IN_SET(r, -ENOENT, -ENOTDIR))
a837f088 4741 return r;
fe51822e 4742
a837f088
LP
4743 /* Empty the symlink names for the next run */
4744 set_clear_free(symlink_names);
036643a2
LP
4745 }
4746 }
034c6ed7 4747
e48614c4 4748 if (!filename)
8f05424d 4749 /* Hmm, no suitable file found? */
e48614c4 4750 return 0;
87f0e418 4751
8a993b61 4752 if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
a837f088
LP
4753 log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
4754 return -ELOOP;
4755 }
4756
23a177ef 4757 merged = u;
9946996c
LP
4758 r = merge_by_names(&merged, symlink_names, id);
4759 if (r < 0)
e48614c4 4760 return r;
87f0e418 4761
23a177ef 4762 if (merged != u) {
ac155bb8 4763 u->load_state = UNIT_MERGED;
e48614c4 4764 return 0;
034c6ed7
LP
4765 }
4766
e48614c4
ZJS
4767 if (fstat(fileno(f), &st) < 0)
4768 return -errno;
45fb0699 4769
3a8db9fe 4770 if (null_or_empty(&st)) {
ac155bb8 4771 u->load_state = UNIT_MASKED;
3a8db9fe
ZJS
4772 u->fragment_mtime = 0;
4773 } else {
c2756a68 4774 u->load_state = UNIT_LOADED;
3a8db9fe 4775 u->fragment_mtime = timespec_load(&st.st_mtim);
c2756a68 4776
00dc5d76 4777 /* Now, parse the file contents */
36f822c4
ZJS
4778 r = config_parse(u->id, filename, f,
4779 UNIT_VTABLE(u)->sections,
4780 config_item_perf_lookup, load_fragment_gperf_lookup,
bcde742e 4781 CONFIG_PARSE_ALLOW_INCLUDE, u);
f975e971 4782 if (r < 0)
e48614c4 4783 return r;
00dc5d76 4784 }
b08d03ff 4785
ac155bb8
MS
4786 free(u->fragment_path);
4787 u->fragment_path = filename;
0301abf4 4788 filename = NULL;
87f0e418 4789
1b64d026
LP
4790 if (u->source_path) {
4791 if (stat(u->source_path, &st) >= 0)
4792 u->source_mtime = timespec_load(&st.st_mtim);
4793 else
4794 u->source_mtime = 0;
4795 }
4796
e48614c4 4797 return 0;
0301abf4
LP
4798}
4799
e537352b 4800int unit_load_fragment(Unit *u) {
23a177ef 4801 int r;
294d81f1
LP
4802 Iterator i;
4803 const char *t;
0301abf4
LP
4804
4805 assert(u);
ac155bb8
MS
4806 assert(u->load_state == UNIT_STUB);
4807 assert(u->id);
23a177ef 4808
3f5e8115
LP
4809 if (u->transient) {
4810 u->load_state = UNIT_LOADED;
4811 return 0;
4812 }
4813
294d81f1
LP
4814 /* First, try to find the unit under its id. We always look
4815 * for unit files in the default directories, to make it easy
4816 * to override things by placing things in /etc/systemd/system */
9946996c
LP
4817 r = load_from_path(u, u->id);
4818 if (r < 0)
294d81f1
LP
4819 return r;
4820
4821 /* Try to find an alias we can load this with */
abc08d4d 4822 if (u->load_state == UNIT_STUB) {
ac155bb8 4823 SET_FOREACH(t, u->names, i) {
294d81f1 4824
ac155bb8 4825 if (t == u->id)
294d81f1
LP
4826 continue;
4827
9946996c
LP
4828 r = load_from_path(u, t);
4829 if (r < 0)
294d81f1
LP
4830 return r;
4831
ac155bb8 4832 if (u->load_state != UNIT_STUB)
294d81f1
LP
4833 break;
4834 }
abc08d4d 4835 }
23a177ef 4836
294d81f1 4837 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 4838 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 4839
9946996c
LP
4840 r = load_from_path(u, u->fragment_path);
4841 if (r < 0)
23a177ef 4842 return r;
0301abf4 4843
ece174c5 4844 if (u->load_state == UNIT_STUB)
6ccb1b44
LP
4845 /* Hmm, this didn't work? Then let's get rid
4846 * of the fragment path stored for us, so that
4847 * we don't point to an invalid location. */
a1e58e8e 4848 u->fragment_path = mfree(u->fragment_path);
6ccb1b44
LP
4849 }
4850
294d81f1 4851 /* Look for a template */
ac155bb8 4852 if (u->load_state == UNIT_STUB && u->instance) {
7410616c 4853 _cleanup_free_ char *k = NULL;
294d81f1 4854
7410616c
LP
4855 r = unit_name_template(u->id, &k);
4856 if (r < 0)
4857 return r;
294d81f1
LP
4858
4859 r = load_from_path(u, k);
bb28e684
ZJS
4860 if (r < 0) {
4861 if (r == -ENOEXEC)
4862 log_unit_notice(u, "Unit configuration has fatal error, unit will not be started.");
9e2f7c11 4863 return r;
bb28e684 4864 }
890f434c 4865
abc08d4d 4866 if (u->load_state == UNIT_STUB) {
ac155bb8 4867 SET_FOREACH(t, u->names, i) {
bc9fd78c 4868 _cleanup_free_ char *z = NULL;
87f0e418 4869
ac155bb8 4870 if (t == u->id)
23a177ef 4871 continue;
071830ff 4872
7410616c
LP
4873 r = unit_name_template(t, &z);
4874 if (r < 0)
4875 return r;
294d81f1 4876
bc9fd78c 4877 r = load_from_path(u, z);
294d81f1 4878 if (r < 0)
23a177ef 4879 return r;
890f434c 4880
ac155bb8 4881 if (u->load_state != UNIT_STUB)
23a177ef
LP
4882 break;
4883 }
abc08d4d 4884 }
071830ff
LP
4885 }
4886
23a177ef 4887 return 0;
3efd4195 4888}
e537352b
LP
4889
4890void unit_dump_config_items(FILE *f) {
f975e971
LP
4891 static const struct {
4892 const ConfigParserCallback callback;
4893 const char *rvalue;
4894 } table[] = {
f9fa32f0 4895#if !HAVE_SYSV_COMPAT || !HAVE_SECCOMP || !HAVE_PAM || !HAVE_SELINUX || !ENABLE_SMACK || !HAVE_APPARMOR
17df7223
LP
4896 { config_parse_warn_compat, "NOTSUPPORTED" },
4897#endif
f975e971
LP
4898 { config_parse_int, "INTEGER" },
4899 { config_parse_unsigned, "UNSIGNED" },
5556b5fe 4900 { config_parse_iec_size, "SIZE" },
59f448cf 4901 { config_parse_iec_uint64, "SIZE" },
5556b5fe 4902 { config_parse_si_size, "SIZE" },
f975e971
LP
4903 { config_parse_bool, "BOOLEAN" },
4904 { config_parse_string, "STRING" },
4905 { config_parse_path, "PATH" },
4906 { config_parse_unit_path_printf, "PATH" },
4907 { config_parse_strv, "STRING [...]" },
4908 { config_parse_exec_nice, "NICE" },
4909 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
4910 { config_parse_exec_io_class, "IOCLASS" },
4911 { config_parse_exec_io_priority, "IOPRIORITY" },
4912 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
4913 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
4914 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
4915 { config_parse_mode, "MODE" },
4916 { config_parse_unit_env_file, "FILE" },
52c239d7
LB
4917 { config_parse_exec_output, "OUTPUT" },
4918 { config_parse_exec_input, "INPUT" },
ca37242e
LP
4919 { config_parse_log_facility, "FACILITY" },
4920 { config_parse_log_level, "LEVEL" },
f975e971 4921 { config_parse_exec_secure_bits, "SECUREBITS" },
a103496c 4922 { config_parse_capability_set, "BOUNDINGSET" },
f975e971 4923 { config_parse_limit, "LIMIT" },
f975e971 4924 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
4925 { config_parse_exec, "PATH [ARGUMENT [...]]" },
4926 { config_parse_service_type, "SERVICETYPE" },
4927 { config_parse_service_restart, "SERVICERESTART" },
349cc4a5 4928#if HAVE_SYSV_COMPAT
f975e971 4929 { config_parse_sysv_priority, "SYSVPRIORITY" },
f975e971
LP
4930#endif
4931 { config_parse_kill_mode, "KILLMODE" },
f757855e 4932 { config_parse_signal, "SIGNAL" },
f975e971
LP
4933 { config_parse_socket_listen, "SOCKET [...]" },
4934 { config_parse_socket_bind, "SOCKETBIND" },
4935 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 4936 { config_parse_sec, "SECONDS" },
d88a251b 4937 { config_parse_nsec, "NANOSECONDS" },
94828d2d 4938 { config_parse_namespace_path_strv, "PATH [...]" },
d2d6c096 4939 { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
7c8fa05c 4940 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
4941 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
4942 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 4943 { config_parse_trigger_unit, "UNIT" },
f975e971 4944 { config_parse_timer, "TIMER" },
f975e971 4945 { config_parse_path_spec, "PATH" },
f975e971
LP
4946 { config_parse_notify_access, "ACCESS" },
4947 { config_parse_ip_tos, "TOS" },
4948 { config_parse_unit_condition_path, "CONDITION" },
4949 { config_parse_unit_condition_string, "CONDITION" },
4950 { config_parse_unit_condition_null, "CONDITION" },
a016b922 4951 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
4952 { config_parse_documentation, "URL" },
4953 { config_parse_service_timeout, "SECONDS" },
87a47f99 4954 { config_parse_emergency_action, "ACTION" },
7f0386f6
LP
4955 { config_parse_set_status, "STATUS" },
4956 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 4957 { config_parse_environ, "ENVIRON" },
349cc4a5 4958#if HAVE_SECCOMP
17df7223 4959 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 4960 { config_parse_syscall_archs, "ARCHS" },
17df7223 4961 { config_parse_syscall_errno, "ERRNO" },
4298d0b5 4962 { config_parse_address_families, "FAMILIES" },
add00535 4963 { config_parse_restrict_namespaces, "NAMESPACES" },
c0467cf3 4964#endif
7f0386f6 4965 { config_parse_cpu_shares, "SHARES" },
66ebf6c0 4966 { config_parse_cpu_weight, "WEIGHT" },
7f0386f6
LP
4967 { config_parse_memory_limit, "LIMIT" },
4968 { config_parse_device_allow, "DEVICE" },
4969 { config_parse_device_policy, "POLICY" },
13c31542
TH
4970 { config_parse_io_limit, "LIMIT" },
4971 { config_parse_io_weight, "WEIGHT" },
4972 { config_parse_io_device_weight, "DEVICEWEIGHT" },
7f0386f6
LP
4973 { config_parse_blockio_bandwidth, "BANDWIDTH" },
4974 { config_parse_blockio_weight, "WEIGHT" },
4975 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
4976 { config_parse_long, "LONG" },
4977 { config_parse_socket_service, "SERVICE" },
349cc4a5 4978#if HAVE_SELINUX
6a6751fe
LP
4979 { config_parse_exec_selinux_context, "LABEL" },
4980#endif
4981 { config_parse_job_mode, "MODE" },
4982 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 4983 { config_parse_personality, "PERSONALITY" },
f975e971
LP
4984 };
4985
4986 const char *prev = NULL;
4987 const char *i;
4988
4989 assert(f);
e537352b 4990
f975e971
LP
4991 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
4992 const char *rvalue = "OTHER", *lvalue;
4993 unsigned j;
4994 size_t prefix_len;
4995 const char *dot;
4996 const ConfigPerfItem *p;
4997
4998 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
4999
5000 dot = strchr(i, '.');
5001 lvalue = dot ? dot + 1 : i;
5002 prefix_len = dot-i;
5003
5004 if (dot)
641906e9 5005 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
5006 if (prev)
5007 fputc('\n', f);
5008
5009 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
5010 }
5011
5012 for (j = 0; j < ELEMENTSOF(table); j++)
5013 if (p->parse == table[j].callback) {
5014 rvalue = table[j].rvalue;
5015 break;
5016 }
5017
5018 fprintf(f, "%s=%s\n", lvalue, rvalue);
5019 prev = i;
5020 }
e537352b 5021}