]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
TODO: trim obsolete entries
[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 */
13d92c63 2937 r = seccomp_parse_syscall_filter("@default", -1, c->syscall_filter, SECCOMP_PARSE_WHITELIST);
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
13d92c63
LP
2964 r = seccomp_parse_syscall_filter_full(name, num, c->syscall_filter,
2965 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|(invert ? SECCOMP_PARSE_INVERT : 0)|(c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0),
2966 unit, filename, line);
201c1cc2
TM
2967 if (r < 0)
2968 return r;
c0467cf3
RC
2969 }
2970
17df7223
LP
2971 return 0;
2972}
2973
57183d11
LP
2974int config_parse_syscall_archs(
2975 const char *unit,
2976 const char *filename,
2977 unsigned line,
2978 const char *section,
2979 unsigned section_line,
2980 const char *lvalue,
2981 int ltype,
2982 const char *rvalue,
2983 void *data,
2984 void *userdata) {
2985
d3b1c508 2986 Set **archs = data;
035fe294 2987 const char *p;
57183d11
LP
2988 int r;
2989
2990 if (isempty(rvalue)) {
525d3cc7 2991 *archs = set_free(*archs);
57183d11
LP
2992 return 0;
2993 }
2994
d5099efc 2995 r = set_ensure_allocated(archs, NULL);
57183d11
LP
2996 if (r < 0)
2997 return log_oom();
2998
035fe294
ZJS
2999 for (p = rvalue;;) {
3000 _cleanup_free_ char *word = NULL;
57183d11
LP
3001 uint32_t a;
3002
035fe294
ZJS
3003 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
3004 if (r == 0)
3005 return 0;
3006 if (r == -ENOMEM)
57183d11 3007 return log_oom();
035fe294
ZJS
3008 if (r < 0) {
3009 log_syntax(unit, LOG_WARNING, filename, line, r,
3010 "Invalid syntax, ignoring: %s", rvalue);
3011 return 0;
3012 }
57183d11 3013
035fe294 3014 r = seccomp_arch_from_string(word, &a);
57183d11 3015 if (r < 0) {
035fe294
ZJS
3016 log_syntax(unit, LOG_ERR, filename, line, r,
3017 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
57183d11
LP
3018 continue;
3019 }
3020
d3b1c508 3021 r = set_put(*archs, UINT32_TO_PTR(a + 1));
57183d11
LP
3022 if (r < 0)
3023 return log_oom();
3024 }
57183d11
LP
3025}
3026
17df7223
LP
3027int config_parse_syscall_errno(
3028 const char *unit,
3029 const char *filename,
3030 unsigned line,
3031 const char *section,
3032 unsigned section_line,
3033 const char *lvalue,
3034 int ltype,
3035 const char *rvalue,
3036 void *data,
3037 void *userdata) {
3038
3039 ExecContext *c = data;
3040 int e;
3041
3042 assert(filename);
3043 assert(lvalue);
3044 assert(rvalue);
3045
3046 if (isempty(rvalue)) {
3047 /* Empty assignment resets to KILL */
3048 c->syscall_errno = 0;
3049 return 0;
8351ceae
LP
3050 }
3051
3df90f24
YW
3052 e = parse_errno(rvalue);
3053 if (e <= 0) {
12ca818f 3054 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
17df7223
LP
3055 return 0;
3056 }
8351ceae 3057
17df7223 3058 c->syscall_errno = e;
8351ceae
LP
3059 return 0;
3060}
4298d0b5
LP
3061
3062int config_parse_address_families(
3063 const char *unit,
3064 const char *filename,
3065 unsigned line,
3066 const char *section,
3067 unsigned section_line,
3068 const char *lvalue,
3069 int ltype,
3070 const char *rvalue,
3071 void *data,
3072 void *userdata) {
3073
3074 ExecContext *c = data;
4298d0b5 3075 bool invert = false;
035fe294 3076 const char *p;
4298d0b5
LP
3077 int r;
3078
3079 assert(filename);
3080 assert(lvalue);
3081 assert(rvalue);
4298d0b5
LP
3082
3083 if (isempty(rvalue)) {
3084 /* Empty assignment resets the list */
525d3cc7 3085 c->address_families = set_free(c->address_families);
4298d0b5
LP
3086 c->address_families_whitelist = false;
3087 return 0;
3088 }
3089
3090 if (rvalue[0] == '~') {
3091 invert = true;
3092 rvalue++;
3093 }
3094
3095 if (!c->address_families) {
d5099efc 3096 c->address_families = set_new(NULL);
4298d0b5
LP
3097 if (!c->address_families)
3098 return log_oom();
3099
3100 c->address_families_whitelist = !invert;
3101 }
3102
035fe294
ZJS
3103 for (p = rvalue;;) {
3104 _cleanup_free_ char *word = NULL;
4298d0b5
LP
3105 int af;
3106
035fe294
ZJS
3107 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
3108 if (r == 0)
3109 return 0;
3110 if (r == -ENOMEM)
4298d0b5 3111 return log_oom();
035fe294
ZJS
3112 if (r < 0) {
3113 log_syntax(unit, LOG_WARNING, filename, line, r,
3114 "Invalid syntax, ignoring: %s", rvalue);
3115 return 0;
3116 }
4298d0b5 3117
035fe294 3118 af = af_from_name(word);
4298d0b5 3119 if (af <= 0) {
035fe294
ZJS
3120 log_syntax(unit, LOG_ERR, filename, line, 0,
3121 "Failed to parse address family \"%s\", ignoring: %m", word);
4298d0b5
LP
3122 continue;
3123 }
3124
3125 /* If we previously wanted to forbid an address family and now
035fe294 3126 * we want to allow it, then just remove it from the list.
4298d0b5
LP
3127 */
3128 if (!invert == c->address_families_whitelist) {
3129 r = set_put(c->address_families, INT_TO_PTR(af));
4298d0b5
LP
3130 if (r < 0)
3131 return log_oom();
3132 } else
3133 set_remove(c->address_families, INT_TO_PTR(af));
3134 }
4298d0b5 3135}
add00535
LP
3136
3137int config_parse_restrict_namespaces(
3138 const char *unit,
3139 const char *filename,
3140 unsigned line,
3141 const char *section,
3142 unsigned section_line,
3143 const char *lvalue,
3144 int ltype,
3145 const char *rvalue,
3146 void *data,
3147 void *userdata) {
3148
3149 ExecContext *c = data;
3150 bool invert = false;
3151 int r;
3152
3153 if (isempty(rvalue)) {
3154 /* Reset to the default. */
3155 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
3156 return 0;
3157 }
3158
3159 if (rvalue[0] == '~') {
3160 invert = true;
3161 rvalue++;
3162 }
3163
3164 r = parse_boolean(rvalue);
3165 if (r > 0)
3166 c->restrict_namespaces = 0;
3167 else if (r == 0)
3168 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
3169 else {
3170 /* Not a boolean argument, in this case it's a list of namespace types. */
3171
3172 r = namespace_flag_from_string_many(rvalue, &c->restrict_namespaces);
3173 if (r < 0) {
3174 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
3175 return 0;
3176 }
3177 }
3178
3179 if (invert)
3180 c->restrict_namespaces = (~c->restrict_namespaces) & NAMESPACE_FLAGS_ALL;
3181
3182 return 0;
3183}
c0467cf3 3184#endif
8351ceae 3185
a016b922
LP
3186int config_parse_unit_slice(
3187 const char *unit,
3188 const char *filename,
3189 unsigned line,
3190 const char *section,
71a61510 3191 unsigned section_line,
a016b922
LP
3192 const char *lvalue,
3193 int ltype,
3194 const char *rvalue,
3195 void *data,
3196 void *userdata) {
3197
3198 _cleanup_free_ char *k = NULL;
d79200e2 3199 Unit *u = userdata, *slice = NULL;
a016b922
LP
3200 int r;
3201
3202 assert(filename);
3203 assert(lvalue);
3204 assert(rvalue);
3205 assert(u);
3206
19f6d710 3207 r = unit_name_printf(u, rvalue, &k);
d79200e2
LP
3208 if (r < 0) {
3209 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
3210 return 0;
19f6d710 3211 }
a016b922 3212
19f6d710 3213 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
a016b922 3214 if (r < 0) {
d79200e2 3215 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k);
a016b922
LP
3216 return 0;
3217 }
3218
d79200e2
LP
3219 r = unit_set_slice(u, slice);
3220 if (r < 0) {
3221 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id);
a016b922
LP
3222 return 0;
3223 }
3224
a016b922
LP
3225 return 0;
3226}
3227
4ad49000
LP
3228DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
3229
66ebf6c0
TH
3230int config_parse_cpu_weight(
3231 const char *unit,
3232 const char *filename,
3233 unsigned line,
3234 const char *section,
3235 unsigned section_line,
3236 const char *lvalue,
3237 int ltype,
3238 const char *rvalue,
3239 void *data,
3240 void *userdata) {
3241
3242 uint64_t *weight = data;
3243 int r;
3244
3245 assert(filename);
3246 assert(lvalue);
3247 assert(rvalue);
3248
3249 r = cg_weight_parse(rvalue, weight);
3250 if (r < 0) {
3251 log_syntax(unit, LOG_ERR, filename, line, r, "CPU weight '%s' invalid. Ignoring.", rvalue);
3252 return 0;
3253 }
3254
3255 return 0;
3256}
3257
4ad49000
LP
3258int config_parse_cpu_shares(
3259 const char *unit,
3260 const char *filename,
3261 unsigned line,
3262 const char *section,
71a61510 3263 unsigned section_line,
4ad49000
LP
3264 const char *lvalue,
3265 int ltype,
3266 const char *rvalue,
3267 void *data,
3268 void *userdata) {
3269
d53d9474 3270 uint64_t *shares = data;
95ae05c0
WC
3271 int r;
3272
3273 assert(filename);
3274 assert(lvalue);
3275 assert(rvalue);
3276
d53d9474
LP
3277 r = cg_cpu_shares_parse(rvalue, shares);
3278 if (r < 0) {
3279 log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
3280 return 0;
3281 }
3282
4ad49000
LP
3283 return 0;
3284}
3285
b2f8b02e
LP
3286int config_parse_cpu_quota(
3287 const char *unit,
3288 const char *filename,
3289 unsigned line,
3290 const char *section,
3291 unsigned section_line,
3292 const char *lvalue,
3293 int ltype,
3294 const char *rvalue,
3295 void *data,
3296 void *userdata) {
3297
3298 CGroupContext *c = data;
9184ca48 3299 int r;
b2f8b02e
LP
3300
3301 assert(filename);
3302 assert(lvalue);
3303 assert(rvalue);
3304
3305 if (isempty(rvalue)) {
3a43da28 3306 c->cpu_quota_per_sec_usec = USEC_INFINITY;
b2f8b02e
LP
3307 return 0;
3308 }
3309
5124866d 3310 r = parse_percent_unbounded(rvalue);
9184ca48
LP
3311 if (r <= 0) {
3312 log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
9a054909 3313 return 0;
b2f8b02e
LP
3314 }
3315
9184ca48 3316 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
b2f8b02e
LP
3317 return 0;
3318}
3319
4ad49000
LP
3320int config_parse_memory_limit(
3321 const char *unit,
3322 const char *filename,
3323 unsigned line,
3324 const char *section,
71a61510 3325 unsigned section_line,
4ad49000
LP
3326 const char *lvalue,
3327 int ltype,
3328 const char *rvalue,
3329 void *data,
3330 void *userdata) {
3331
3332 CGroupContext *c = data;
da4d897e 3333 uint64_t bytes = CGROUP_LIMIT_MAX;
4ad49000
LP
3334 int r;
3335
e57c9ce1 3336 if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
875ae566
LP
3337
3338 r = parse_percent(rvalue);
3339 if (r < 0) {
3340 r = parse_size(rvalue, 1024, &bytes);
3341 if (r < 0) {
3342 log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
3343 return 0;
3344 }
3345 } else
d8cf2ac7 3346 bytes = physical_memory_scale(r, 100U);
875ae566 3347
906bdbf5
YW
3348 if (bytes >= UINT64_MAX ||
3349 (bytes <= 0 && !streq(lvalue, "MemorySwapMax"))) {
b3785cd5 3350 log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range. Ignoring.", rvalue);
da4d897e
TH
3351 return 0;
3352 }
4ad49000
LP
3353 }
3354
da4d897e
TH
3355 if (streq(lvalue, "MemoryLow"))
3356 c->memory_low = bytes;
3357 else if (streq(lvalue, "MemoryHigh"))
3358 c->memory_high = bytes;
3359 else if (streq(lvalue, "MemoryMax"))
3360 c->memory_max = bytes;
96e131ea
WC
3361 else if (streq(lvalue, "MemorySwapMax"))
3362 c->memory_swap_max = bytes;
3363 else if (streq(lvalue, "MemoryLimit"))
da4d897e 3364 c->memory_limit = bytes;
96e131ea
WC
3365 else
3366 return -EINVAL;
4ad49000 3367
4ad49000
LP
3368 return 0;
3369}
3370
03a7b521
LP
3371int config_parse_tasks_max(
3372 const char *unit,
3373 const char *filename,
3374 unsigned line,
3375 const char *section,
3376 unsigned section_line,
3377 const char *lvalue,
3378 int ltype,
3379 const char *rvalue,
3380 void *data,
3381 void *userdata) {
3382
f5058264
TH
3383 uint64_t *tasks_max = data, v;
3384 Unit *u = userdata;
03a7b521
LP
3385 int r;
3386
f5058264
TH
3387 if (isempty(rvalue)) {
3388 *tasks_max = u->manager->default_tasks_max;
3389 return 0;
3390 }
3391
3392 if (streq(rvalue, "infinity")) {
3393 *tasks_max = CGROUP_LIMIT_MAX;
03a7b521
LP
3394 return 0;
3395 }
3396
83f8e808
LP
3397 r = parse_percent(rvalue);
3398 if (r < 0) {
f5058264 3399 r = safe_atou64(rvalue, &v);
83f8e808
LP
3400 if (r < 0) {
3401 log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
3402 return 0;
3403 }
3404 } else
f5058264 3405 v = system_tasks_max_scale(r, 100U);
83f8e808 3406
f5058264 3407 if (v <= 0 || v >= UINT64_MAX) {
83f8e808 3408 log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue);
03a7b521
LP
3409 return 0;
3410 }
3411
f5058264 3412 *tasks_max = v;
03a7b521
LP
3413 return 0;
3414}
3415
02638280
LP
3416int config_parse_delegate(
3417 const char *unit,
3418 const char *filename,
3419 unsigned line,
3420 const char *section,
3421 unsigned section_line,
3422 const char *lvalue,
3423 int ltype,
3424 const char *rvalue,
3425 void *data,
3426 void *userdata) {
3427
3428 CGroupContext *c = data;
3429 int r;
3430
3431 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3432 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3433 * mask to delegate. */
3434
1bdfc7b9
YW
3435 if (isempty(rvalue)) {
3436 c->delegate = true;
3437 c->delegate_controllers = 0;
3438 return 0;
3439 }
3440
02638280
LP
3441 r = parse_boolean(rvalue);
3442 if (r < 0) {
3443 const char *p = rvalue;
3444 CGroupMask mask = 0;
3445
3446 for (;;) {
3447 _cleanup_free_ char *word = NULL;
3448 CGroupController cc;
3449
3450 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
3451 if (r == 0)
3452 break;
3453 if (r == -ENOMEM)
3454 return log_oom();
3455 if (r < 0) {
3456 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
3457 return r;
3458 }
3459
3460 cc = cgroup_controller_from_string(word);
3461 if (cc < 0) {
3462 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid controller name '%s', ignoring", rvalue);
3463 continue;
3464 }
3465
3466 mask |= CGROUP_CONTROLLER_TO_MASK(cc);
3467 }
3468
3469 c->delegate = true;
3470 c->delegate_controllers |= mask;
3471
3472 } else if (r > 0) {
3473 c->delegate = true;
3474 c->delegate_controllers = _CGROUP_MASK_ALL;
3475 } else {
3476 c->delegate = false;
3477 c->delegate_controllers = 0;
3478 }
3479
3480 return 0;
3481}
3482
4ad49000
LP
3483int config_parse_device_allow(
3484 const char *unit,
3485 const char *filename,
3486 unsigned line,
3487 const char *section,
71a61510 3488 unsigned section_line,
4ad49000
LP
3489 const char *lvalue,
3490 int ltype,
3491 const char *rvalue,
3492 void *data,
3493 void *userdata) {
3494
1116e14c 3495 _cleanup_free_ char *path = NULL, *t = NULL;
4ad49000
LP
3496 CGroupContext *c = data;
3497 CGroupDeviceAllow *a;
1116e14c 3498 const char *m = NULL;
4ad49000 3499 size_t n;
1116e14c 3500 int r;
4ad49000
LP
3501
3502 if (isempty(rvalue)) {
3503 while (c->device_allow)
3504 cgroup_context_free_device_allow(c, c->device_allow);
3505
3506 return 0;
3507 }
3508
1116e14c 3509 r = unit_full_printf(userdata, rvalue, &t);
508f63b4 3510 if (r < 0) {
1116e14c
NBS
3511 log_syntax(unit, LOG_WARNING, filename, line, r,
3512 "Failed to resolve specifiers in %s, ignoring: %m",
3513 rvalue);
3514 }
3515
3516 n = strcspn(t, WHITESPACE);
3517
3518 path = strndup(t, n);
4ad49000
LP
3519 if (!path)
3520 return log_oom();
3521
32048f54
YW
3522 if (!is_deviceallow_pattern(path) &&
3523 !path_startswith(path, "/run/systemd/inaccessible/")) {
12ca818f 3524 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
3525 return 0;
3526 }
3527
1116e14c 3528 m = t + n + strspn(t + n, WHITESPACE);
4ad49000
LP
3529 if (isempty(m))
3530 m = "rwm";
3531
3532 if (!in_charset(m, "rwm")) {
12ca818f 3533 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
4ad49000
LP
3534 return 0;
3535 }
3536
3537 a = new0(CGroupDeviceAllow, 1);
3538 if (!a)
3539 return log_oom();
3540
3541 a->path = path;
3542 path = NULL;
3543 a->r = !!strchr(m, 'r');
3544 a->w = !!strchr(m, 'w');
3545 a->m = !!strchr(m, 'm');
3546
71fda00f 3547 LIST_PREPEND(device_allow, c->device_allow, a);
4ad49000
LP
3548 return 0;
3549}
3550
13c31542
TH
3551int config_parse_io_weight(
3552 const char *unit,
3553 const char *filename,
3554 unsigned line,
3555 const char *section,
3556 unsigned section_line,
3557 const char *lvalue,
3558 int ltype,
3559 const char *rvalue,
3560 void *data,
3561 void *userdata) {
3562
3563 uint64_t *weight = data;
3564 int r;
3565
3566 assert(filename);
3567 assert(lvalue);
3568 assert(rvalue);
3569
3570 r = cg_weight_parse(rvalue, weight);
3571 if (r < 0) {
3572 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue);
3573 return 0;
3574 }
3575
3576 return 0;
3577}
3578
3579int config_parse_io_device_weight(
3580 const char *unit,
3581 const char *filename,
3582 unsigned line,
3583 const char *section,
3584 unsigned section_line,
3585 const char *lvalue,
3586 int ltype,
3587 const char *rvalue,
3588 void *data,
3589 void *userdata) {
3590
3591 _cleanup_free_ char *path = NULL;
3592 CGroupIODeviceWeight *w;
3593 CGroupContext *c = data;
3594 const char *weight;
3595 uint64_t u;
3596 size_t n;
3597 int r;
3598
3599 assert(filename);
3600 assert(lvalue);
3601 assert(rvalue);
3602
3603 if (isempty(rvalue)) {
3604 while (c->io_device_weights)
3605 cgroup_context_free_io_device_weight(c, c->io_device_weights);
3606
3607 return 0;
3608 }
3609
3610 n = strcspn(rvalue, WHITESPACE);
3611 weight = rvalue + n;
3612 weight += strspn(weight, WHITESPACE);
3613
3614 if (isempty(weight)) {
3615 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
3616 return 0;
3617 }
3618
3619 path = strndup(rvalue, n);
3620 if (!path)
3621 return log_oom();
3622
32048f54
YW
3623 if (!path_startswith(path, "/dev") &&
3624 !path_startswith(path, "/run/systemd/inaccessible/")) {
13c31542
TH
3625 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3626 return 0;
3627 }
3628
3629 r = cg_weight_parse(weight, &u);
3630 if (r < 0) {
3631 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight);
3632 return 0;
3633 }
3634
3635 assert(u != CGROUP_WEIGHT_INVALID);
3636
3637 w = new0(CGroupIODeviceWeight, 1);
3638 if (!w)
3639 return log_oom();
3640
3641 w->path = path;
3642 path = NULL;
3643
3644 w->weight = u;
3645
3646 LIST_PREPEND(device_weights, c->io_device_weights, w);
3647 return 0;
3648}
3649
3650int config_parse_io_limit(
3651 const char *unit,
3652 const char *filename,
3653 unsigned line,
3654 const char *section,
3655 unsigned section_line,
3656 const char *lvalue,
3657 int ltype,
3658 const char *rvalue,
3659 void *data,
3660 void *userdata) {
3661
3662 _cleanup_free_ char *path = NULL;
3663 CGroupIODeviceLimit *l = NULL, *t;
3664 CGroupContext *c = data;
9be57249 3665 CGroupIOLimitType type;
13c31542
TH
3666 const char *limit;
3667 uint64_t num;
13c31542
TH
3668 size_t n;
3669 int r;
3670
3671 assert(filename);
3672 assert(lvalue);
3673 assert(rvalue);
3674
9be57249
TH
3675 type = cgroup_io_limit_type_from_string(lvalue);
3676 assert(type >= 0);
13c31542
TH
3677
3678 if (isempty(rvalue)) {
3679 LIST_FOREACH(device_limits, l, c->io_device_limits)
9be57249 3680 l->limits[type] = cgroup_io_limit_defaults[type];
13c31542
TH
3681 return 0;
3682 }
3683
3684 n = strcspn(rvalue, WHITESPACE);
3685 limit = rvalue + n;
3686 limit += strspn(limit, WHITESPACE);
3687
3688 if (!*limit) {
3689 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3690 return 0;
3691 }
3692
3693 path = strndup(rvalue, n);
3694 if (!path)
3695 return log_oom();
3696
32048f54
YW
3697 if (!path_startswith(path, "/dev") &&
3698 !path_startswith(path, "/run/systemd/inaccessible/")) {
13c31542
TH
3699 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3700 return 0;
3701 }
3702
e57c9ce1 3703 if (streq("infinity", limit)) {
13c31542
TH
3704 num = CGROUP_LIMIT_MAX;
3705 } else {
3706 r = parse_size(limit, 1000, &num);
3707 if (r < 0 || num <= 0) {
3708 log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue);
3709 return 0;
3710 }
3711 }
3712
3713 LIST_FOREACH(device_limits, t, c->io_device_limits) {
3714 if (path_equal(path, t->path)) {
3715 l = t;
3716 break;
3717 }
3718 }
3719
3720 if (!l) {
9be57249
TH
3721 CGroupIOLimitType ttype;
3722
13c31542
TH
3723 l = new0(CGroupIODeviceLimit, 1);
3724 if (!l)
3725 return log_oom();
3726
3727 l->path = path;
3728 path = NULL;
9be57249
TH
3729 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
3730 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
13c31542
TH
3731
3732 LIST_PREPEND(device_limits, c->io_device_limits, l);
3733 }
3734
9be57249 3735 l->limits[type] = num;
13c31542
TH
3736
3737 return 0;
3738}
3739
4ad49000
LP
3740int config_parse_blockio_weight(
3741 const char *unit,
3742 const char *filename,
3743 unsigned line,
3744 const char *section,
71a61510 3745 unsigned section_line,
4ad49000
LP
3746 const char *lvalue,
3747 int ltype,
3748 const char *rvalue,
3749 void *data,
3750 void *userdata) {
3751
d53d9474 3752 uint64_t *weight = data;
95ae05c0
WC
3753 int r;
3754
3755 assert(filename);
3756 assert(lvalue);
3757 assert(rvalue);
3758
d53d9474
LP
3759 r = cg_blkio_weight_parse(rvalue, weight);
3760 if (r < 0) {
3761 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
3762 return 0;
3763 }
3764
8e7076ca
LP
3765 return 0;
3766}
3767
3768int config_parse_blockio_device_weight(
3769 const char *unit,
3770 const char *filename,
3771 unsigned line,
3772 const char *section,
71a61510 3773 unsigned section_line,
8e7076ca
LP
3774 const char *lvalue,
3775 int ltype,
3776 const char *rvalue,
3777 void *data,
3778 void *userdata) {
3779
4ad49000 3780 _cleanup_free_ char *path = NULL;
8e7076ca 3781 CGroupBlockIODeviceWeight *w;
4ad49000 3782 CGroupContext *c = data;
4ad49000 3783 const char *weight;
d53d9474 3784 uint64_t u;
4ad49000
LP
3785 size_t n;
3786 int r;
3787
3788 assert(filename);
3789 assert(lvalue);
3790 assert(rvalue);
3791
3792 if (isempty(rvalue)) {
4ad49000
LP
3793 while (c->blockio_device_weights)
3794 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
3795
3796 return 0;
3797 }
3798
3799 n = strcspn(rvalue, WHITESPACE);
3800 weight = rvalue + n;
d53d9474
LP
3801 weight += strspn(weight, WHITESPACE);
3802
3803 if (isempty(weight)) {
12ca818f 3804 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
8e7076ca
LP
3805 return 0;
3806 }
4ad49000 3807
8e7076ca
LP
3808 path = strndup(rvalue, n);
3809 if (!path)
3810 return log_oom();
4ad49000 3811
32048f54
YW
3812 if (!path_startswith(path, "/dev") &&
3813 !path_startswith(path, "/run/systemd/inaccessible/")) {
12ca818f 3814 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
8e7076ca
LP
3815 return 0;
3816 }
4ad49000 3817
d53d9474
LP
3818 r = cg_blkio_weight_parse(weight, &u);
3819 if (r < 0) {
3820 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
4ad49000
LP
3821 return 0;
3822 }
3823
d53d9474
LP
3824 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
3825
8e7076ca
LP
3826 w = new0(CGroupBlockIODeviceWeight, 1);
3827 if (!w)
3828 return log_oom();
4ad49000 3829
8e7076ca
LP
3830 w->path = path;
3831 path = NULL;
4ad49000 3832
d53d9474 3833 w->weight = u;
4ad49000 3834
71fda00f 3835 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
3836 return 0;
3837}
3838
3839int config_parse_blockio_bandwidth(
3840 const char *unit,
3841 const char *filename,
3842 unsigned line,
3843 const char *section,
71a61510 3844 unsigned section_line,
4ad49000
LP
3845 const char *lvalue,
3846 int ltype,
3847 const char *rvalue,
3848 void *data,
3849 void *userdata) {
3850
3851 _cleanup_free_ char *path = NULL;
979d0311 3852 CGroupBlockIODeviceBandwidth *b = NULL, *t;
4ad49000
LP
3853 CGroupContext *c = data;
3854 const char *bandwidth;
59f448cf 3855 uint64_t bytes;
47c0980d 3856 bool read;
4ad49000
LP
3857 size_t n;
3858 int r;
3859
3860 assert(filename);
3861 assert(lvalue);
3862 assert(rvalue);
3863
47c0980d
G
3864 read = streq("BlockIOReadBandwidth", lvalue);
3865
4ad49000 3866 if (isempty(rvalue)) {
979d0311
TH
3867 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
3868 b->rbps = CGROUP_LIMIT_MAX;
3869 b->wbps = CGROUP_LIMIT_MAX;
3870 }
4ad49000
LP
3871 return 0;
3872 }
3873
3874 n = strcspn(rvalue, WHITESPACE);
3875 bandwidth = rvalue + n;
3876 bandwidth += strspn(bandwidth, WHITESPACE);
3877
3878 if (!*bandwidth) {
12ca818f 3879 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
4ad49000
LP
3880 return 0;
3881 }
3882
3883 path = strndup(rvalue, n);
3884 if (!path)
3885 return log_oom();
3886
32048f54
YW
3887 if (!path_startswith(path, "/dev") &&
3888 !path_startswith(path, "/run/systemd/inaccessible/")) {
12ca818f 3889 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
3890 return 0;
3891 }
3892
5556b5fe 3893 r = parse_size(bandwidth, 1000, &bytes);
4ad49000 3894 if (r < 0 || bytes <= 0) {
12ca818f 3895 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
3896 return 0;
3897 }
3898
979d0311
TH
3899 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
3900 if (path_equal(path, t->path)) {
3901 b = t;
3902 break;
3903 }
3904 }
4ad49000 3905
979d0311
TH
3906 if (!t) {
3907 b = new0(CGroupBlockIODeviceBandwidth, 1);
3908 if (!b)
3909 return log_oom();
3910
3911 b->path = path;
3912 path = NULL;
3913 b->rbps = CGROUP_LIMIT_MAX;
3914 b->wbps = CGROUP_LIMIT_MAX;
3915
3916 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
3917 }
4ad49000 3918
979d0311
TH
3919 if (read)
3920 b->rbps = bytes;
3921 else
3922 b->wbps = bytes;
4ad49000
LP
3923
3924 return 0;
3925}
3926
d420282b
LP
3927DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
3928
3929int config_parse_job_mode_isolate(
3930 const char *unit,
3931 const char *filename,
3932 unsigned line,
3933 const char *section,
3934 unsigned section_line,
3935 const char *lvalue,
3936 int ltype,
3937 const char *rvalue,
3938 void *data,
3939 void *userdata) {
3940
3941 JobMode *m = data;
3942 int r;
3943
3944 assert(filename);
3945 assert(lvalue);
3946 assert(rvalue);
3947
3948 r = parse_boolean(rvalue);
3949 if (r < 0) {
12ca818f 3950 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
d420282b
LP
3951 return 0;
3952 }
3953
8ab39347
YW
3954 log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue);
3955
d420282b
LP
3956 *m = r ? JOB_ISOLATE : JOB_REPLACE;
3957 return 0;
3958}
3959
53f47dfc
YW
3960DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
3961
3536f49e 3962int config_parse_exec_directories(
e66cf1a3
LP
3963 const char *unit,
3964 const char *filename,
3965 unsigned line,
3966 const char *section,
3967 unsigned section_line,
3968 const char *lvalue,
3969 int ltype,
3970 const char *rvalue,
3971 void *data,
3972 void *userdata) {
3973
a2a5291b 3974 char***rt = data;
9b5864d9 3975 Unit *u = userdata;
035fe294 3976 const char *p;
e66cf1a3
LP
3977 int r;
3978
3979 assert(filename);
3980 assert(lvalue);
3981 assert(rvalue);
3982 assert(data);
3983
3984 if (isempty(rvalue)) {
3985 /* Empty assignment resets the list */
6796073e 3986 *rt = strv_free(*rt);
e66cf1a3
LP
3987 return 0;
3988 }
3989
035fe294
ZJS
3990 for (p = rvalue;;) {
3991 _cleanup_free_ char *word = NULL, *k = NULL;
e66cf1a3 3992
035fe294 3993 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
035fe294 3994 if (r == -ENOMEM)
e66cf1a3 3995 return log_oom();
035fe294
ZJS
3996 if (r < 0) {
3997 log_syntax(unit, LOG_WARNING, filename, line, r,
3998 "Invalid syntax, ignoring: %s", rvalue);
3999 return 0;
4000 }
091e9efe
LP
4001 if (r == 0)
4002 return 0;
e66cf1a3 4003
18913df9 4004 r = unit_full_printf(u, word, &k);
9b5864d9 4005 if (r < 0) {
035fe294
ZJS
4006 log_syntax(unit, LOG_ERR, filename, line, r,
4007 "Failed to resolve specifiers in \"%s\", ignoring: %m", word);
9b5864d9
MG
4008 continue;
4009 }
4010
99be45a4 4011 if (!path_is_normalized(k) || path_is_absolute(k)) {
035fe294 4012 log_syntax(unit, LOG_ERR, filename, line, 0,
091e9efe 4013 "%s= path is not valid, ignoring assignment: %s", lvalue, rvalue);
e66cf1a3
LP
4014 continue;
4015 }
4016
035fe294 4017 r = strv_push(rt, k);
e66cf1a3
LP
4018 if (r < 0)
4019 return log_oom();
035fe294 4020 k = NULL;
e66cf1a3 4021 }
e66cf1a3
LP
4022}
4023
3af00fb8
LP
4024int config_parse_set_status(
4025 const char *unit,
4026 const char *filename,
4027 unsigned line,
4028 const char *section,
4029 unsigned section_line,
4030 const char *lvalue,
4031 int ltype,
4032 const char *rvalue,
4033 void *data,
4034 void *userdata) {
4035
3af00fb8 4036 size_t l;
a2a5291b 4037 const char *word, *state;
3af00fb8
LP
4038 int r;
4039 ExitStatusSet *status_set = data;
4040
4041 assert(filename);
4042 assert(lvalue);
4043 assert(rvalue);
4044 assert(data);
4045
3e2d435b 4046 /* Empty assignment resets the list */
3af00fb8 4047 if (isempty(rvalue)) {
3e2d435b 4048 exit_status_set_free(status_set);
3af00fb8
LP
4049 return 0;
4050 }
4051
a2a5291b 4052 FOREACH_WORD(word, l, rvalue, state) {
3af00fb8
LP
4053 _cleanup_free_ char *temp;
4054 int val;
61593865 4055 Set **set;
3af00fb8 4056
a2a5291b 4057 temp = strndup(word, l);
3af00fb8
LP
4058 if (!temp)
4059 return log_oom();
4060
4061 r = safe_atoi(temp, &val);
4062 if (r < 0) {
4063 val = signal_from_string_try_harder(temp);
4064
1e2fd62d 4065 if (val <= 0) {
12ca818f 4066 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
61593865 4067 continue;
3af00fb8 4068 }
61593865 4069 set = &status_set->signal;
3af00fb8 4070 } else {
1e2fd62d 4071 if (val < 0 || val > 255) {
12ca818f 4072 log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
1e2fd62d 4073 continue;
3af00fb8 4074 }
61593865 4075 set = &status_set->status;
3af00fb8 4076 }
1e2fd62d 4077
61593865 4078 r = set_ensure_allocated(set, NULL);
1e2fd62d
ZJS
4079 if (r < 0)
4080 return log_oom();
4081
61593865 4082 r = set_put(*set, INT_TO_PTR(val));
1e2fd62d 4083 if (r < 0) {
12ca818f 4084 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
1e2fd62d
ZJS
4085 return r;
4086 }
3af00fb8 4087 }
b2fadec6 4088 if (!isempty(state))
12ca818f 4089 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3af00fb8
LP
4090
4091 return 0;
4092}
4093
94828d2d
LP
4094int config_parse_namespace_path_strv(
4095 const char *unit,
4096 const char *filename,
4097 unsigned line,
4098 const char *section,
4099 unsigned section_line,
4100 const char *lvalue,
4101 int ltype,
4102 const char *rvalue,
4103 void *data,
4104 void *userdata) {
4105
7b07e993 4106 Unit *u = userdata;
a2a5291b 4107 char*** sv = data;
727f76d7 4108 const char *cur;
94828d2d
LP
4109 int r;
4110
4111 assert(filename);
4112 assert(lvalue);
4113 assert(rvalue);
4114 assert(data);
4115
4116 if (isempty(rvalue)) {
4117 /* Empty assignment resets the list */
6796073e 4118 *sv = strv_free(*sv);
94828d2d
LP
4119 return 0;
4120 }
4121
7b07e993 4122 cur = rvalue;
727f76d7 4123 for (;;) {
7b07e993 4124 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
20b7a007
LP
4125 const char *w;
4126 bool ignore_enoent = false, shall_prefix = false;
94828d2d 4127
727f76d7 4128 r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
0293a7a8
EV
4129 if (r == 0)
4130 break;
4131 if (r == -ENOMEM)
4132 return log_oom();
727f76d7 4133 if (r < 0) {
7b07e993 4134 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
727f76d7
EV
4135 return 0;
4136 }
94828d2d 4137
727f76d7
EV
4138 if (!utf8_is_valid(word)) {
4139 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
94828d2d
LP
4140 continue;
4141 }
4142
20b7a007
LP
4143 w = word;
4144 if (startswith(w, "-")) {
4145 ignore_enoent = true;
4146 w++;
4147 }
4148 if (startswith(w, "+")) {
4149 shall_prefix = true;
4150 w++;
4151 }
7b07e993 4152
20b7a007 4153 r = unit_full_printf(u, w, &resolved);
7b07e993
LP
4154 if (r < 0) {
4155 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word);
94828d2d
LP
4156 continue;
4157 }
4158
7b07e993
LP
4159 if (!path_is_absolute(resolved)) {
4160 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", resolved);
4161 continue;
4162 }
4163
4164 path_kill_slashes(resolved);
94828d2d 4165
20b7a007
LP
4166 joined = strjoin(ignore_enoent ? "-" : "",
4167 shall_prefix ? "+" : "",
4168 resolved);
7b07e993
LP
4169
4170 r = strv_push(sv, joined);
94828d2d
LP
4171 if (r < 0)
4172 return log_oom();
4173
7b07e993 4174 joined = NULL;
94828d2d
LP
4175 }
4176
4177 return 0;
4178}
4179
2abd4e38
YW
4180int config_parse_temporary_filesystems(
4181 const char *unit,
4182 const char *filename,
4183 unsigned line,
4184 const char *section,
4185 unsigned section_line,
4186 const char *lvalue,
4187 int ltype,
4188 const char *rvalue,
4189 void *data,
4190 void *userdata) {
4191
4192 Unit *u = userdata;
4193 ExecContext *c = data;
4194 const char *cur;
4195 int r;
4196
4197 assert(filename);
4198 assert(lvalue);
4199 assert(rvalue);
4200 assert(data);
4201
4202 if (isempty(rvalue)) {
4203 /* Empty assignment resets the list */
4204 temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
4205 c->temporary_filesystems = NULL;
4206 c->n_temporary_filesystems = 0;
4207 return 0;
4208 }
4209
4210 cur = rvalue;
4211 for (;;) {
4212 _cleanup_free_ char *word = NULL, *path = NULL, *resolved = NULL;
4213 const char *w;
4214
4215 r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
4216 if (r == 0)
4217 break;
4218 if (r == -ENOMEM)
4219 return log_oom();
4220 if (r < 0) {
4221 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
4222 return 0;
4223 }
4224
4225 w = word;
4226 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4227 if (r < 0)
4228 return r;
4229 if (r == 0)
4230 return -EINVAL;
4231
4232 r = unit_full_printf(u, path, &resolved);
4233 if (r < 0) {
4234 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s, ignoring: %m", word);
4235 continue;
4236 }
4237
4238 if (!path_is_absolute(resolved)) {
4239 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", resolved);
4240 continue;
4241 }
4242
4243 path_kill_slashes(resolved);
4244
4245 r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, path, w);
4246 if (r == -ENOMEM)
4247 return log_oom();
4248 if (r < 0) {
4249 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse mount options, ignoring: %s", word);
4250 continue;
4251 }
4252 }
4253
4254 return 0;
4255}
4256
d2d6c096
LP
4257int config_parse_bind_paths(
4258 const char *unit,
4259 const char *filename,
4260 unsigned line,
4261 const char *section,
4262 unsigned section_line,
4263 const char *lvalue,
4264 int ltype,
4265 const char *rvalue,
4266 void *data,
4267 void *userdata) {
4268
4269 ExecContext *c = data;
42d43f21 4270 Unit *u = userdata;
d2d6c096
LP
4271 const char *p;
4272 int r;
4273
4274 assert(filename);
4275 assert(lvalue);
4276 assert(rvalue);
4277 assert(data);
4278
4279 if (isempty(rvalue)) {
4280 /* Empty assignment resets the list */
4281 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
4282 c->bind_mounts = NULL;
4283 c->n_bind_mounts = 0;
4284 return 0;
4285 }
4286
4287 p = rvalue;
4288 for (;;) {
4289 _cleanup_free_ char *source = NULL, *destination = NULL;
42d43f21 4290 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
d2d6c096
LP
4291 char *s = NULL, *d = NULL;
4292 bool rbind = true, ignore_enoent = false;
4293
4294 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
4295 if (r == 0)
4296 break;
4297 if (r == -ENOMEM)
4298 return log_oom();
4299 if (r < 0) {
4300 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
4301 return 0;
4302 }
4303
42d43f21
DC
4304 r = unit_full_printf(u, source, &sresolved);
4305 if (r < 0) {
4306 log_syntax(unit, LOG_ERR, filename, line, r,
4307 "Failed to resolved specifiers in \"%s\", ignoring: %m", source);
4308 return 0;
4309 }
4310
4311 s = sresolved;
d2d6c096
LP
4312 if (s[0] == '-') {
4313 ignore_enoent = true;
4314 s++;
4315 }
4316
4317 if (!utf8_is_valid(s)) {
4318 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, s);
4319 return 0;
4320 }
4321 if (!path_is_absolute(s)) {
4322 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute source path, ignoring: %s", s);
4323 return 0;
4324 }
4325
4326 path_kill_slashes(s);
4327
4328 /* Optionally, the destination is specified. */
4329 if (p && p[-1] == ':') {
4330 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
4331 if (r == -ENOMEM)
4332 return log_oom();
4333 if (r < 0) {
4334 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
4335 return 0;
4336 }
4337 if (r == 0) {
4338 log_syntax(unit, LOG_ERR, filename, line, 0, "Missing argument after ':': %s", rvalue);
4339 return 0;
4340 }
4341
42d43f21
DC
4342 r = unit_full_printf(u, destination, &dresolved);
4343 if (r < 0) {
4344 log_syntax(unit, LOG_ERR, filename, line, r,
4345 "Failed to resolved specifiers in \"%s\", ignoring: %m", destination);
4346 return 0;
4347 }
4348
4349 if (!utf8_is_valid(dresolved)) {
4350 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, dresolved);
d2d6c096
LP
4351 return 0;
4352 }
42d43f21
DC
4353 if (!path_is_absolute(dresolved)) {
4354 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute destination path, ignoring: %s", dresolved);
d2d6c096
LP
4355 return 0;
4356 }
4357
42d43f21 4358 d = path_kill_slashes(dresolved);
d2d6c096
LP
4359
4360 /* Optionally, there's also a short option string specified */
4361 if (p && p[-1] == ':') {
4362 _cleanup_free_ char *options = NULL;
4363
4364 r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
4365 if (r == -ENOMEM)
4366 return log_oom();
4367 if (r < 0) {
4368 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
4369 return 0;
4370 }
4371
4372 if (isempty(options) || streq(options, "rbind"))
4373 rbind = true;
4374 else if (streq(options, "norbind"))
4375 rbind = false;
4376 else {
4377 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
4378 return 0;
4379 }
4380 }
4381 } else
4382 d = s;
4383
4384 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
4385 &(BindMount) {
4386 .source = s,
4387 .destination = d,
4388 .read_only = !!strstr(lvalue, "ReadOnly"),
4389 .recursive = rbind,
4390 .ignore_enoent = ignore_enoent,
4391 });
4392 if (r < 0)
4393 return log_oom();
4394 }
4395
4396 return 0;
4397}
4398
f1721625 4399int config_parse_no_new_privileges(
760b9d7c
LP
4400 const char* unit,
4401 const char *filename,
4402 unsigned line,
4403 const char *section,
4404 unsigned section_line,
4405 const char *lvalue,
4406 int ltype,
4407 const char *rvalue,
4408 void *data,
4409 void *userdata) {
4410
4411 ExecContext *c = data;
4412 int k;
4413
4414 assert(filename);
4415 assert(lvalue);
4416 assert(rvalue);
4417 assert(data);
4418
4419 k = parse_boolean(rvalue);
4420 if (k < 0) {
12ca818f 4421 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
760b9d7c
LP
4422 return 0;
4423 }
4424
9b232d32 4425 c->no_new_privileges = k;
760b9d7c
LP
4426
4427 return 0;
4428}
4429
1b8689f9 4430int config_parse_protect_home(
417116f2
LP
4431 const char* unit,
4432 const char *filename,
4433 unsigned line,
4434 const char *section,
4435 unsigned section_line,
4436 const char *lvalue,
4437 int ltype,
4438 const char *rvalue,
4439 void *data,
4440 void *userdata) {
4441
4442 ExecContext *c = data;
5e1c6154 4443 ProtectHome h;
417116f2
LP
4444
4445 assert(filename);
4446 assert(lvalue);
4447 assert(rvalue);
4448 assert(data);
4449
4450 /* Our enum shall be a superset of booleans, hence first try
61233823 4451 * to parse as boolean, and then as enum */
417116f2 4452
5e1c6154
YW
4453 h = parse_protect_home_or_bool(rvalue);
4454 if (h < 0) {
4455 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
4456 return 0;
1b8689f9
LP
4457 }
4458
5e1c6154
YW
4459 c->protect_home = h;
4460
1b8689f9
LP
4461 return 0;
4462}
4463
4464int config_parse_protect_system(
4465 const char* unit,
4466 const char *filename,
4467 unsigned line,
4468 const char *section,
4469 unsigned section_line,
4470 const char *lvalue,
4471 int ltype,
4472 const char *rvalue,
4473 void *data,
4474 void *userdata) {
4475
4476 ExecContext *c = data;
03c791aa 4477 ProtectSystem s;
1b8689f9
LP
4478
4479 assert(filename);
4480 assert(lvalue);
4481 assert(rvalue);
4482 assert(data);
4483
4484 /* Our enum shall be a superset of booleans, hence first try
61233823 4485 * to parse as boolean, and then as enum */
1b8689f9 4486
03c791aa
YW
4487 s = parse_protect_system_or_bool(rvalue);
4488 if (s < 0) {
4489 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
4490 return 0;
417116f2
LP
4491 }
4492
03c791aa
YW
4493 c->protect_system = s;
4494
417116f2
LP
4495 return 0;
4496}
4497
b1edf445
LP
4498DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
4499
eae51da3
LP
4500int config_parse_job_timeout_sec(
4501 const char* unit,
4502 const char *filename,
4503 unsigned line,
4504 const char *section,
4505 unsigned section_line,
4506 const char *lvalue,
4507 int ltype,
4508 const char *rvalue,
4509 void *data,
4510 void *userdata) {
4511
4512 Unit *u = data;
4513 usec_t usec;
4514 int r;
4515
4516 assert(filename);
4517 assert(lvalue);
4518 assert(rvalue);
4519 assert(u);
4520
4521 r = parse_sec_fix_0(rvalue, &usec);
4522 if (r < 0) {
4523 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue);
4524 return 0;
4525 }
4526
4527 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
c05f3c8f 4528 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
eae51da3
LP
4529 * count. */
4530
4531 if (!u->job_running_timeout_set)
4532 u->job_running_timeout = usec;
4533
4534 u->job_timeout = usec;
4535
4536 return 0;
4537}
4538
4539int config_parse_job_running_timeout_sec(
4540 const char* unit,
4541 const char *filename,
4542 unsigned line,
4543 const char *section,
4544 unsigned section_line,
4545 const char *lvalue,
4546 int ltype,
4547 const char *rvalue,
4548 void *data,
4549 void *userdata) {
4550
4551 Unit *u = data;
4552 usec_t usec;
4553 int r;
4554
4555 assert(filename);
4556 assert(lvalue);
4557 assert(rvalue);
4558 assert(u);
4559
4560 r = parse_sec_fix_0(rvalue, &usec);
4561 if (r < 0) {
4562 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue);
4563 return 0;
4564 }
4565
4566 u->job_running_timeout = usec;
4567 u->job_running_timeout_set = true;
4568
4569 return 0;
4570}
4571
071830ff 4572#define FOLLOW_MAX 8
87f0e418 4573
9e2f7c11 4574static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
a837f088 4575 char *id = NULL;
0301abf4 4576 unsigned c = 0;
87f0e418
LP
4577 int fd, r;
4578 FILE *f;
87f0e418
LP
4579
4580 assert(filename);
4581 assert(*filename);
4582 assert(_f);
4583 assert(names);
4584
0301abf4
LP
4585 /* This will update the filename pointer if the loaded file is
4586 * reached by a symlink. The old string will be freed. */
87f0e418 4587
0301abf4 4588 for (;;) {
2c7108c4 4589 char *target, *name;
87f0e418 4590
0301abf4
LP
4591 if (c++ >= FOLLOW_MAX)
4592 return -ELOOP;
4593
b08d03ff
LP
4594 path_kill_slashes(*filename);
4595
87f0e418 4596 /* Add the file name we are currently looking at to
8f05424d
LP
4597 * the names of this unit, but only if it is a valid
4598 * unit name. */
2b6bf07d 4599 name = basename(*filename);
7410616c 4600 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
8f05424d 4601
15e11d81
LP
4602 id = set_get(names, name);
4603 if (!id) {
4604 id = strdup(name);
4605 if (!id)
8f05424d 4606 return -ENOMEM;
87f0e418 4607
ef42202a
ZJS
4608 r = set_consume(names, id);
4609 if (r < 0)
8f05424d 4610 return r;
87f0e418 4611 }
87f0e418
LP
4612 }
4613
0301abf4 4614 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
4615 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
4616 if (fd >= 0)
87f0e418
LP
4617 break;
4618
0301abf4
LP
4619 if (errno != ELOOP)
4620 return -errno;
4621
87f0e418 4622 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
4623 r = readlink_and_make_absolute(*filename, &target);
4624 if (r < 0)
0301abf4 4625 return r;
87f0e418 4626
0301abf4 4627 free(*filename);
2c7108c4 4628 *filename = target;
87f0e418
LP
4629 }
4630
9946996c
LP
4631 f = fdopen(fd, "re");
4632 if (!f) {
03e334a1 4633 safe_close(fd);
d4ad27a1 4634 return -errno;
87f0e418
LP
4635 }
4636
4637 *_f = f;
9e2f7c11 4638 *_final = id;
a837f088 4639
0301abf4 4640 return 0;
87f0e418
LP
4641}
4642
23a177ef
LP
4643static int merge_by_names(Unit **u, Set *names, const char *id) {
4644 char *k;
4645 int r;
4646
4647 assert(u);
4648 assert(*u);
4649 assert(names);
4650
4651 /* Let's try to add in all symlink names we found */
4652 while ((k = set_steal_first(names))) {
4653
4654 /* First try to merge in the other name into our
4655 * unit */
9946996c
LP
4656 r = unit_merge_by_name(*u, k);
4657 if (r < 0) {
23a177ef
LP
4658 Unit *other;
4659
4660 /* Hmm, we couldn't merge the other unit into
4661 * ours? Then let's try it the other way
4662 * round */
4663
7aad67e7
MS
4664 /* If the symlink name we are looking at is unit template, then
4665 we must search for instance of this template */
9d3e3406 4666 if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) {
7aad67e7
MS
4667 _cleanup_free_ char *instance = NULL;
4668
4669 r = unit_name_replace_instance(k, (*u)->instance, &instance);
4670 if (r < 0)
4671 return r;
4672
4673 other = manager_get_unit((*u)->manager, instance);
4674 } else
4675 other = manager_get_unit((*u)->manager, k);
4676
23a177ef
LP
4677 free(k);
4678
9946996c
LP
4679 if (other) {
4680 r = unit_merge(other, *u);
4681 if (r >= 0) {
23a177ef
LP
4682 *u = other;
4683 return merge_by_names(u, names, NULL);
4684 }
9946996c 4685 }
23a177ef
LP
4686
4687 return r;
4688 }
4689
4690 if (id == k)
4691 unit_choose_id(*u, id);
4692
4693 free(k);
4694 }
4695
4696 return 0;
4697}
4698
e537352b 4699static int load_from_path(Unit *u, const char *path) {
e48614c4
ZJS
4700 _cleanup_set_free_free_ Set *symlink_names = NULL;
4701 _cleanup_fclose_ FILE *f = NULL;
4702 _cleanup_free_ char *filename = NULL;
4703 char *id = NULL;
23a177ef 4704 Unit *merged;
45fb0699 4705 struct stat st;
a837f088 4706 int r;
23a177ef
LP
4707
4708 assert(u);
e537352b 4709 assert(path);
3efd4195 4710
d5099efc 4711 symlink_names = set_new(&string_hash_ops);
f975e971 4712 if (!symlink_names)
87f0e418 4713 return -ENOMEM;
3efd4195 4714
036643a2
LP
4715 if (path_is_absolute(path)) {
4716
9946996c 4717 filename = strdup(path);
e48614c4
ZJS
4718 if (!filename)
4719 return -ENOMEM;
036643a2 4720
9946996c
LP
4721 r = open_follow(&filename, &f, symlink_names, &id);
4722 if (r < 0) {
97b11eed 4723 filename = mfree(filename);
036643a2 4724 if (r != -ENOENT)
e48614c4 4725 return r;
036643a2
LP
4726 }
4727
4728 } else {
4729 char **p;
4730
a3c4eb07 4731 STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
036643a2
LP
4732
4733 /* Instead of opening the path right away, we manually
4734 * follow all symlinks and add their name to our unit
4735 * name set while doing so */
9946996c 4736 filename = path_make_absolute(path, *p);
e48614c4
ZJS
4737 if (!filename)
4738 return -ENOMEM;
036643a2 4739
ac155bb8
MS
4740 if (u->manager->unit_path_cache &&
4741 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
4742 r = -ENOENT;
4743 else
4744 r = open_follow(&filename, &f, symlink_names, &id);
a837f088
LP
4745 if (r >= 0)
4746 break;
4747 filename = mfree(filename);
a1feacf7
ZJS
4748
4749 /* ENOENT means that the file is missing or is a dangling symlink.
4750 * ENOTDIR means that one of paths we expect to be is a directory
4751 * is not a directory, we should just ignore that.
4752 * EACCES means that the directory or file permissions are wrong.
4753 */
4754 if (r == -EACCES)
4755 log_debug_errno(r, "Cannot access \"%s\": %m", filename);
4756 else if (!IN_SET(r, -ENOENT, -ENOTDIR))
a837f088 4757 return r;
fe51822e 4758
a837f088
LP
4759 /* Empty the symlink names for the next run */
4760 set_clear_free(symlink_names);
036643a2
LP
4761 }
4762 }
034c6ed7 4763
e48614c4 4764 if (!filename)
8f05424d 4765 /* Hmm, no suitable file found? */
e48614c4 4766 return 0;
87f0e418 4767
8a993b61 4768 if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
a837f088
LP
4769 log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
4770 return -ELOOP;
4771 }
4772
23a177ef 4773 merged = u;
9946996c
LP
4774 r = merge_by_names(&merged, symlink_names, id);
4775 if (r < 0)
e48614c4 4776 return r;
87f0e418 4777
23a177ef 4778 if (merged != u) {
ac155bb8 4779 u->load_state = UNIT_MERGED;
e48614c4 4780 return 0;
034c6ed7
LP
4781 }
4782
e48614c4
ZJS
4783 if (fstat(fileno(f), &st) < 0)
4784 return -errno;
45fb0699 4785
3a8db9fe 4786 if (null_or_empty(&st)) {
ac155bb8 4787 u->load_state = UNIT_MASKED;
3a8db9fe
ZJS
4788 u->fragment_mtime = 0;
4789 } else {
c2756a68 4790 u->load_state = UNIT_LOADED;
3a8db9fe 4791 u->fragment_mtime = timespec_load(&st.st_mtim);
c2756a68 4792
00dc5d76 4793 /* Now, parse the file contents */
36f822c4
ZJS
4794 r = config_parse(u->id, filename, f,
4795 UNIT_VTABLE(u)->sections,
4796 config_item_perf_lookup, load_fragment_gperf_lookup,
bcde742e 4797 CONFIG_PARSE_ALLOW_INCLUDE, u);
f975e971 4798 if (r < 0)
e48614c4 4799 return r;
00dc5d76 4800 }
b08d03ff 4801
ac155bb8
MS
4802 free(u->fragment_path);
4803 u->fragment_path = filename;
0301abf4 4804 filename = NULL;
87f0e418 4805
1b64d026
LP
4806 if (u->source_path) {
4807 if (stat(u->source_path, &st) >= 0)
4808 u->source_mtime = timespec_load(&st.st_mtim);
4809 else
4810 u->source_mtime = 0;
4811 }
4812
e48614c4 4813 return 0;
0301abf4
LP
4814}
4815
e537352b 4816int unit_load_fragment(Unit *u) {
23a177ef 4817 int r;
294d81f1
LP
4818 Iterator i;
4819 const char *t;
0301abf4
LP
4820
4821 assert(u);
ac155bb8
MS
4822 assert(u->load_state == UNIT_STUB);
4823 assert(u->id);
23a177ef 4824
3f5e8115
LP
4825 if (u->transient) {
4826 u->load_state = UNIT_LOADED;
4827 return 0;
4828 }
4829
294d81f1
LP
4830 /* First, try to find the unit under its id. We always look
4831 * for unit files in the default directories, to make it easy
4832 * to override things by placing things in /etc/systemd/system */
9946996c
LP
4833 r = load_from_path(u, u->id);
4834 if (r < 0)
294d81f1
LP
4835 return r;
4836
4837 /* Try to find an alias we can load this with */
abc08d4d 4838 if (u->load_state == UNIT_STUB) {
ac155bb8 4839 SET_FOREACH(t, u->names, i) {
294d81f1 4840
ac155bb8 4841 if (t == u->id)
294d81f1
LP
4842 continue;
4843
9946996c
LP
4844 r = load_from_path(u, t);
4845 if (r < 0)
294d81f1
LP
4846 return r;
4847
ac155bb8 4848 if (u->load_state != UNIT_STUB)
294d81f1
LP
4849 break;
4850 }
abc08d4d 4851 }
23a177ef 4852
294d81f1 4853 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 4854 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 4855
9946996c
LP
4856 r = load_from_path(u, u->fragment_path);
4857 if (r < 0)
23a177ef 4858 return r;
0301abf4 4859
ece174c5 4860 if (u->load_state == UNIT_STUB)
6ccb1b44
LP
4861 /* Hmm, this didn't work? Then let's get rid
4862 * of the fragment path stored for us, so that
4863 * we don't point to an invalid location. */
a1e58e8e 4864 u->fragment_path = mfree(u->fragment_path);
6ccb1b44
LP
4865 }
4866
294d81f1 4867 /* Look for a template */
ac155bb8 4868 if (u->load_state == UNIT_STUB && u->instance) {
7410616c 4869 _cleanup_free_ char *k = NULL;
294d81f1 4870
7410616c
LP
4871 r = unit_name_template(u->id, &k);
4872 if (r < 0)
4873 return r;
294d81f1
LP
4874
4875 r = load_from_path(u, k);
bb28e684
ZJS
4876 if (r < 0) {
4877 if (r == -ENOEXEC)
4878 log_unit_notice(u, "Unit configuration has fatal error, unit will not be started.");
9e2f7c11 4879 return r;
bb28e684 4880 }
890f434c 4881
abc08d4d 4882 if (u->load_state == UNIT_STUB) {
ac155bb8 4883 SET_FOREACH(t, u->names, i) {
bc9fd78c 4884 _cleanup_free_ char *z = NULL;
87f0e418 4885
ac155bb8 4886 if (t == u->id)
23a177ef 4887 continue;
071830ff 4888
7410616c
LP
4889 r = unit_name_template(t, &z);
4890 if (r < 0)
4891 return r;
294d81f1 4892
bc9fd78c 4893 r = load_from_path(u, z);
294d81f1 4894 if (r < 0)
23a177ef 4895 return r;
890f434c 4896
ac155bb8 4897 if (u->load_state != UNIT_STUB)
23a177ef
LP
4898 break;
4899 }
abc08d4d 4900 }
071830ff
LP
4901 }
4902
23a177ef 4903 return 0;
3efd4195 4904}
e537352b
LP
4905
4906void unit_dump_config_items(FILE *f) {
f975e971
LP
4907 static const struct {
4908 const ConfigParserCallback callback;
4909 const char *rvalue;
4910 } table[] = {
f9fa32f0 4911#if !HAVE_SYSV_COMPAT || !HAVE_SECCOMP || !HAVE_PAM || !HAVE_SELINUX || !ENABLE_SMACK || !HAVE_APPARMOR
17df7223
LP
4912 { config_parse_warn_compat, "NOTSUPPORTED" },
4913#endif
f975e971
LP
4914 { config_parse_int, "INTEGER" },
4915 { config_parse_unsigned, "UNSIGNED" },
5556b5fe 4916 { config_parse_iec_size, "SIZE" },
59f448cf 4917 { config_parse_iec_uint64, "SIZE" },
5556b5fe 4918 { config_parse_si_size, "SIZE" },
f975e971
LP
4919 { config_parse_bool, "BOOLEAN" },
4920 { config_parse_string, "STRING" },
4921 { config_parse_path, "PATH" },
4922 { config_parse_unit_path_printf, "PATH" },
4923 { config_parse_strv, "STRING [...]" },
4924 { config_parse_exec_nice, "NICE" },
4925 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
4926 { config_parse_exec_io_class, "IOCLASS" },
4927 { config_parse_exec_io_priority, "IOPRIORITY" },
4928 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
4929 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
4930 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
4931 { config_parse_mode, "MODE" },
4932 { config_parse_unit_env_file, "FILE" },
52c239d7
LB
4933 { config_parse_exec_output, "OUTPUT" },
4934 { config_parse_exec_input, "INPUT" },
ca37242e
LP
4935 { config_parse_log_facility, "FACILITY" },
4936 { config_parse_log_level, "LEVEL" },
f975e971 4937 { config_parse_exec_secure_bits, "SECUREBITS" },
a103496c 4938 { config_parse_capability_set, "BOUNDINGSET" },
f975e971 4939 { config_parse_limit, "LIMIT" },
f975e971 4940 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
4941 { config_parse_exec, "PATH [ARGUMENT [...]]" },
4942 { config_parse_service_type, "SERVICETYPE" },
4943 { config_parse_service_restart, "SERVICERESTART" },
349cc4a5 4944#if HAVE_SYSV_COMPAT
f975e971 4945 { config_parse_sysv_priority, "SYSVPRIORITY" },
f975e971
LP
4946#endif
4947 { config_parse_kill_mode, "KILLMODE" },
f757855e 4948 { config_parse_signal, "SIGNAL" },
f975e971
LP
4949 { config_parse_socket_listen, "SOCKET [...]" },
4950 { config_parse_socket_bind, "SOCKETBIND" },
4951 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 4952 { config_parse_sec, "SECONDS" },
d88a251b 4953 { config_parse_nsec, "NANOSECONDS" },
94828d2d 4954 { config_parse_namespace_path_strv, "PATH [...]" },
d2d6c096 4955 { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
7c8fa05c 4956 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
4957 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
4958 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 4959 { config_parse_trigger_unit, "UNIT" },
f975e971 4960 { config_parse_timer, "TIMER" },
f975e971 4961 { config_parse_path_spec, "PATH" },
f975e971
LP
4962 { config_parse_notify_access, "ACCESS" },
4963 { config_parse_ip_tos, "TOS" },
4964 { config_parse_unit_condition_path, "CONDITION" },
4965 { config_parse_unit_condition_string, "CONDITION" },
4966 { config_parse_unit_condition_null, "CONDITION" },
a016b922 4967 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
4968 { config_parse_documentation, "URL" },
4969 { config_parse_service_timeout, "SECONDS" },
87a47f99 4970 { config_parse_emergency_action, "ACTION" },
7f0386f6
LP
4971 { config_parse_set_status, "STATUS" },
4972 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 4973 { config_parse_environ, "ENVIRON" },
349cc4a5 4974#if HAVE_SECCOMP
17df7223 4975 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 4976 { config_parse_syscall_archs, "ARCHS" },
17df7223 4977 { config_parse_syscall_errno, "ERRNO" },
4298d0b5 4978 { config_parse_address_families, "FAMILIES" },
add00535 4979 { config_parse_restrict_namespaces, "NAMESPACES" },
c0467cf3 4980#endif
7f0386f6 4981 { config_parse_cpu_shares, "SHARES" },
66ebf6c0 4982 { config_parse_cpu_weight, "WEIGHT" },
7f0386f6
LP
4983 { config_parse_memory_limit, "LIMIT" },
4984 { config_parse_device_allow, "DEVICE" },
4985 { config_parse_device_policy, "POLICY" },
13c31542
TH
4986 { config_parse_io_limit, "LIMIT" },
4987 { config_parse_io_weight, "WEIGHT" },
4988 { config_parse_io_device_weight, "DEVICEWEIGHT" },
7f0386f6
LP
4989 { config_parse_blockio_bandwidth, "BANDWIDTH" },
4990 { config_parse_blockio_weight, "WEIGHT" },
4991 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
4992 { config_parse_long, "LONG" },
4993 { config_parse_socket_service, "SERVICE" },
349cc4a5 4994#if HAVE_SELINUX
6a6751fe
LP
4995 { config_parse_exec_selinux_context, "LABEL" },
4996#endif
4997 { config_parse_job_mode, "MODE" },
4998 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 4999 { config_parse_personality, "PERSONALITY" },
f975e971
LP
5000 };
5001
5002 const char *prev = NULL;
5003 const char *i;
5004
5005 assert(f);
e537352b 5006
f975e971
LP
5007 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
5008 const char *rvalue = "OTHER", *lvalue;
5009 unsigned j;
5010 size_t prefix_len;
5011 const char *dot;
5012 const ConfigPerfItem *p;
5013
5014 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
5015
5016 dot = strchr(i, '.');
5017 lvalue = dot ? dot + 1 : i;
5018 prefix_len = dot-i;
5019
5020 if (dot)
641906e9 5021 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
5022 if (prev)
5023 fputc('\n', f);
5024
5025 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
5026 }
5027
5028 for (j = 0; j < ELEMENTSOF(table); j++)
5029 if (p->parse == table[j].callback) {
5030 rvalue = table[j].rvalue;
5031 break;
5032 }
5033
5034 fprintf(f, "%s=%s\n", lvalue, rvalue);
5035 prev = i;
5036 }
e537352b 5037}