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