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