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