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