]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
namespace: instead of chasing mount symlinks a priori, do so as-we-go
[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];
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
83555251 1281 unsigned long flags;
e28bb14a 1282 ExecContext *c = data;
15ae422b
LP
1283
1284 assert(filename);
1285 assert(lvalue);
1286 assert(rvalue);
1287 assert(data);
1288
83555251
LP
1289 if (isempty(rvalue))
1290 flags = 0;
e28bb14a 1291 else {
83555251
LP
1292 flags = mount_propagation_flags_from_string(rvalue);
1293 if (flags == 0) {
1294 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring.", rvalue);
1295 return 0;
1296 }
15ae422b
LP
1297 }
1298
1299 c->mount_flags = flags;
e28bb14a 1300
15ae422b
LP
1301 return 0;
1302}
1303
5f8640fb
LP
1304int config_parse_exec_selinux_context(
1305 const char *unit,
1306 const char *filename,
1307 unsigned line,
1308 const char *section,
1309 unsigned section_line,
1310 const char *lvalue,
1311 int ltype,
1312 const char *rvalue,
1313 void *data,
1314 void *userdata) {
1315
1316 ExecContext *c = data;
1317 Unit *u = userdata;
1318 bool ignore;
1319 char *k;
1320 int r;
1321
1322 assert(filename);
1323 assert(lvalue);
1324 assert(rvalue);
1325 assert(data);
1326
1327 if (isempty(rvalue)) {
a1e58e8e 1328 c->selinux_context = mfree(c->selinux_context);
5f8640fb
LP
1329 c->selinux_context_ignore = false;
1330 return 0;
1331 }
1332
1333 if (rvalue[0] == '-') {
1334 ignore = true;
1335 rvalue++;
1336 } else
1337 ignore = false;
1338
18913df9 1339 r = unit_full_printf(u, rvalue, &k);
5f8640fb 1340 if (r < 0) {
12ca818f 1341 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
5f8640fb
LP
1342 return 0;
1343 }
1344
1345 free(c->selinux_context);
1346 c->selinux_context = k;
1347 c->selinux_context_ignore = ignore;
1348
1349 return 0;
1350}
1351
eef65bf3
MS
1352int config_parse_exec_apparmor_profile(
1353 const char *unit,
1354 const char *filename,
1355 unsigned line,
1356 const char *section,
1357 unsigned section_line,
1358 const char *lvalue,
1359 int ltype,
1360 const char *rvalue,
1361 void *data,
1362 void *userdata) {
1363
1364 ExecContext *c = data;
1365 Unit *u = userdata;
1366 bool ignore;
1367 char *k;
1368 int r;
1369
1370 assert(filename);
1371 assert(lvalue);
1372 assert(rvalue);
1373 assert(data);
1374
1375 if (isempty(rvalue)) {
a1e58e8e 1376 c->apparmor_profile = mfree(c->apparmor_profile);
eef65bf3
MS
1377 c->apparmor_profile_ignore = false;
1378 return 0;
1379 }
1380
1381 if (rvalue[0] == '-') {
1382 ignore = true;
1383 rvalue++;
1384 } else
1385 ignore = false;
1386
18913df9 1387 r = unit_full_printf(u, rvalue, &k);
eef65bf3 1388 if (r < 0) {
12ca818f 1389 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
eef65bf3
MS
1390 return 0;
1391 }
1392
1393 free(c->apparmor_profile);
1394 c->apparmor_profile = k;
1395 c->apparmor_profile_ignore = ignore;
1396
1397 return 0;
1398}
1399
2ca620c4
WC
1400int config_parse_exec_smack_process_label(
1401 const char *unit,
1402 const char *filename,
1403 unsigned line,
1404 const char *section,
1405 unsigned section_line,
1406 const char *lvalue,
1407 int ltype,
1408 const char *rvalue,
1409 void *data,
1410 void *userdata) {
1411
1412 ExecContext *c = data;
1413 Unit *u = userdata;
1414 bool ignore;
1415 char *k;
1416 int r;
1417
1418 assert(filename);
1419 assert(lvalue);
1420 assert(rvalue);
1421 assert(data);
1422
1423 if (isempty(rvalue)) {
a1e58e8e 1424 c->smack_process_label = mfree(c->smack_process_label);
2ca620c4
WC
1425 c->smack_process_label_ignore = false;
1426 return 0;
1427 }
1428
1429 if (rvalue[0] == '-') {
1430 ignore = true;
1431 rvalue++;
1432 } else
1433 ignore = false;
1434
18913df9 1435 r = unit_full_printf(u, rvalue, &k);
2ca620c4 1436 if (r < 0) {
12ca818f 1437 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
2ca620c4
WC
1438 return 0;
1439 }
1440
1441 free(c->smack_process_label);
1442 c->smack_process_label = k;
1443 c->smack_process_label_ignore = ignore;
1444
1445 return 0;
1446}
1447
e8e581bf
ZJS
1448int config_parse_timer(const char *unit,
1449 const char *filename,
1450 unsigned line,
1451 const char *section,
71a61510 1452 unsigned section_line,
e8e581bf
ZJS
1453 const char *lvalue,
1454 int ltype,
1455 const char *rvalue,
1456 void *data,
1457 void *userdata) {
871d7de4
LP
1458
1459 Timer *t = data;
2507992f 1460 usec_t usec = 0;
871d7de4
LP
1461 TimerValue *v;
1462 TimerBase b;
36697dc0 1463 CalendarSpec *c = NULL;
2507992f
DC
1464 Unit *u = userdata;
1465 _cleanup_free_ char *k = NULL;
1466 int r;
871d7de4
LP
1467
1468 assert(filename);
1469 assert(lvalue);
1470 assert(rvalue);
1471 assert(data);
1472
74051b9b
LP
1473 if (isempty(rvalue)) {
1474 /* Empty assignment resets list */
1475 timer_free_values(t);
1476 return 0;
1477 }
1478
36697dc0
LP
1479 b = timer_base_from_string(lvalue);
1480 if (b < 0) {
12ca818f 1481 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer base, ignoring: %s", lvalue);
c0b34696 1482 return 0;
871d7de4
LP
1483 }
1484
2507992f
DC
1485 r = unit_full_printf(u, rvalue, &k);
1486 if (r < 0) {
1487 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
1488 return 0;
1489 }
1490
36697dc0 1491 if (b == TIMER_CALENDAR) {
2507992f
DC
1492 if (calendar_spec_from_string(k, &c) < 0) {
1493 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", k);
36697dc0
LP
1494 return 0;
1495 }
36697dc0 1496 } else {
2507992f
DC
1497 if (parse_sec(k, &usec) < 0) {
1498 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", k);
36697dc0
LP
1499 return 0;
1500 }
871d7de4
LP
1501 }
1502
36697dc0 1503 v = new0(TimerValue, 1);
4d5e13a1 1504 if (!v) {
0b76b4d8 1505 calendar_spec_free(c);
74051b9b 1506 return log_oom();
4d5e13a1 1507 }
871d7de4
LP
1508
1509 v->base = b;
2507992f 1510 v->value = usec;
36697dc0 1511 v->calendar_spec = c;
871d7de4 1512
71fda00f 1513 LIST_PREPEND(value, t->values, v);
871d7de4
LP
1514
1515 return 0;
1516}
1517
3ecaa09b
LP
1518int config_parse_trigger_unit(
1519 const char *unit,
1520 const char *filename,
1521 unsigned line,
1522 const char *section,
71a61510 1523 unsigned section_line,
3ecaa09b
LP
1524 const char *lvalue,
1525 int ltype,
1526 const char *rvalue,
1527 void *data,
1528 void *userdata) {
871d7de4 1529
74051b9b 1530 _cleanup_free_ char *p = NULL;
3ecaa09b
LP
1531 Unit *u = data;
1532 UnitType type;
1533 int r;
398ef8ba
LP
1534
1535 assert(filename);
1536 assert(lvalue);
1537 assert(rvalue);
1538 assert(data);
1539
3ecaa09b 1540 if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
12ca818f 1541 log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
3ecaa09b
LP
1542 return 0;
1543 }
871d7de4 1544
19f6d710 1545 r = unit_name_printf(u, rvalue, &p);
12ca818f
LP
1546 if (r < 0) {
1547 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1548 return 0;
1549 }
74051b9b 1550
12ca818f 1551 type = unit_name_to_type(p);
3ecaa09b 1552 if (type < 0) {
12ca818f 1553 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue);
c0b34696 1554 return 0;
871d7de4
LP
1555 }
1556
3ecaa09b 1557 if (type == u->type) {
12ca818f 1558 log_syntax(unit, LOG_ERR, filename, line, 0, "Trigger cannot be of same type, ignoring: %s", rvalue);
3ecaa09b
LP
1559 return 0;
1560 }
1561
12ca818f 1562 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
57020a3a 1563 if (r < 0) {
12ca818f 1564 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
c0b34696 1565 return 0;
871d7de4
LP
1566 }
1567
1568 return 0;
1569}
1570
e8e581bf
ZJS
1571int config_parse_path_spec(const char *unit,
1572 const char *filename,
1573 unsigned line,
1574 const char *section,
71a61510 1575 unsigned section_line,
e8e581bf
ZJS
1576 const char *lvalue,
1577 int ltype,
1578 const char *rvalue,
1579 void *data,
1580 void *userdata) {
01f78473
LP
1581
1582 Path *p = data;
1583 PathSpec *s;
1584 PathType b;
7fd1b19b 1585 _cleanup_free_ char *k = NULL;
19f6d710 1586 int r;
01f78473
LP
1587
1588 assert(filename);
1589 assert(lvalue);
1590 assert(rvalue);
1591 assert(data);
1592
74051b9b
LP
1593 if (isempty(rvalue)) {
1594 /* Empty assignment clears list */
1595 path_free_specs(p);
1596 return 0;
1597 }
1598
93e4c84b
LP
1599 b = path_type_from_string(lvalue);
1600 if (b < 0) {
12ca818f 1601 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue);
c0b34696 1602 return 0;
01f78473
LP
1603 }
1604
19f6d710
LP
1605 r = unit_full_printf(UNIT(p), rvalue, &k);
1606 if (r < 0) {
12ca818f
LP
1607 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
1608 return 0;
487060c2 1609 }
93e4c84b
LP
1610
1611 if (!path_is_absolute(k)) {
12ca818f 1612 log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k);
c0b34696 1613 return 0;
01f78473
LP
1614 }
1615
93e4c84b 1616 s = new0(PathSpec, 1);
543295ad 1617 if (!s)
93e4c84b 1618 return log_oom();
01f78473 1619
718db961 1620 s->unit = UNIT(p);
93e4c84b 1621 s->path = path_kill_slashes(k);
543295ad 1622 k = NULL;
01f78473
LP
1623 s->type = b;
1624 s->inotify_fd = -1;
1625
71fda00f 1626 LIST_PREPEND(spec, p->specs, s);
01f78473
LP
1627
1628 return 0;
1629}
1630
b02cb41c
LP
1631int config_parse_socket_service(
1632 const char *unit,
1633 const char *filename,
1634 unsigned line,
1635 const char *section,
1636 unsigned section_line,
1637 const char *lvalue,
1638 int ltype,
1639 const char *rvalue,
1640 void *data,
1641 void *userdata) {
d9ff321a 1642
4afd3348 1643 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
8dd4c05b 1644 _cleanup_free_ char *p = NULL;
d9ff321a 1645 Socket *s = data;
4ff77f66 1646 Unit *x;
8dd4c05b 1647 int r;
d9ff321a
LP
1648
1649 assert(filename);
1650 assert(lvalue);
1651 assert(rvalue);
1652 assert(data);
1653
19f6d710 1654 r = unit_name_printf(UNIT(s), rvalue, &p);
613b411c 1655 if (r < 0) {
b02cb41c 1656 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
613b411c
LP
1657 return 0;
1658 }
74051b9b 1659
613b411c 1660 if (!endswith(p, ".service")) {
12ca818f 1661 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
d9ff321a
LP
1662 return 0;
1663 }
1664
613b411c 1665 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
4ff77f66 1666 if (r < 0) {
12ca818f 1667 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
d9ff321a
LP
1668 return 0;
1669 }
1670
4ff77f66
LP
1671 unit_ref_set(&s->service, x);
1672
d9ff321a
LP
1673 return 0;
1674}
1675
8dd4c05b
LP
1676int config_parse_fdname(
1677 const char *unit,
1678 const char *filename,
1679 unsigned line,
1680 const char *section,
1681 unsigned section_line,
1682 const char *lvalue,
1683 int ltype,
1684 const char *rvalue,
1685 void *data,
1686 void *userdata) {
1687
1688 _cleanup_free_ char *p = NULL;
1689 Socket *s = data;
1690 int r;
1691
1692 assert(filename);
1693 assert(lvalue);
1694 assert(rvalue);
1695 assert(data);
1696
1697 if (isempty(rvalue)) {
1698 s->fdname = mfree(s->fdname);
1699 return 0;
1700 }
1701
18913df9 1702 r = unit_full_printf(UNIT(s), rvalue, &p);
8dd4c05b
LP
1703 if (r < 0) {
1704 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
1705 return 0;
1706 }
1707
1708 if (!fdname_is_valid(p)) {
1709 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
1710 return 0;
1711 }
1712
3b319885 1713 return free_and_replace(s->fdname, p);
8dd4c05b
LP
1714}
1715
b02cb41c
LP
1716int config_parse_service_sockets(
1717 const char *unit,
1718 const char *filename,
1719 unsigned line,
1720 const char *section,
1721 unsigned section_line,
1722 const char *lvalue,
1723 int ltype,
1724 const char *rvalue,
1725 void *data,
1726 void *userdata) {
f976f3f6
LP
1727
1728 Service *s = data;
7b2313f5 1729 const char *p;
b02cb41c 1730 int r;
f976f3f6
LP
1731
1732 assert(filename);
1733 assert(lvalue);
1734 assert(rvalue);
1735 assert(data);
1736
7b2313f5 1737 p = rvalue;
9ed794a3 1738 for (;;) {
6a0f3175 1739 _cleanup_free_ char *word = NULL, *k = NULL;
f976f3f6 1740
7b2313f5
SS
1741 r = extract_first_word(&p, &word, NULL, 0);
1742 if (r == 0)
1743 break;
1744 if (r == -ENOMEM)
74051b9b 1745 return log_oom();
7b2313f5
SS
1746 if (r < 0) {
1747 log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
1748 break;
1749 }
f976f3f6 1750
7b2313f5 1751 r = unit_name_printf(UNIT(s), word, &k);
b02cb41c
LP
1752 if (r < 0) {
1753 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
1754 continue;
1755 }
57020a3a 1756
b02cb41c 1757 if (!endswith(k, ".socket")) {
12ca818f 1758 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
f976f3f6
LP
1759 continue;
1760 }
1761
b02cb41c 1762 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
57020a3a 1763 if (r < 0)
b02cb41c 1764 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
f976f3f6 1765
b02cb41c 1766 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
57020a3a 1767 if (r < 0)
b02cb41c 1768 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
f976f3f6
LP
1769 }
1770
1771 return 0;
1772}
1773
b02cb41c
LP
1774int config_parse_bus_name(
1775 const char *unit,
1776 const char *filename,
1777 unsigned line,
1778 const char *section,
1779 unsigned section_line,
1780 const char *lvalue,
1781 int ltype,
1782 const char *rvalue,
1783 void *data,
1784 void *userdata) {
1785
1786 _cleanup_free_ char *k = NULL;
1787 Unit *u = userdata;
1788 int r;
1789
1790 assert(filename);
1791 assert(lvalue);
1792 assert(rvalue);
1793 assert(u);
1794
1795 r = unit_full_printf(u, rvalue, &k);
1796 if (r < 0) {
1797 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
1798 return 0;
1799 }
1800
1801 if (!service_name_is_valid(k)) {
12ca818f 1802 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k);
b02cb41c
LP
1803 return 0;
1804 }
1805
1806 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
1807}
1808
aad41f08
LP
1809int config_parse_service_timeout(
1810 const char *unit,
1811 const char *filename,
1812 unsigned line,
1813 const char *section,
1814 unsigned section_line,
1815 const char *lvalue,
1816 int ltype,
1817 const char *rvalue,
1818 void *data,
1819 void *userdata) {
98709151
LN
1820
1821 Service *s = userdata;
aad41f08 1822 usec_t usec;
98709151
LN
1823 int r;
1824
1825 assert(filename);
1826 assert(lvalue);
1827 assert(rvalue);
1828 assert(s);
1829
aad41f08 1830 /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
98709151 1831
aad41f08
LP
1832 r = parse_sec(rvalue, &usec);
1833 if (r < 0) {
1834 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
1835 return 0;
1836 }
d568a335 1837
36c16a7c
LP
1838 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
1839 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
1840 * all other timeouts. */
aad41f08
LP
1841 if (usec <= 0)
1842 usec = USEC_INFINITY;
1843
1844 if (!streq(lvalue, "TimeoutStopSec")) {
1845 s->start_timeout_defined = true;
1846 s->timeout_start_usec = usec;
1847 }
36c16a7c 1848
aad41f08
LP
1849 if (!streq(lvalue, "TimeoutStartSec"))
1850 s->timeout_stop_usec = usec;
36c16a7c 1851
d568a335 1852 return 0;
98709151
LN
1853}
1854
89beff89
LP
1855int config_parse_sec_fix_0(
1856 const char *unit,
1857 const char *filename,
1858 unsigned line,
1859 const char *section,
1860 unsigned section_line,
1861 const char *lvalue,
1862 int ltype,
1863 const char *rvalue,
1864 void *data,
1865 void *userdata) {
1866
1867 usec_t *usec = data;
1868 int r;
1869
1870 assert(filename);
1871 assert(lvalue);
1872 assert(rvalue);
1873 assert(usec);
1874
1875 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
1876 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
1877 * timeout. */
1878
1879 r = parse_sec(rvalue, usec);
1880 if (r < 0) {
1881 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
1882 return 0;
1883 }
1884
1885 if (*usec <= 0)
1886 *usec = USEC_INFINITY;
1887
1888 return 0;
1889}
1890
66dccd8d
LP
1891int config_parse_user_group(
1892 const char *unit,
1893 const char *filename,
1894 unsigned line,
1895 const char *section,
1896 unsigned section_line,
1897 const char *lvalue,
1898 int ltype,
1899 const char *rvalue,
1900 void *data,
1901 void *userdata) {
1902
1903 char **user = data, *n;
1904 Unit *u = userdata;
1905 int r;
1906
1907 assert(filename);
1908 assert(lvalue);
1909 assert(rvalue);
1910 assert(u);
1911
1912 if (isempty(rvalue))
1913 n = NULL;
1914 else {
1915 _cleanup_free_ char *k = NULL;
1916
1917 r = unit_full_printf(u, rvalue, &k);
1918 if (r < 0) {
1919 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
1920 return 0;
1921 }
1922
1923 if (!valid_user_group_name_or_id(k)) {
1924 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k);
1925 return 0;
1926 }
1927
1928 n = k;
1929 k = NULL;
1930 }
1931
1932 free(*user);
1933 *user = n;
1934
1935 return 0;
1936}
1937
1938int config_parse_user_group_strv(
1939 const char *unit,
1940 const char *filename,
1941 unsigned line,
1942 const char *section,
1943 unsigned section_line,
1944 const char *lvalue,
1945 int ltype,
1946 const char *rvalue,
1947 void *data,
1948 void *userdata) {
1949
1950 char ***users = data;
1951 Unit *u = userdata;
1952 const char *p;
1953 int r;
1954
1955 assert(filename);
1956 assert(lvalue);
1957 assert(rvalue);
1958 assert(u);
1959
1960 if (isempty(rvalue)) {
1961 char **empty;
1962
1963 empty = new0(char*, 1);
1964 if (!empty)
1965 return log_oom();
1966
1967 strv_free(*users);
1968 *users = empty;
1969
1970 return 0;
1971 }
1972
1973 p = rvalue;
1974 for (;;) {
1975 _cleanup_free_ char *word = NULL, *k = NULL;
1976
9a82ab95 1977 r = extract_first_word(&p, &word, NULL, 0);
66dccd8d
LP
1978 if (r == 0)
1979 break;
1980 if (r == -ENOMEM)
1981 return log_oom();
1982 if (r < 0) {
1983 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
1984 break;
1985 }
1986
1987 r = unit_full_printf(u, word, &k);
1988 if (r < 0) {
1989 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word);
1990 continue;
1991 }
1992
1993 if (!valid_user_group_name_or_id(k)) {
1994 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k);
1995 continue;
1996 }
1997
1998 r = strv_push(users, k);
1999 if (r < 0)
2000 return log_oom();
2001
2002 k = NULL;
2003 }
2004
2005 return 0;
2006}
2007
e821075a
LP
2008int config_parse_busname_service(
2009 const char *unit,
2010 const char *filename,
2011 unsigned line,
2012 const char *section,
2013 unsigned section_line,
2014 const char *lvalue,
2015 int ltype,
2016 const char *rvalue,
2017 void *data,
2018 void *userdata) {
2019
4afd3348 2020 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
e821075a
LP
2021 BusName *n = data;
2022 int r;
2023 Unit *x;
2024 _cleanup_free_ char *p = NULL;
2025
2026 assert(filename);
2027 assert(lvalue);
2028 assert(rvalue);
2029 assert(data);
2030
2031 r = unit_name_printf(UNIT(n), rvalue, &p);
2032 if (r < 0) {
12ca818f 2033 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
e821075a
LP
2034 return 0;
2035 }
2036
2037 if (!endswith(p, ".service")) {
12ca818f 2038 log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
e821075a
LP
2039 return 0;
2040 }
2041
2042 r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
2043 if (r < 0) {
12ca818f 2044 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
e821075a
LP
2045 return 0;
2046 }
2047
2048 unit_ref_set(&n->service, x);
2049
2050 return 0;
2051}
2052
5369c77d 2053DEFINE_CONFIG_PARSE_ENUM(config_parse_bus_policy_world, bus_policy_access, BusPolicyAccess, "Failed to parse bus name policy access");
a4152e3f 2054
54d76c92
DM
2055int config_parse_bus_policy(
2056 const char *unit,
2057 const char *filename,
2058 unsigned line,
2059 const char *section,
2060 unsigned section_line,
2061 const char *lvalue,
2062 int ltype,
2063 const char *rvalue,
2064 void *data,
2065 void *userdata) {
2066
2067 _cleanup_free_ BusNamePolicy *p = NULL;
2068 _cleanup_free_ char *id_str = NULL;
2069 BusName *busname = data;
2070 char *access_str;
54d76c92
DM
2071
2072 assert(filename);
2073 assert(lvalue);
2074 assert(rvalue);
2075 assert(data);
2076
2077 p = new0(BusNamePolicy, 1);
2078 if (!p)
2079 return log_oom();
2080
2081 if (streq(lvalue, "AllowUser"))
2082 p->type = BUSNAME_POLICY_TYPE_USER;
2083 else if (streq(lvalue, "AllowGroup"))
2084 p->type = BUSNAME_POLICY_TYPE_GROUP;
54d76c92
DM
2085 else
2086 assert_not_reached("Unknown lvalue");
2087
2088 id_str = strdup(rvalue);
2089 if (!id_str)
2090 return log_oom();
2091
a4152e3f
LP
2092 access_str = strpbrk(id_str, WHITESPACE);
2093 if (!access_str) {
12ca818f 2094 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy value '%s'", rvalue);
a4152e3f 2095 return 0;
54d76c92
DM
2096 }
2097
a4152e3f
LP
2098 *access_str = '\0';
2099 access_str++;
2100 access_str += strspn(access_str, WHITESPACE);
2101
5369c77d 2102 p->access = bus_policy_access_from_string(access_str);
54d76c92 2103 if (p->access < 0) {
12ca818f 2104 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy access type '%s'", access_str);
54d76c92
DM
2105 return 0;
2106 }
2107
a4152e3f
LP
2108 p->name = id_str;
2109 id_str = NULL;
2110
54d76c92
DM
2111 LIST_PREPEND(policy, busname->policy, p);
2112 p = NULL;
2113
2114 return 0;
2115}
2116
5f5d8eab
LP
2117int config_parse_working_directory(
2118 const char *unit,
2119 const char *filename,
2120 unsigned line,
2121 const char *section,
2122 unsigned section_line,
2123 const char *lvalue,
2124 int ltype,
2125 const char *rvalue,
2126 void *data,
2127 void *userdata) {
2128
2129 ExecContext *c = data;
2130 Unit *u = userdata;
2131 bool missing_ok;
2132 int r;
2133
2134 assert(filename);
2135 assert(lvalue);
2136 assert(rvalue);
2137 assert(c);
2138 assert(u);
2139
2140 if (rvalue[0] == '-') {
2141 missing_ok = true;
2142 rvalue++;
2143 } else
2144 missing_ok = false;
2145
2146 if (streq(rvalue, "~")) {
2147 c->working_directory_home = true;
2148 c->working_directory = mfree(c->working_directory);
2149 } else {
2150 _cleanup_free_ char *k = NULL;
2151
2152 r = unit_full_printf(u, rvalue, &k);
2153 if (r < 0) {
2154 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue);
2155 return 0;
2156 }
2157
2158 path_kill_slashes(k);
2159
2160 if (!utf8_is_valid(k)) {
0e05ee04 2161 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
5f5d8eab
LP
2162 return 0;
2163 }
2164
2165 if (!path_is_absolute(k)) {
2166 log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue);
2167 return 0;
2168 }
2169
3b319885 2170 free_and_replace(c->working_directory, k);
5f5d8eab
LP
2171
2172 c->working_directory_home = false;
2173 }
2174
2175 c->working_directory_missing_ok = missing_ok;
2176 return 0;
2177}
2178
e8e581bf
ZJS
2179int config_parse_unit_env_file(const char *unit,
2180 const char *filename,
2181 unsigned line,
2182 const char *section,
71a61510 2183 unsigned section_line,
e8e581bf
ZJS
2184 const char *lvalue,
2185 int ltype,
2186 const char *rvalue,
2187 void *data,
2188 void *userdata) {
ddb26e18 2189
853b8397 2190 char ***env = data;
8fef7659 2191 Unit *u = userdata;
19f6d710 2192 _cleanup_free_ char *n = NULL;
853b8397 2193 int r;
ddb26e18
LP
2194
2195 assert(filename);
2196 assert(lvalue);
2197 assert(rvalue);
2198 assert(data);
2199
74051b9b
LP
2200 if (isempty(rvalue)) {
2201 /* Empty assignment frees the list */
6796073e 2202 *env = strv_free(*env);
74051b9b
LP
2203 return 0;
2204 }
2205
19f6d710 2206 r = unit_full_printf(u, rvalue, &n);
12ca818f
LP
2207 if (r < 0) {
2208 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
2209 return 0;
2210 }
8fef7659 2211
12ca818f
LP
2212 if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) {
2213 log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n);
afe4bfe2
LP
2214 return 0;
2215 }
2216
12ca818f 2217 r = strv_extend(env, n);
853b8397
LP
2218 if (r < 0)
2219 return log_oom();
2220
2221 return 0;
2222}
2223
e8e581bf
ZJS
2224int config_parse_environ(const char *unit,
2225 const char *filename,
2226 unsigned line,
2227 const char *section,
71a61510 2228 unsigned section_line,
e8e581bf
ZJS
2229 const char *lvalue,
2230 int ltype,
2231 const char *rvalue,
2232 void *data,
2233 void *userdata) {
853b8397
LP
2234
2235 Unit *u = userdata;
035fe294
ZJS
2236 char ***env = data;
2237 const char *p;
19f6d710 2238 int r;
853b8397
LP
2239
2240 assert(filename);
2241 assert(lvalue);
2242 assert(rvalue);
97d0e5f8 2243 assert(data);
853b8397
LP
2244
2245 if (isempty(rvalue)) {
2246 /* Empty assignment resets the list */
6796073e 2247 *env = strv_free(*env);
853b8397
LP
2248 return 0;
2249 }
2250
035fe294
ZJS
2251 for (p = rvalue;; ) {
2252 _cleanup_free_ char *word = NULL, *k = NULL;
035fe294
ZJS
2253
2254 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
2255 if (r == 0)
2256 return 0;
2257 if (r == -ENOMEM)
2258 return log_oom();
12ca818f 2259 if (r < 0) {
035fe294
ZJS
2260 log_syntax(unit, LOG_WARNING, filename, line, r,
2261 "Invalid syntax, ignoring: %s", rvalue);
12ca818f
LP
2262 return 0;
2263 }
97d0e5f8 2264
035fe294
ZJS
2265 if (u) {
2266 r = unit_full_printf(u, word, &k);
2267 if (r < 0) {
2268 log_syntax(unit, LOG_ERR, filename, line, r,
2269 "Failed to resolve specifiers, ignoring: %s", k);
2270 continue;
2271 }
2272 } else {
2273 k = word;
2274 word = NULL;
527b7a42 2275 }
853b8397 2276
035fe294
ZJS
2277 if (!env_assignment_is_valid(k)) {
2278 log_syntax(unit, LOG_ERR, filename, line, 0,
2279 "Invalid environment assignment, ignoring: %s", k);
853b8397
LP
2280 continue;
2281 }
2282
54ac3494
ZJS
2283 r = strv_env_replace(env, k);
2284 if (r < 0)
853b8397 2285 return log_oom();
54ac3494 2286 k = NULL;
853b8397 2287 }
ddb26e18
LP
2288}
2289
b4c14404
FB
2290int config_parse_pass_environ(const char *unit,
2291 const char *filename,
2292 unsigned line,
2293 const char *section,
2294 unsigned section_line,
2295 const char *lvalue,
2296 int ltype,
2297 const char *rvalue,
2298 void *data,
2299 void *userdata) {
2300
2301 const char *whole_rvalue = rvalue;
2302 char*** passenv = data;
2303 _cleanup_strv_free_ char **n = NULL;
2304 size_t nlen = 0, nbufsize = 0;
2305 int r;
2306
2307 assert(filename);
2308 assert(lvalue);
2309 assert(rvalue);
2310 assert(data);
2311
2312 if (isempty(rvalue)) {
2313 /* Empty assignment resets the list */
2314 *passenv = strv_free(*passenv);
2315 return 0;
2316 }
2317
2318 for (;;) {
2319 _cleanup_free_ char *word = NULL;
2320
9a82ab95 2321 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
b4c14404
FB
2322 if (r == 0)
2323 break;
2324 if (r == -ENOMEM)
2325 return log_oom();
2326 if (r < 0) {
2327 log_syntax(unit, LOG_ERR, filename, line, r,
2328 "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
2329 break;
2330 }
2331
2332 if (!env_name_is_valid(word)) {
2333 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
2334 "Invalid environment name for %s, ignoring: %s", lvalue, word);
2335 continue;
2336 }
2337
2338 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2339 return log_oom();
2340 n[nlen++] = word;
2341 n[nlen] = NULL;
2342 word = NULL;
2343 }
2344
2345 if (n) {
2346 r = strv_extend_strv(passenv, n, true);
2347 if (r < 0)
2348 return r;
2349 }
2350
2351 return 0;
2352}
2353
e8e581bf
ZJS
2354int config_parse_ip_tos(const char *unit,
2355 const char *filename,
2356 unsigned line,
2357 const char *section,
71a61510 2358 unsigned section_line,
e8e581bf
ZJS
2359 const char *lvalue,
2360 int ltype,
2361 const char *rvalue,
2362 void *data,
2363 void *userdata) {
4fd5948e
LP
2364
2365 int *ip_tos = data, x;
4fd5948e
LP
2366
2367 assert(filename);
2368 assert(lvalue);
2369 assert(rvalue);
2370 assert(data);
2371
f8b69d1d
MS
2372 x = ip_tos_from_string(rvalue);
2373 if (x < 0) {
12ca818f 2374 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue);
f8b69d1d
MS
2375 return 0;
2376 }
4fd5948e
LP
2377
2378 *ip_tos = x;
2379 return 0;
2380}
2381
59fccdc5
LP
2382int config_parse_unit_condition_path(
2383 const char *unit,
2384 const char *filename,
2385 unsigned line,
2386 const char *section,
2387 unsigned section_line,
2388 const char *lvalue,
2389 int ltype,
2390 const char *rvalue,
2391 void *data,
2392 void *userdata) {
52661efd 2393
2fbe635a 2394 _cleanup_free_ char *p = NULL;
59fccdc5
LP
2395 Condition **list = data, *c;
2396 ConditionType t = ltype;
2397 bool trigger, negate;
2398 Unit *u = userdata;
19f6d710 2399 int r;
52661efd
LP
2400
2401 assert(filename);
2402 assert(lvalue);
2403 assert(rvalue);
2404 assert(data);
2405
74051b9b
LP
2406 if (isempty(rvalue)) {
2407 /* Empty assignment resets the list */
447021aa 2408 *list = condition_free_list(*list);
74051b9b
LP
2409 return 0;
2410 }
2411
ab7f148f
LP
2412 trigger = rvalue[0] == '|';
2413 if (trigger)
267632f0
LP
2414 rvalue++;
2415
ab7f148f
LP
2416 negate = rvalue[0] == '!';
2417 if (negate)
52661efd
LP
2418 rvalue++;
2419
19f6d710 2420 r = unit_full_printf(u, rvalue, &p);
59fccdc5 2421 if (r < 0) {
12ca818f 2422 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
59fccdc5 2423 return 0;
19f6d710 2424 }
095b2d7a
AK
2425
2426 if (!path_is_absolute(p)) {
12ca818f 2427 log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p);
52661efd
LP
2428 return 0;
2429 }
2430
59fccdc5 2431 c = condition_new(t, p, trigger, negate);
ab7f148f 2432 if (!c)
74051b9b 2433 return log_oom();
52661efd 2434
59fccdc5 2435 LIST_PREPEND(conditions, *list, c);
52661efd
LP
2436 return 0;
2437}
2438
59fccdc5
LP
2439int config_parse_unit_condition_string(
2440 const char *unit,
2441 const char *filename,
2442 unsigned line,
2443 const char *section,
2444 unsigned section_line,
2445 const char *lvalue,
2446 int ltype,
2447 const char *rvalue,
2448 void *data,
2449 void *userdata) {
039655a4 2450
2fbe635a 2451 _cleanup_free_ char *s = NULL;
59fccdc5
LP
2452 Condition **list = data, *c;
2453 ConditionType t = ltype;
2454 bool trigger, negate;
2455 Unit *u = userdata;
19f6d710 2456 int r;
039655a4
LP
2457
2458 assert(filename);
2459 assert(lvalue);
2460 assert(rvalue);
2461 assert(data);
2462
74051b9b
LP
2463 if (isempty(rvalue)) {
2464 /* Empty assignment resets the list */
447021aa 2465 *list = condition_free_list(*list);
74051b9b
LP
2466 return 0;
2467 }
2468
c0d6e764
LP
2469 trigger = rvalue[0] == '|';
2470 if (trigger)
267632f0
LP
2471 rvalue++;
2472
c0d6e764
LP
2473 negate = rvalue[0] == '!';
2474 if (negate)
039655a4
LP
2475 rvalue++;
2476
19f6d710 2477 r = unit_full_printf(u, rvalue, &s);
59fccdc5 2478 if (r < 0) {
12ca818f 2479 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
59fccdc5 2480 return 0;
19f6d710 2481 }
095b2d7a 2482
59fccdc5 2483 c = condition_new(t, s, trigger, negate);
c0d6e764
LP
2484 if (!c)
2485 return log_oom();
039655a4 2486
59fccdc5 2487 LIST_PREPEND(conditions, *list, c);
039655a4
LP
2488 return 0;
2489}
2490
59fccdc5
LP
2491int config_parse_unit_condition_null(
2492 const char *unit,
2493 const char *filename,
2494 unsigned line,
2495 const char *section,
2496 unsigned section_line,
2497 const char *lvalue,
2498 int ltype,
2499 const char *rvalue,
2500 void *data,
2501 void *userdata) {
d257ddef 2502
59fccdc5 2503 Condition **list = data, *c;
267632f0 2504 bool trigger, negate;
d257ddef
LP
2505 int b;
2506
2507 assert(filename);
2508 assert(lvalue);
2509 assert(rvalue);
2510 assert(data);
2511
74051b9b
LP
2512 if (isempty(rvalue)) {
2513 /* Empty assignment resets the list */
447021aa 2514 *list = condition_free_list(*list);
74051b9b
LP
2515 return 0;
2516 }
2517
2518 trigger = rvalue[0] == '|';
2519 if (trigger)
267632f0
LP
2520 rvalue++;
2521
74051b9b
LP
2522 negate = rvalue[0] == '!';
2523 if (negate)
d257ddef
LP
2524 rvalue++;
2525
74051b9b
LP
2526 b = parse_boolean(rvalue);
2527 if (b < 0) {
12ca818f 2528 log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
d257ddef
LP
2529 return 0;
2530 }
2531
2532 if (!b)
2533 negate = !negate;
2534
74051b9b
LP
2535 c = condition_new(CONDITION_NULL, NULL, trigger, negate);
2536 if (!c)
2537 return log_oom();
d257ddef 2538
59fccdc5 2539 LIST_PREPEND(conditions, *list, c);
d257ddef
LP
2540 return 0;
2541}
2542
f975e971 2543DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
87a47f99 2544DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier");
c952c6ec 2545
a57f7e2c
LP
2546int config_parse_unit_requires_mounts_for(
2547 const char *unit,
2548 const char *filename,
2549 unsigned line,
2550 const char *section,
71a61510 2551 unsigned section_line,
a57f7e2c
LP
2552 const char *lvalue,
2553 int ltype,
2554 const char *rvalue,
2555 void *data,
2556 void *userdata) {
7c8fa05c
LP
2557
2558 Unit *u = userdata;
035fe294
ZJS
2559 const char *p;
2560 int r;
7c8fa05c
LP
2561
2562 assert(filename);
2563 assert(lvalue);
2564 assert(rvalue);
2565 assert(data);
2566
035fe294 2567 for (p = rvalue;; ) {
744bb5b1 2568 _cleanup_free_ char *word = NULL, *resolved = NULL;
a57f7e2c 2569
035fe294
ZJS
2570 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2571 if (r == 0)
2572 return 0;
2573 if (r == -ENOMEM)
a57f7e2c 2574 return log_oom();
035fe294
ZJS
2575 if (r < 0) {
2576 log_syntax(unit, LOG_WARNING, filename, line, r,
2577 "Invalid syntax, ignoring: %s", rvalue);
2578 return 0;
2579 }
7c8fa05c 2580
035fe294 2581 if (!utf8_is_valid(word)) {
0e05ee04 2582 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
a57f7e2c
LP
2583 continue;
2584 }
7c8fa05c 2585
744bb5b1
LP
2586 r = unit_full_printf(u, word, &resolved);
2587 if (r < 0) {
2588 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit name \"%s\", ignoring: %m", word);
2589 continue;
2590 }
2591
2592 r = unit_require_mounts_for(u, resolved);
a57f7e2c 2593 if (r < 0) {
744bb5b1 2594 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount \"%s\", ignoring: %m", resolved);
a57f7e2c
LP
2595 continue;
2596 }
2597 }
7c8fa05c 2598}
9e372868 2599
e8e581bf
ZJS
2600int config_parse_documentation(const char *unit,
2601 const char *filename,
2602 unsigned line,
2603 const char *section,
71a61510 2604 unsigned section_line,
e8e581bf
ZJS
2605 const char *lvalue,
2606 int ltype,
2607 const char *rvalue,
2608 void *data,
2609 void *userdata) {
49dbfa7b
LP
2610
2611 Unit *u = userdata;
2612 int r;
2613 char **a, **b;
2614
2615 assert(filename);
2616 assert(lvalue);
2617 assert(rvalue);
2618 assert(u);
2619
74051b9b
LP
2620 if (isempty(rvalue)) {
2621 /* Empty assignment resets the list */
6796073e 2622 u->documentation = strv_free(u->documentation);
74051b9b
LP
2623 return 0;
2624 }
2625
71a61510 2626 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 2627 rvalue, data, userdata);
49dbfa7b
LP
2628 if (r < 0)
2629 return r;
2630
2631 for (a = b = u->documentation; a && *a; a++) {
2632
a2e03378 2633 if (documentation_url_is_valid(*a))
49dbfa7b
LP
2634 *(b++) = *a;
2635 else {
12ca818f 2636 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a);
49dbfa7b
LP
2637 free(*a);
2638 }
2639 }
f6d2d421
ZJS
2640 if (b)
2641 *b = NULL;
49dbfa7b
LP
2642
2643 return r;
2644}
2645
c0467cf3 2646#ifdef HAVE_SECCOMP
8130926d 2647
201c1cc2
TM
2648static int syscall_filter_parse_one(
2649 const char *unit,
2650 const char *filename,
2651 unsigned line,
2652 ExecContext *c,
2653 bool invert,
2654 const char *t,
2655 bool warn) {
2656 int r;
2657
8130926d
LP
2658 if (t[0] == '@') {
2659 const SyscallFilterSet *set;
2660 const char *i;
201c1cc2 2661
8130926d
LP
2662 set = syscall_filter_set_find(t);
2663 if (!set) {
2664 if (warn)
2665 log_syntax(unit, LOG_WARNING, filename, line, 0, "Don't know system call group, ignoring: %s", t);
2666 return 0;
2667 }
201c1cc2 2668
8130926d
LP
2669 NULSTR_FOREACH(i, set->value) {
2670 r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false);
2671 if (r < 0)
2672 return r;
2673 }
201c1cc2
TM
2674 } else {
2675 int id;
2676
2677 id = seccomp_syscall_resolve_name(t);
391b81cd 2678 if (id == __NR_SCMP_ERROR) {
201c1cc2 2679 if (warn)
8130926d 2680 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
201c1cc2
TM
2681 return 0;
2682 }
2683
2684 /* If we previously wanted to forbid a syscall and now
2685 * we want to allow it, then remove it from the list
2686 */
2687 if (!invert == c->syscall_whitelist) {
2688 r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
2689 if (r == 0)
2690 return 0;
2691 if (r < 0)
2692 return log_oom();
2693 } else
8130926d 2694 (void) set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
201c1cc2 2695 }
8130926d 2696
201c1cc2
TM
2697 return 0;
2698}
2699
17df7223
LP
2700int config_parse_syscall_filter(
2701 const char *unit,
2702 const char *filename,
2703 unsigned line,
2704 const char *section,
2705 unsigned section_line,
2706 const char *lvalue,
2707 int ltype,
2708 const char *rvalue,
2709 void *data,
2710 void *userdata) {
2711
8351ceae
LP
2712 ExecContext *c = data;
2713 Unit *u = userdata;
b5fb3789 2714 bool invert = false;
8130926d 2715 const char *p;
17df7223 2716 int r;
8351ceae
LP
2717
2718 assert(filename);
2719 assert(lvalue);
2720 assert(rvalue);
2721 assert(u);
2722
74051b9b
LP
2723 if (isempty(rvalue)) {
2724 /* Empty assignment resets the list */
525d3cc7 2725 c->syscall_filter = set_free(c->syscall_filter);
17df7223 2726 c->syscall_whitelist = false;
74051b9b
LP
2727 return 0;
2728 }
2729
8351ceae
LP
2730 if (rvalue[0] == '~') {
2731 invert = true;
2732 rvalue++;
2733 }
2734
17df7223 2735 if (!c->syscall_filter) {
d5099efc 2736 c->syscall_filter = set_new(NULL);
17df7223
LP
2737 if (!c->syscall_filter)
2738 return log_oom();
2739
c0467cf3 2740 if (invert)
17df7223
LP
2741 /* Allow everything but the ones listed */
2742 c->syscall_whitelist = false;
c0467cf3 2743 else {
17df7223
LP
2744 /* Allow nothing but the ones listed */
2745 c->syscall_whitelist = true;
8351ceae 2746
17df7223 2747 /* Accept default syscalls if we are on a whitelist */
201c1cc2
TM
2748 r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false);
2749 if (r < 0)
2750 return r;
c0467cf3 2751 }
8351ceae
LP
2752 }
2753
8130926d
LP
2754 p = rvalue;
2755 for (;;) {
2756 _cleanup_free_ char *word = NULL;
8351ceae 2757
8130926d
LP
2758 r = extract_first_word(&p, &word, NULL, 0);
2759 if (r == 0)
2760 break;
2761 if (r == -ENOMEM)
74051b9b 2762 return log_oom();
8130926d
LP
2763 if (r < 0) {
2764 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
2765 break;
2766 }
8351ceae 2767
8130926d 2768 r = syscall_filter_parse_one(unit, filename, line, c, invert, word, true);
201c1cc2
TM
2769 if (r < 0)
2770 return r;
c0467cf3
RC
2771 }
2772
17df7223
LP
2773 return 0;
2774}
2775
57183d11
LP
2776int config_parse_syscall_archs(
2777 const char *unit,
2778 const char *filename,
2779 unsigned line,
2780 const char *section,
2781 unsigned section_line,
2782 const char *lvalue,
2783 int ltype,
2784 const char *rvalue,
2785 void *data,
2786 void *userdata) {
2787
d3b1c508 2788 Set **archs = data;
035fe294 2789 const char *p;
57183d11
LP
2790 int r;
2791
2792 if (isempty(rvalue)) {
525d3cc7 2793 *archs = set_free(*archs);
57183d11
LP
2794 return 0;
2795 }
2796
d5099efc 2797 r = set_ensure_allocated(archs, NULL);
57183d11
LP
2798 if (r < 0)
2799 return log_oom();
2800
035fe294
ZJS
2801 for (p = rvalue;;) {
2802 _cleanup_free_ char *word = NULL;
57183d11
LP
2803 uint32_t a;
2804
035fe294
ZJS
2805 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2806 if (r == 0)
2807 return 0;
2808 if (r == -ENOMEM)
57183d11 2809 return log_oom();
035fe294
ZJS
2810 if (r < 0) {
2811 log_syntax(unit, LOG_WARNING, filename, line, r,
2812 "Invalid syntax, ignoring: %s", rvalue);
2813 return 0;
2814 }
57183d11 2815
035fe294 2816 r = seccomp_arch_from_string(word, &a);
57183d11 2817 if (r < 0) {
035fe294
ZJS
2818 log_syntax(unit, LOG_ERR, filename, line, r,
2819 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
57183d11
LP
2820 continue;
2821 }
2822
d3b1c508 2823 r = set_put(*archs, UINT32_TO_PTR(a + 1));
57183d11
LP
2824 if (r < 0)
2825 return log_oom();
2826 }
57183d11
LP
2827}
2828
17df7223
LP
2829int config_parse_syscall_errno(
2830 const char *unit,
2831 const char *filename,
2832 unsigned line,
2833 const char *section,
2834 unsigned section_line,
2835 const char *lvalue,
2836 int ltype,
2837 const char *rvalue,
2838 void *data,
2839 void *userdata) {
2840
2841 ExecContext *c = data;
2842 int e;
2843
2844 assert(filename);
2845 assert(lvalue);
2846 assert(rvalue);
2847
2848 if (isempty(rvalue)) {
2849 /* Empty assignment resets to KILL */
2850 c->syscall_errno = 0;
2851 return 0;
8351ceae
LP
2852 }
2853
17df7223
LP
2854 e = errno_from_name(rvalue);
2855 if (e < 0) {
12ca818f 2856 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
17df7223
LP
2857 return 0;
2858 }
8351ceae 2859
17df7223 2860 c->syscall_errno = e;
8351ceae
LP
2861 return 0;
2862}
4298d0b5
LP
2863
2864int config_parse_address_families(
2865 const char *unit,
2866 const char *filename,
2867 unsigned line,
2868 const char *section,
2869 unsigned section_line,
2870 const char *lvalue,
2871 int ltype,
2872 const char *rvalue,
2873 void *data,
2874 void *userdata) {
2875
2876 ExecContext *c = data;
4298d0b5 2877 bool invert = false;
035fe294 2878 const char *p;
4298d0b5
LP
2879 int r;
2880
2881 assert(filename);
2882 assert(lvalue);
2883 assert(rvalue);
4298d0b5
LP
2884
2885 if (isempty(rvalue)) {
2886 /* Empty assignment resets the list */
525d3cc7 2887 c->address_families = set_free(c->address_families);
4298d0b5
LP
2888 c->address_families_whitelist = false;
2889 return 0;
2890 }
2891
2892 if (rvalue[0] == '~') {
2893 invert = true;
2894 rvalue++;
2895 }
2896
2897 if (!c->address_families) {
d5099efc 2898 c->address_families = set_new(NULL);
4298d0b5
LP
2899 if (!c->address_families)
2900 return log_oom();
2901
2902 c->address_families_whitelist = !invert;
2903 }
2904
035fe294
ZJS
2905 for (p = rvalue;;) {
2906 _cleanup_free_ char *word = NULL;
4298d0b5
LP
2907 int af;
2908
035fe294
ZJS
2909 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
2910 if (r == 0)
2911 return 0;
2912 if (r == -ENOMEM)
4298d0b5 2913 return log_oom();
035fe294
ZJS
2914 if (r < 0) {
2915 log_syntax(unit, LOG_WARNING, filename, line, r,
2916 "Invalid syntax, ignoring: %s", rvalue);
2917 return 0;
2918 }
4298d0b5 2919
035fe294 2920 af = af_from_name(word);
4298d0b5 2921 if (af <= 0) {
035fe294
ZJS
2922 log_syntax(unit, LOG_ERR, filename, line, 0,
2923 "Failed to parse address family \"%s\", ignoring: %m", word);
4298d0b5
LP
2924 continue;
2925 }
2926
2927 /* If we previously wanted to forbid an address family and now
035fe294 2928 * we want to allow it, then just remove it from the list.
4298d0b5
LP
2929 */
2930 if (!invert == c->address_families_whitelist) {
2931 r = set_put(c->address_families, INT_TO_PTR(af));
4298d0b5
LP
2932 if (r < 0)
2933 return log_oom();
2934 } else
2935 set_remove(c->address_families, INT_TO_PTR(af));
2936 }
4298d0b5 2937}
add00535
LP
2938
2939int config_parse_restrict_namespaces(
2940 const char *unit,
2941 const char *filename,
2942 unsigned line,
2943 const char *section,
2944 unsigned section_line,
2945 const char *lvalue,
2946 int ltype,
2947 const char *rvalue,
2948 void *data,
2949 void *userdata) {
2950
2951 ExecContext *c = data;
2952 bool invert = false;
2953 int r;
2954
2955 if (isempty(rvalue)) {
2956 /* Reset to the default. */
2957 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
2958 return 0;
2959 }
2960
2961 if (rvalue[0] == '~') {
2962 invert = true;
2963 rvalue++;
2964 }
2965
2966 r = parse_boolean(rvalue);
2967 if (r > 0)
2968 c->restrict_namespaces = 0;
2969 else if (r == 0)
2970 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
2971 else {
2972 /* Not a boolean argument, in this case it's a list of namespace types. */
2973
2974 r = namespace_flag_from_string_many(rvalue, &c->restrict_namespaces);
2975 if (r < 0) {
2976 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
2977 return 0;
2978 }
2979 }
2980
2981 if (invert)
2982 c->restrict_namespaces = (~c->restrict_namespaces) & NAMESPACE_FLAGS_ALL;
2983
2984 return 0;
2985}
c0467cf3 2986#endif
8351ceae 2987
a016b922
LP
2988int config_parse_unit_slice(
2989 const char *unit,
2990 const char *filename,
2991 unsigned line,
2992 const char *section,
71a61510 2993 unsigned section_line,
a016b922
LP
2994 const char *lvalue,
2995 int ltype,
2996 const char *rvalue,
2997 void *data,
2998 void *userdata) {
2999
3000 _cleanup_free_ char *k = NULL;
d79200e2 3001 Unit *u = userdata, *slice = NULL;
a016b922
LP
3002 int r;
3003
3004 assert(filename);
3005 assert(lvalue);
3006 assert(rvalue);
3007 assert(u);
3008
19f6d710 3009 r = unit_name_printf(u, rvalue, &k);
d79200e2
LP
3010 if (r < 0) {
3011 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
3012 return 0;
19f6d710 3013 }
a016b922 3014
19f6d710 3015 r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
a016b922 3016 if (r < 0) {
d79200e2 3017 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k);
a016b922
LP
3018 return 0;
3019 }
3020
d79200e2
LP
3021 r = unit_set_slice(u, slice);
3022 if (r < 0) {
3023 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id);
a016b922
LP
3024 return 0;
3025 }
3026
a016b922
LP
3027 return 0;
3028}
3029
4ad49000
LP
3030DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
3031
66ebf6c0
TH
3032int config_parse_cpu_weight(
3033 const char *unit,
3034 const char *filename,
3035 unsigned line,
3036 const char *section,
3037 unsigned section_line,
3038 const char *lvalue,
3039 int ltype,
3040 const char *rvalue,
3041 void *data,
3042 void *userdata) {
3043
3044 uint64_t *weight = data;
3045 int r;
3046
3047 assert(filename);
3048 assert(lvalue);
3049 assert(rvalue);
3050
3051 r = cg_weight_parse(rvalue, weight);
3052 if (r < 0) {
3053 log_syntax(unit, LOG_ERR, filename, line, r, "CPU weight '%s' invalid. Ignoring.", rvalue);
3054 return 0;
3055 }
3056
3057 return 0;
3058}
3059
4ad49000
LP
3060int config_parse_cpu_shares(
3061 const char *unit,
3062 const char *filename,
3063 unsigned line,
3064 const char *section,
71a61510 3065 unsigned section_line,
4ad49000
LP
3066 const char *lvalue,
3067 int ltype,
3068 const char *rvalue,
3069 void *data,
3070 void *userdata) {
3071
d53d9474 3072 uint64_t *shares = data;
95ae05c0
WC
3073 int r;
3074
3075 assert(filename);
3076 assert(lvalue);
3077 assert(rvalue);
3078
d53d9474
LP
3079 r = cg_cpu_shares_parse(rvalue, shares);
3080 if (r < 0) {
3081 log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
3082 return 0;
3083 }
3084
4ad49000
LP
3085 return 0;
3086}
3087
b2f8b02e
LP
3088int config_parse_cpu_quota(
3089 const char *unit,
3090 const char *filename,
3091 unsigned line,
3092 const char *section,
3093 unsigned section_line,
3094 const char *lvalue,
3095 int ltype,
3096 const char *rvalue,
3097 void *data,
3098 void *userdata) {
3099
3100 CGroupContext *c = data;
9184ca48 3101 int r;
b2f8b02e
LP
3102
3103 assert(filename);
3104 assert(lvalue);
3105 assert(rvalue);
3106
3107 if (isempty(rvalue)) {
3a43da28 3108 c->cpu_quota_per_sec_usec = USEC_INFINITY;
b2f8b02e
LP
3109 return 0;
3110 }
3111
5124866d 3112 r = parse_percent_unbounded(rvalue);
9184ca48
LP
3113 if (r <= 0) {
3114 log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
9a054909 3115 return 0;
b2f8b02e
LP
3116 }
3117
9184ca48 3118 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
b2f8b02e
LP
3119 return 0;
3120}
3121
4ad49000
LP
3122int config_parse_memory_limit(
3123 const char *unit,
3124 const char *filename,
3125 unsigned line,
3126 const char *section,
71a61510 3127 unsigned section_line,
4ad49000
LP
3128 const char *lvalue,
3129 int ltype,
3130 const char *rvalue,
3131 void *data,
3132 void *userdata) {
3133
3134 CGroupContext *c = data;
da4d897e 3135 uint64_t bytes = CGROUP_LIMIT_MAX;
4ad49000
LP
3136 int r;
3137
e57c9ce1 3138 if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
875ae566
LP
3139
3140 r = parse_percent(rvalue);
3141 if (r < 0) {
3142 r = parse_size(rvalue, 1024, &bytes);
3143 if (r < 0) {
3144 log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
3145 return 0;
3146 }
3147 } else
d8cf2ac7 3148 bytes = physical_memory_scale(r, 100U);
875ae566 3149
b3785cd5
LP
3150 if (bytes <= 0 || bytes >= UINT64_MAX) {
3151 log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range. Ignoring.", rvalue);
da4d897e
TH
3152 return 0;
3153 }
4ad49000
LP
3154 }
3155
da4d897e
TH
3156 if (streq(lvalue, "MemoryLow"))
3157 c->memory_low = bytes;
3158 else if (streq(lvalue, "MemoryHigh"))
3159 c->memory_high = bytes;
3160 else if (streq(lvalue, "MemoryMax"))
3161 c->memory_max = bytes;
96e131ea
WC
3162 else if (streq(lvalue, "MemorySwapMax"))
3163 c->memory_swap_max = bytes;
3164 else if (streq(lvalue, "MemoryLimit"))
da4d897e 3165 c->memory_limit = bytes;
96e131ea
WC
3166 else
3167 return -EINVAL;
4ad49000 3168
4ad49000
LP
3169 return 0;
3170}
3171
03a7b521
LP
3172int config_parse_tasks_max(
3173 const char *unit,
3174 const char *filename,
3175 unsigned line,
3176 const char *section,
3177 unsigned section_line,
3178 const char *lvalue,
3179 int ltype,
3180 const char *rvalue,
3181 void *data,
3182 void *userdata) {
3183
f5058264
TH
3184 uint64_t *tasks_max = data, v;
3185 Unit *u = userdata;
03a7b521
LP
3186 int r;
3187
f5058264
TH
3188 if (isempty(rvalue)) {
3189 *tasks_max = u->manager->default_tasks_max;
3190 return 0;
3191 }
3192
3193 if (streq(rvalue, "infinity")) {
3194 *tasks_max = CGROUP_LIMIT_MAX;
03a7b521
LP
3195 return 0;
3196 }
3197
83f8e808
LP
3198 r = parse_percent(rvalue);
3199 if (r < 0) {
f5058264 3200 r = safe_atou64(rvalue, &v);
83f8e808
LP
3201 if (r < 0) {
3202 log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
3203 return 0;
3204 }
3205 } else
f5058264 3206 v = system_tasks_max_scale(r, 100U);
83f8e808 3207
f5058264 3208 if (v <= 0 || v >= UINT64_MAX) {
83f8e808 3209 log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue);
03a7b521
LP
3210 return 0;
3211 }
3212
f5058264 3213 *tasks_max = v;
03a7b521
LP
3214 return 0;
3215}
3216
4ad49000
LP
3217int config_parse_device_allow(
3218 const char *unit,
3219 const char *filename,
3220 unsigned line,
3221 const char *section,
71a61510 3222 unsigned section_line,
4ad49000
LP
3223 const char *lvalue,
3224 int ltype,
3225 const char *rvalue,
3226 void *data,
3227 void *userdata) {
3228
1116e14c 3229 _cleanup_free_ char *path = NULL, *t = NULL;
4ad49000
LP
3230 CGroupContext *c = data;
3231 CGroupDeviceAllow *a;
1116e14c 3232 const char *m = NULL;
4ad49000 3233 size_t n;
1116e14c 3234 int r;
4ad49000
LP
3235
3236 if (isempty(rvalue)) {
3237 while (c->device_allow)
3238 cgroup_context_free_device_allow(c, c->device_allow);
3239
3240 return 0;
3241 }
3242
1116e14c
NBS
3243 r = unit_full_printf(userdata, rvalue, &t);
3244 if(r < 0) {
3245 log_syntax(unit, LOG_WARNING, filename, line, r,
3246 "Failed to resolve specifiers in %s, ignoring: %m",
3247 rvalue);
3248 }
3249
3250 n = strcspn(t, WHITESPACE);
3251
3252 path = strndup(t, n);
4ad49000
LP
3253 if (!path)
3254 return log_oom();
3255
3ccb8862 3256 if (!is_deviceallow_pattern(path)) {
12ca818f 3257 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
3258 return 0;
3259 }
3260
1116e14c 3261 m = t + n + strspn(t + n, WHITESPACE);
4ad49000
LP
3262 if (isempty(m))
3263 m = "rwm";
3264
3265 if (!in_charset(m, "rwm")) {
12ca818f 3266 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
4ad49000
LP
3267 return 0;
3268 }
3269
3270 a = new0(CGroupDeviceAllow, 1);
3271 if (!a)
3272 return log_oom();
3273
3274 a->path = path;
3275 path = NULL;
3276 a->r = !!strchr(m, 'r');
3277 a->w = !!strchr(m, 'w');
3278 a->m = !!strchr(m, 'm');
3279
71fda00f 3280 LIST_PREPEND(device_allow, c->device_allow, a);
4ad49000
LP
3281 return 0;
3282}
3283
13c31542
TH
3284int config_parse_io_weight(
3285 const char *unit,
3286 const char *filename,
3287 unsigned line,
3288 const char *section,
3289 unsigned section_line,
3290 const char *lvalue,
3291 int ltype,
3292 const char *rvalue,
3293 void *data,
3294 void *userdata) {
3295
3296 uint64_t *weight = data;
3297 int r;
3298
3299 assert(filename);
3300 assert(lvalue);
3301 assert(rvalue);
3302
3303 r = cg_weight_parse(rvalue, weight);
3304 if (r < 0) {
3305 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue);
3306 return 0;
3307 }
3308
3309 return 0;
3310}
3311
3312int config_parse_io_device_weight(
3313 const char *unit,
3314 const char *filename,
3315 unsigned line,
3316 const char *section,
3317 unsigned section_line,
3318 const char *lvalue,
3319 int ltype,
3320 const char *rvalue,
3321 void *data,
3322 void *userdata) {
3323
3324 _cleanup_free_ char *path = NULL;
3325 CGroupIODeviceWeight *w;
3326 CGroupContext *c = data;
3327 const char *weight;
3328 uint64_t u;
3329 size_t n;
3330 int r;
3331
3332 assert(filename);
3333 assert(lvalue);
3334 assert(rvalue);
3335
3336 if (isempty(rvalue)) {
3337 while (c->io_device_weights)
3338 cgroup_context_free_io_device_weight(c, c->io_device_weights);
3339
3340 return 0;
3341 }
3342
3343 n = strcspn(rvalue, WHITESPACE);
3344 weight = rvalue + n;
3345 weight += strspn(weight, WHITESPACE);
3346
3347 if (isempty(weight)) {
3348 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
3349 return 0;
3350 }
3351
3352 path = strndup(rvalue, n);
3353 if (!path)
3354 return log_oom();
3355
3356 if (!path_startswith(path, "/dev")) {
3357 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3358 return 0;
3359 }
3360
3361 r = cg_weight_parse(weight, &u);
3362 if (r < 0) {
3363 log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight);
3364 return 0;
3365 }
3366
3367 assert(u != CGROUP_WEIGHT_INVALID);
3368
3369 w = new0(CGroupIODeviceWeight, 1);
3370 if (!w)
3371 return log_oom();
3372
3373 w->path = path;
3374 path = NULL;
3375
3376 w->weight = u;
3377
3378 LIST_PREPEND(device_weights, c->io_device_weights, w);
3379 return 0;
3380}
3381
3382int config_parse_io_limit(
3383 const char *unit,
3384 const char *filename,
3385 unsigned line,
3386 const char *section,
3387 unsigned section_line,
3388 const char *lvalue,
3389 int ltype,
3390 const char *rvalue,
3391 void *data,
3392 void *userdata) {
3393
3394 _cleanup_free_ char *path = NULL;
3395 CGroupIODeviceLimit *l = NULL, *t;
3396 CGroupContext *c = data;
9be57249 3397 CGroupIOLimitType type;
13c31542
TH
3398 const char *limit;
3399 uint64_t num;
13c31542
TH
3400 size_t n;
3401 int r;
3402
3403 assert(filename);
3404 assert(lvalue);
3405 assert(rvalue);
3406
9be57249
TH
3407 type = cgroup_io_limit_type_from_string(lvalue);
3408 assert(type >= 0);
13c31542
TH
3409
3410 if (isempty(rvalue)) {
3411 LIST_FOREACH(device_limits, l, c->io_device_limits)
9be57249 3412 l->limits[type] = cgroup_io_limit_defaults[type];
13c31542
TH
3413 return 0;
3414 }
3415
3416 n = strcspn(rvalue, WHITESPACE);
3417 limit = rvalue + n;
3418 limit += strspn(limit, WHITESPACE);
3419
3420 if (!*limit) {
3421 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
3422 return 0;
3423 }
3424
3425 path = strndup(rvalue, n);
3426 if (!path)
3427 return log_oom();
3428
3429 if (!path_startswith(path, "/dev")) {
3430 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
3431 return 0;
3432 }
3433
e57c9ce1 3434 if (streq("infinity", limit)) {
13c31542
TH
3435 num = CGROUP_LIMIT_MAX;
3436 } else {
3437 r = parse_size(limit, 1000, &num);
3438 if (r < 0 || num <= 0) {
3439 log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue);
3440 return 0;
3441 }
3442 }
3443
3444 LIST_FOREACH(device_limits, t, c->io_device_limits) {
3445 if (path_equal(path, t->path)) {
3446 l = t;
3447 break;
3448 }
3449 }
3450
3451 if (!l) {
9be57249
TH
3452 CGroupIOLimitType ttype;
3453
13c31542
TH
3454 l = new0(CGroupIODeviceLimit, 1);
3455 if (!l)
3456 return log_oom();
3457
3458 l->path = path;
3459 path = NULL;
9be57249
TH
3460 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
3461 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
13c31542
TH
3462
3463 LIST_PREPEND(device_limits, c->io_device_limits, l);
3464 }
3465
9be57249 3466 l->limits[type] = num;
13c31542
TH
3467
3468 return 0;
3469}
3470
4ad49000
LP
3471int config_parse_blockio_weight(
3472 const char *unit,
3473 const char *filename,
3474 unsigned line,
3475 const char *section,
71a61510 3476 unsigned section_line,
4ad49000
LP
3477 const char *lvalue,
3478 int ltype,
3479 const char *rvalue,
3480 void *data,
3481 void *userdata) {
3482
d53d9474 3483 uint64_t *weight = data;
95ae05c0
WC
3484 int r;
3485
3486 assert(filename);
3487 assert(lvalue);
3488 assert(rvalue);
3489
d53d9474
LP
3490 r = cg_blkio_weight_parse(rvalue, weight);
3491 if (r < 0) {
3492 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
95ae05c0
WC
3493 return 0;
3494 }
3495
8e7076ca
LP
3496 return 0;
3497}
3498
3499int config_parse_blockio_device_weight(
3500 const char *unit,
3501 const char *filename,
3502 unsigned line,
3503 const char *section,
71a61510 3504 unsigned section_line,
8e7076ca
LP
3505 const char *lvalue,
3506 int ltype,
3507 const char *rvalue,
3508 void *data,
3509 void *userdata) {
3510
4ad49000 3511 _cleanup_free_ char *path = NULL;
8e7076ca 3512 CGroupBlockIODeviceWeight *w;
4ad49000 3513 CGroupContext *c = data;
4ad49000 3514 const char *weight;
d53d9474 3515 uint64_t u;
4ad49000
LP
3516 size_t n;
3517 int r;
3518
3519 assert(filename);
3520 assert(lvalue);
3521 assert(rvalue);
3522
3523 if (isempty(rvalue)) {
4ad49000
LP
3524 while (c->blockio_device_weights)
3525 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
3526
3527 return 0;
3528 }
3529
3530 n = strcspn(rvalue, WHITESPACE);
3531 weight = rvalue + n;
d53d9474
LP
3532 weight += strspn(weight, WHITESPACE);
3533
3534 if (isempty(weight)) {
12ca818f 3535 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
8e7076ca
LP
3536 return 0;
3537 }
4ad49000 3538
8e7076ca
LP
3539 path = strndup(rvalue, n);
3540 if (!path)
3541 return log_oom();
4ad49000 3542
8e7076ca 3543 if (!path_startswith(path, "/dev")) {
12ca818f 3544 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
8e7076ca
LP
3545 return 0;
3546 }
4ad49000 3547
d53d9474
LP
3548 r = cg_blkio_weight_parse(weight, &u);
3549 if (r < 0) {
3550 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
4ad49000
LP
3551 return 0;
3552 }
3553
d53d9474
LP
3554 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
3555
8e7076ca
LP
3556 w = new0(CGroupBlockIODeviceWeight, 1);
3557 if (!w)
3558 return log_oom();
4ad49000 3559
8e7076ca
LP
3560 w->path = path;
3561 path = NULL;
4ad49000 3562
d53d9474 3563 w->weight = u;
4ad49000 3564
71fda00f 3565 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
3566 return 0;
3567}
3568
3569int config_parse_blockio_bandwidth(
3570 const char *unit,
3571 const char *filename,
3572 unsigned line,
3573 const char *section,
71a61510 3574 unsigned section_line,
4ad49000
LP
3575 const char *lvalue,
3576 int ltype,
3577 const char *rvalue,
3578 void *data,
3579 void *userdata) {
3580
3581 _cleanup_free_ char *path = NULL;
979d0311 3582 CGroupBlockIODeviceBandwidth *b = NULL, *t;
4ad49000
LP
3583 CGroupContext *c = data;
3584 const char *bandwidth;
59f448cf 3585 uint64_t bytes;
47c0980d 3586 bool read;
4ad49000
LP
3587 size_t n;
3588 int r;
3589
3590 assert(filename);
3591 assert(lvalue);
3592 assert(rvalue);
3593
47c0980d
G
3594 read = streq("BlockIOReadBandwidth", lvalue);
3595
4ad49000 3596 if (isempty(rvalue)) {
979d0311
TH
3597 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
3598 b->rbps = CGROUP_LIMIT_MAX;
3599 b->wbps = CGROUP_LIMIT_MAX;
3600 }
4ad49000
LP
3601 return 0;
3602 }
3603
3604 n = strcspn(rvalue, WHITESPACE);
3605 bandwidth = rvalue + n;
3606 bandwidth += strspn(bandwidth, WHITESPACE);
3607
3608 if (!*bandwidth) {
12ca818f 3609 log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
4ad49000
LP
3610 return 0;
3611 }
3612
3613 path = strndup(rvalue, n);
3614 if (!path)
3615 return log_oom();
3616
3617 if (!path_startswith(path, "/dev")) {
12ca818f 3618 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
4ad49000
LP
3619 return 0;
3620 }
3621
5556b5fe 3622 r = parse_size(bandwidth, 1000, &bytes);
4ad49000 3623 if (r < 0 || bytes <= 0) {
12ca818f 3624 log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
4ad49000
LP
3625 return 0;
3626 }
3627
979d0311
TH
3628 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
3629 if (path_equal(path, t->path)) {
3630 b = t;
3631 break;
3632 }
3633 }
4ad49000 3634
979d0311
TH
3635 if (!t) {
3636 b = new0(CGroupBlockIODeviceBandwidth, 1);
3637 if (!b)
3638 return log_oom();
3639
3640 b->path = path;
3641 path = NULL;
3642 b->rbps = CGROUP_LIMIT_MAX;
3643 b->wbps = CGROUP_LIMIT_MAX;
3644
3645 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
3646 }
4ad49000 3647
979d0311
TH
3648 if (read)
3649 b->rbps = bytes;
3650 else
3651 b->wbps = bytes;
4ad49000
LP
3652
3653 return 0;
3654}
3655
d420282b
LP
3656DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
3657
3658int config_parse_job_mode_isolate(
3659 const char *unit,
3660 const char *filename,
3661 unsigned line,
3662 const char *section,
3663 unsigned section_line,
3664 const char *lvalue,
3665 int ltype,
3666 const char *rvalue,
3667 void *data,
3668 void *userdata) {
3669
3670 JobMode *m = data;
3671 int r;
3672
3673 assert(filename);
3674 assert(lvalue);
3675 assert(rvalue);
3676
3677 r = parse_boolean(rvalue);
3678 if (r < 0) {
12ca818f 3679 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
d420282b
LP
3680 return 0;
3681 }
3682
3683 *m = r ? JOB_ISOLATE : JOB_REPLACE;
3684 return 0;
3685}
3686
e66cf1a3
LP
3687int config_parse_runtime_directory(
3688 const char *unit,
3689 const char *filename,
3690 unsigned line,
3691 const char *section,
3692 unsigned section_line,
3693 const char *lvalue,
3694 int ltype,
3695 const char *rvalue,
3696 void *data,
3697 void *userdata) {
3698
a2a5291b 3699 char***rt = data;
9b5864d9 3700 Unit *u = userdata;
035fe294 3701 const char *p;
e66cf1a3
LP
3702 int r;
3703
3704 assert(filename);
3705 assert(lvalue);
3706 assert(rvalue);
3707 assert(data);
3708
3709 if (isempty(rvalue)) {
3710 /* Empty assignment resets the list */
6796073e 3711 *rt = strv_free(*rt);
e66cf1a3
LP
3712 return 0;
3713 }
3714
035fe294
ZJS
3715 for (p = rvalue;;) {
3716 _cleanup_free_ char *word = NULL, *k = NULL;
e66cf1a3 3717
035fe294
ZJS
3718 r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
3719 if (r == 0)
3720 return 0;
3721 if (r == -ENOMEM)
e66cf1a3 3722 return log_oom();
035fe294
ZJS
3723 if (r < 0) {
3724 log_syntax(unit, LOG_WARNING, filename, line, r,
3725 "Invalid syntax, ignoring: %s", rvalue);
3726 return 0;
3727 }
e66cf1a3 3728
18913df9 3729 r = unit_full_printf(u, word, &k);
9b5864d9 3730 if (r < 0) {
035fe294
ZJS
3731 log_syntax(unit, LOG_ERR, filename, line, r,
3732 "Failed to resolve specifiers in \"%s\", ignoring: %m", word);
9b5864d9
MG
3733 continue;
3734 }
3735
035fe294
ZJS
3736 if (!filename_is_valid(k)) {
3737 log_syntax(unit, LOG_ERR, filename, line, 0,
3738 "Runtime directory is not valid, ignoring assignment: %s", rvalue);
e66cf1a3
LP
3739 continue;
3740 }
3741
035fe294 3742 r = strv_push(rt, k);
e66cf1a3
LP
3743 if (r < 0)
3744 return log_oom();
035fe294 3745 k = NULL;
e66cf1a3 3746 }
e66cf1a3
LP
3747}
3748
3af00fb8
LP
3749int config_parse_set_status(
3750 const char *unit,
3751 const char *filename,
3752 unsigned line,
3753 const char *section,
3754 unsigned section_line,
3755 const char *lvalue,
3756 int ltype,
3757 const char *rvalue,
3758 void *data,
3759 void *userdata) {
3760
3af00fb8 3761 size_t l;
a2a5291b 3762 const char *word, *state;
3af00fb8
LP
3763 int r;
3764 ExitStatusSet *status_set = data;
3765
3766 assert(filename);
3767 assert(lvalue);
3768 assert(rvalue);
3769 assert(data);
3770
3e2d435b 3771 /* Empty assignment resets the list */
3af00fb8 3772 if (isempty(rvalue)) {
3e2d435b 3773 exit_status_set_free(status_set);
3af00fb8
LP
3774 return 0;
3775 }
3776
a2a5291b 3777 FOREACH_WORD(word, l, rvalue, state) {
3af00fb8
LP
3778 _cleanup_free_ char *temp;
3779 int val;
61593865 3780 Set **set;
3af00fb8 3781
a2a5291b 3782 temp = strndup(word, l);
3af00fb8
LP
3783 if (!temp)
3784 return log_oom();
3785
3786 r = safe_atoi(temp, &val);
3787 if (r < 0) {
3788 val = signal_from_string_try_harder(temp);
3789
1e2fd62d 3790 if (val <= 0) {
12ca818f 3791 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
61593865 3792 continue;
3af00fb8 3793 }
61593865 3794 set = &status_set->signal;
3af00fb8 3795 } else {
1e2fd62d 3796 if (val < 0 || val > 255) {
12ca818f 3797 log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
1e2fd62d 3798 continue;
3af00fb8 3799 }
61593865 3800 set = &status_set->status;
3af00fb8 3801 }
1e2fd62d 3802
61593865 3803 r = set_ensure_allocated(set, NULL);
1e2fd62d
ZJS
3804 if (r < 0)
3805 return log_oom();
3806
61593865 3807 r = set_put(*set, INT_TO_PTR(val));
1e2fd62d 3808 if (r < 0) {
12ca818f 3809 log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
1e2fd62d
ZJS
3810 return r;
3811 }
3af00fb8 3812 }
b2fadec6 3813 if (!isempty(state))
12ca818f 3814 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
3af00fb8
LP
3815
3816 return 0;
3817}
3818
94828d2d
LP
3819int config_parse_namespace_path_strv(
3820 const char *unit,
3821 const char *filename,
3822 unsigned line,
3823 const char *section,
3824 unsigned section_line,
3825 const char *lvalue,
3826 int ltype,
3827 const char *rvalue,
3828 void *data,
3829 void *userdata) {
3830
7b07e993 3831 Unit *u = userdata;
a2a5291b 3832 char*** sv = data;
727f76d7 3833 const char *cur;
94828d2d
LP
3834 int r;
3835
3836 assert(filename);
3837 assert(lvalue);
3838 assert(rvalue);
3839 assert(data);
3840
3841 if (isempty(rvalue)) {
3842 /* Empty assignment resets the list */
6796073e 3843 *sv = strv_free(*sv);
94828d2d
LP
3844 return 0;
3845 }
3846
7b07e993 3847 cur = rvalue;
727f76d7 3848 for (;;) {
7b07e993
LP
3849 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
3850 bool ignore_enoent;
94828d2d 3851
727f76d7 3852 r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
0293a7a8
EV
3853 if (r == 0)
3854 break;
3855 if (r == -ENOMEM)
3856 return log_oom();
727f76d7 3857 if (r < 0) {
7b07e993 3858 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
727f76d7
EV
3859 return 0;
3860 }
94828d2d 3861
727f76d7
EV
3862 if (!utf8_is_valid(word)) {
3863 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
94828d2d
LP
3864 continue;
3865 }
3866
7b07e993
LP
3867 ignore_enoent = word[0] == '-';
3868
3869 r = unit_full_printf(u, word + ignore_enoent, &resolved);
3870 if (r < 0) {
3871 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word);
94828d2d
LP
3872 continue;
3873 }
3874
7b07e993
LP
3875 if (!path_is_absolute(resolved)) {
3876 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", resolved);
3877 continue;
3878 }
3879
3880 path_kill_slashes(resolved);
94828d2d 3881
7b07e993
LP
3882 joined = strjoin(ignore_enoent ? "-" : "", resolved);
3883
3884 r = strv_push(sv, joined);
94828d2d
LP
3885 if (r < 0)
3886 return log_oom();
3887
7b07e993 3888 joined = NULL;
94828d2d
LP
3889 }
3890
3891 return 0;
3892}
3893
f1721625 3894int config_parse_no_new_privileges(
760b9d7c
LP
3895 const char* unit,
3896 const char *filename,
3897 unsigned line,
3898 const char *section,
3899 unsigned section_line,
3900 const char *lvalue,
3901 int ltype,
3902 const char *rvalue,
3903 void *data,
3904 void *userdata) {
3905
3906 ExecContext *c = data;
3907 int k;
3908
3909 assert(filename);
3910 assert(lvalue);
3911 assert(rvalue);
3912 assert(data);
3913
3914 k = parse_boolean(rvalue);
3915 if (k < 0) {
12ca818f 3916 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
760b9d7c
LP
3917 return 0;
3918 }
3919
9b232d32 3920 c->no_new_privileges = k;
760b9d7c
LP
3921
3922 return 0;
3923}
3924
1b8689f9 3925int config_parse_protect_home(
417116f2
LP
3926 const char* unit,
3927 const char *filename,
3928 unsigned line,
3929 const char *section,
3930 unsigned section_line,
3931 const char *lvalue,
3932 int ltype,
3933 const char *rvalue,
3934 void *data,
3935 void *userdata) {
3936
3937 ExecContext *c = data;
3938 int k;
3939
3940 assert(filename);
3941 assert(lvalue);
3942 assert(rvalue);
3943 assert(data);
3944
3945 /* Our enum shall be a superset of booleans, hence first try
61233823 3946 * to parse as boolean, and then as enum */
417116f2
LP
3947
3948 k = parse_boolean(rvalue);
3949 if (k > 0)
1b8689f9 3950 c->protect_home = PROTECT_HOME_YES;
417116f2 3951 else if (k == 0)
1b8689f9 3952 c->protect_home = PROTECT_HOME_NO;
417116f2 3953 else {
1b8689f9 3954 ProtectHome h;
417116f2 3955
1b8689f9 3956 h = protect_home_from_string(rvalue);
9ed794a3 3957 if (h < 0) {
12ca818f 3958 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
417116f2
LP
3959 return 0;
3960 }
3961
1b8689f9
LP
3962 c->protect_home = h;
3963 }
3964
3965 return 0;
3966}
3967
3968int config_parse_protect_system(
3969 const char* unit,
3970 const char *filename,
3971 unsigned line,
3972 const char *section,
3973 unsigned section_line,
3974 const char *lvalue,
3975 int ltype,
3976 const char *rvalue,
3977 void *data,
3978 void *userdata) {
3979
3980 ExecContext *c = data;
3981 int k;
3982
3983 assert(filename);
3984 assert(lvalue);
3985 assert(rvalue);
3986 assert(data);
3987
3988 /* Our enum shall be a superset of booleans, hence first try
61233823 3989 * to parse as boolean, and then as enum */
1b8689f9
LP
3990
3991 k = parse_boolean(rvalue);
3992 if (k > 0)
3993 c->protect_system = PROTECT_SYSTEM_YES;
3994 else if (k == 0)
3995 c->protect_system = PROTECT_SYSTEM_NO;
3996 else {
3997 ProtectSystem s;
3998
3999 s = protect_system_from_string(rvalue);
9ed794a3 4000 if (s < 0) {
12ca818f 4001 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
1b8689f9
LP
4002 return 0;
4003 }
4004
4005 c->protect_system = s;
417116f2
LP
4006 }
4007
4008 return 0;
4009}
4010
071830ff 4011#define FOLLOW_MAX 8
87f0e418 4012
9e2f7c11 4013static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
a837f088 4014 char *id = NULL;
0301abf4 4015 unsigned c = 0;
87f0e418
LP
4016 int fd, r;
4017 FILE *f;
87f0e418
LP
4018
4019 assert(filename);
4020 assert(*filename);
4021 assert(_f);
4022 assert(names);
4023
0301abf4
LP
4024 /* This will update the filename pointer if the loaded file is
4025 * reached by a symlink. The old string will be freed. */
87f0e418 4026
0301abf4 4027 for (;;) {
2c7108c4 4028 char *target, *name;
87f0e418 4029
0301abf4
LP
4030 if (c++ >= FOLLOW_MAX)
4031 return -ELOOP;
4032
b08d03ff
LP
4033 path_kill_slashes(*filename);
4034
87f0e418 4035 /* Add the file name we are currently looking at to
8f05424d
LP
4036 * the names of this unit, but only if it is a valid
4037 * unit name. */
2b6bf07d 4038 name = basename(*filename);
7410616c 4039 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
8f05424d 4040
15e11d81
LP
4041 id = set_get(names, name);
4042 if (!id) {
4043 id = strdup(name);
4044 if (!id)
8f05424d 4045 return -ENOMEM;
87f0e418 4046
ef42202a
ZJS
4047 r = set_consume(names, id);
4048 if (r < 0)
8f05424d 4049 return r;
87f0e418 4050 }
87f0e418
LP
4051 }
4052
0301abf4 4053 /* Try to open the file name, but don't if its a symlink */
9946996c
LP
4054 fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
4055 if (fd >= 0)
87f0e418
LP
4056 break;
4057
0301abf4
LP
4058 if (errno != ELOOP)
4059 return -errno;
4060
87f0e418 4061 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
9946996c
LP
4062 r = readlink_and_make_absolute(*filename, &target);
4063 if (r < 0)
0301abf4 4064 return r;
87f0e418 4065
0301abf4 4066 free(*filename);
2c7108c4 4067 *filename = target;
87f0e418
LP
4068 }
4069
9946996c
LP
4070 f = fdopen(fd, "re");
4071 if (!f) {
03e334a1 4072 safe_close(fd);
d4ad27a1 4073 return -errno;
87f0e418
LP
4074 }
4075
4076 *_f = f;
9e2f7c11 4077 *_final = id;
a837f088 4078
0301abf4 4079 return 0;
87f0e418
LP
4080}
4081
23a177ef
LP
4082static int merge_by_names(Unit **u, Set *names, const char *id) {
4083 char *k;
4084 int r;
4085
4086 assert(u);
4087 assert(*u);
4088 assert(names);
4089
4090 /* Let's try to add in all symlink names we found */
4091 while ((k = set_steal_first(names))) {
4092
4093 /* First try to merge in the other name into our
4094 * unit */
9946996c
LP
4095 r = unit_merge_by_name(*u, k);
4096 if (r < 0) {
23a177ef
LP
4097 Unit *other;
4098
4099 /* Hmm, we couldn't merge the other unit into
4100 * ours? Then let's try it the other way
4101 * round */
4102
7aad67e7
MS
4103 /* If the symlink name we are looking at is unit template, then
4104 we must search for instance of this template */
9d3e3406 4105 if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) {
7aad67e7
MS
4106 _cleanup_free_ char *instance = NULL;
4107
4108 r = unit_name_replace_instance(k, (*u)->instance, &instance);
4109 if (r < 0)
4110 return r;
4111
4112 other = manager_get_unit((*u)->manager, instance);
4113 } else
4114 other = manager_get_unit((*u)->manager, k);
4115
23a177ef
LP
4116 free(k);
4117
9946996c
LP
4118 if (other) {
4119 r = unit_merge(other, *u);
4120 if (r >= 0) {
23a177ef
LP
4121 *u = other;
4122 return merge_by_names(u, names, NULL);
4123 }
9946996c 4124 }
23a177ef
LP
4125
4126 return r;
4127 }
4128
4129 if (id == k)
4130 unit_choose_id(*u, id);
4131
4132 free(k);
4133 }
4134
4135 return 0;
4136}
4137
e537352b 4138static int load_from_path(Unit *u, const char *path) {
e48614c4
ZJS
4139 _cleanup_set_free_free_ Set *symlink_names = NULL;
4140 _cleanup_fclose_ FILE *f = NULL;
4141 _cleanup_free_ char *filename = NULL;
4142 char *id = NULL;
23a177ef 4143 Unit *merged;
45fb0699 4144 struct stat st;
a837f088 4145 int r;
23a177ef
LP
4146
4147 assert(u);
e537352b 4148 assert(path);
3efd4195 4149
d5099efc 4150 symlink_names = set_new(&string_hash_ops);
f975e971 4151 if (!symlink_names)
87f0e418 4152 return -ENOMEM;
3efd4195 4153
036643a2
LP
4154 if (path_is_absolute(path)) {
4155
9946996c 4156 filename = strdup(path);
e48614c4
ZJS
4157 if (!filename)
4158 return -ENOMEM;
036643a2 4159
9946996c
LP
4160 r = open_follow(&filename, &f, symlink_names, &id);
4161 if (r < 0) {
97b11eed 4162 filename = mfree(filename);
036643a2 4163 if (r != -ENOENT)
e48614c4 4164 return r;
036643a2
LP
4165 }
4166
4167 } else {
4168 char **p;
4169
a3c4eb07 4170 STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
036643a2
LP
4171
4172 /* Instead of opening the path right away, we manually
4173 * follow all symlinks and add their name to our unit
4174 * name set while doing so */
9946996c 4175 filename = path_make_absolute(path, *p);
e48614c4
ZJS
4176 if (!filename)
4177 return -ENOMEM;
036643a2 4178
ac155bb8
MS
4179 if (u->manager->unit_path_cache &&
4180 !set_get(u->manager->unit_path_cache, filename))
fe51822e
LP
4181 r = -ENOENT;
4182 else
4183 r = open_follow(&filename, &f, symlink_names, &id);
a837f088
LP
4184 if (r >= 0)
4185 break;
4186 filename = mfree(filename);
a1feacf7
ZJS
4187
4188 /* ENOENT means that the file is missing or is a dangling symlink.
4189 * ENOTDIR means that one of paths we expect to be is a directory
4190 * is not a directory, we should just ignore that.
4191 * EACCES means that the directory or file permissions are wrong.
4192 */
4193 if (r == -EACCES)
4194 log_debug_errno(r, "Cannot access \"%s\": %m", filename);
4195 else if (!IN_SET(r, -ENOENT, -ENOTDIR))
a837f088 4196 return r;
fe51822e 4197
a837f088
LP
4198 /* Empty the symlink names for the next run */
4199 set_clear_free(symlink_names);
036643a2
LP
4200 }
4201 }
034c6ed7 4202
e48614c4 4203 if (!filename)
8f05424d 4204 /* Hmm, no suitable file found? */
e48614c4 4205 return 0;
87f0e418 4206
8a993b61 4207 if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
a837f088
LP
4208 log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
4209 return -ELOOP;
4210 }
4211
23a177ef 4212 merged = u;
9946996c
LP
4213 r = merge_by_names(&merged, symlink_names, id);
4214 if (r < 0)
e48614c4 4215 return r;
87f0e418 4216
23a177ef 4217 if (merged != u) {
ac155bb8 4218 u->load_state = UNIT_MERGED;
e48614c4 4219 return 0;
034c6ed7
LP
4220 }
4221
e48614c4
ZJS
4222 if (fstat(fileno(f), &st) < 0)
4223 return -errno;
45fb0699 4224
3a8db9fe 4225 if (null_or_empty(&st)) {
ac155bb8 4226 u->load_state = UNIT_MASKED;
3a8db9fe
ZJS
4227 u->fragment_mtime = 0;
4228 } else {
c2756a68 4229 u->load_state = UNIT_LOADED;
3a8db9fe 4230 u->fragment_mtime = timespec_load(&st.st_mtim);
c2756a68 4231
00dc5d76 4232 /* Now, parse the file contents */
36f822c4
ZJS
4233 r = config_parse(u->id, filename, f,
4234 UNIT_VTABLE(u)->sections,
4235 config_item_perf_lookup, load_fragment_gperf_lookup,
4236 false, true, false, u);
f975e971 4237 if (r < 0)
e48614c4 4238 return r;
00dc5d76 4239 }
b08d03ff 4240
ac155bb8
MS
4241 free(u->fragment_path);
4242 u->fragment_path = filename;
0301abf4 4243 filename = NULL;
87f0e418 4244
1b64d026
LP
4245 if (u->source_path) {
4246 if (stat(u->source_path, &st) >= 0)
4247 u->source_mtime = timespec_load(&st.st_mtim);
4248 else
4249 u->source_mtime = 0;
4250 }
4251
e48614c4 4252 return 0;
0301abf4
LP
4253}
4254
e537352b 4255int unit_load_fragment(Unit *u) {
23a177ef 4256 int r;
294d81f1
LP
4257 Iterator i;
4258 const char *t;
0301abf4
LP
4259
4260 assert(u);
ac155bb8
MS
4261 assert(u->load_state == UNIT_STUB);
4262 assert(u->id);
23a177ef 4263
3f5e8115
LP
4264 if (u->transient) {
4265 u->load_state = UNIT_LOADED;
4266 return 0;
4267 }
4268
294d81f1
LP
4269 /* First, try to find the unit under its id. We always look
4270 * for unit files in the default directories, to make it easy
4271 * to override things by placing things in /etc/systemd/system */
9946996c
LP
4272 r = load_from_path(u, u->id);
4273 if (r < 0)
294d81f1
LP
4274 return r;
4275
4276 /* Try to find an alias we can load this with */
abc08d4d 4277 if (u->load_state == UNIT_STUB) {
ac155bb8 4278 SET_FOREACH(t, u->names, i) {
294d81f1 4279
ac155bb8 4280 if (t == u->id)
294d81f1
LP
4281 continue;
4282
9946996c
LP
4283 r = load_from_path(u, t);
4284 if (r < 0)
294d81f1
LP
4285 return r;
4286
ac155bb8 4287 if (u->load_state != UNIT_STUB)
294d81f1
LP
4288 break;
4289 }
abc08d4d 4290 }
23a177ef 4291
294d81f1 4292 /* And now, try looking for it under the suggested (originally linked) path */
ac155bb8 4293 if (u->load_state == UNIT_STUB && u->fragment_path) {
6ccb1b44 4294
9946996c
LP
4295 r = load_from_path(u, u->fragment_path);
4296 if (r < 0)
23a177ef 4297 return r;
0301abf4 4298
ece174c5 4299 if (u->load_state == UNIT_STUB)
6ccb1b44
LP
4300 /* Hmm, this didn't work? Then let's get rid
4301 * of the fragment path stored for us, so that
4302 * we don't point to an invalid location. */
a1e58e8e 4303 u->fragment_path = mfree(u->fragment_path);
6ccb1b44
LP
4304 }
4305
294d81f1 4306 /* Look for a template */
ac155bb8 4307 if (u->load_state == UNIT_STUB && u->instance) {
7410616c 4308 _cleanup_free_ char *k = NULL;
294d81f1 4309
7410616c
LP
4310 r = unit_name_template(u->id, &k);
4311 if (r < 0)
4312 return r;
294d81f1
LP
4313
4314 r = load_from_path(u, k);
294d81f1 4315 if (r < 0)
9e2f7c11 4316 return r;
890f434c 4317
abc08d4d 4318 if (u->load_state == UNIT_STUB) {
ac155bb8 4319 SET_FOREACH(t, u->names, i) {
bc9fd78c 4320 _cleanup_free_ char *z = NULL;
87f0e418 4321
ac155bb8 4322 if (t == u->id)
23a177ef 4323 continue;
071830ff 4324
7410616c
LP
4325 r = unit_name_template(t, &z);
4326 if (r < 0)
4327 return r;
294d81f1 4328
bc9fd78c 4329 r = load_from_path(u, z);
294d81f1 4330 if (r < 0)
23a177ef 4331 return r;
890f434c 4332
ac155bb8 4333 if (u->load_state != UNIT_STUB)
23a177ef
LP
4334 break;
4335 }
abc08d4d 4336 }
071830ff
LP
4337 }
4338
23a177ef 4339 return 0;
3efd4195 4340}
e537352b
LP
4341
4342void unit_dump_config_items(FILE *f) {
f975e971
LP
4343 static const struct {
4344 const ConfigParserCallback callback;
4345 const char *rvalue;
4346 } table[] = {
7f8aa671 4347#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR)
17df7223
LP
4348 { config_parse_warn_compat, "NOTSUPPORTED" },
4349#endif
f975e971
LP
4350 { config_parse_int, "INTEGER" },
4351 { config_parse_unsigned, "UNSIGNED" },
5556b5fe 4352 { config_parse_iec_size, "SIZE" },
59f448cf 4353 { config_parse_iec_uint64, "SIZE" },
5556b5fe 4354 { config_parse_si_size, "SIZE" },
f975e971
LP
4355 { config_parse_bool, "BOOLEAN" },
4356 { config_parse_string, "STRING" },
4357 { config_parse_path, "PATH" },
4358 { config_parse_unit_path_printf, "PATH" },
4359 { config_parse_strv, "STRING [...]" },
4360 { config_parse_exec_nice, "NICE" },
4361 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
4362 { config_parse_exec_io_class, "IOCLASS" },
4363 { config_parse_exec_io_priority, "IOPRIORITY" },
4364 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
4365 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
4366 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
4367 { config_parse_mode, "MODE" },
4368 { config_parse_unit_env_file, "FILE" },
52c239d7
LB
4369 { config_parse_exec_output, "OUTPUT" },
4370 { config_parse_exec_input, "INPUT" },
ca37242e
LP
4371 { config_parse_log_facility, "FACILITY" },
4372 { config_parse_log_level, "LEVEL" },
f975e971 4373 { config_parse_exec_secure_bits, "SECUREBITS" },
a103496c 4374 { config_parse_capability_set, "BOUNDINGSET" },
f975e971 4375 { config_parse_limit, "LIMIT" },
f975e971 4376 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
4377 { config_parse_exec, "PATH [ARGUMENT [...]]" },
4378 { config_parse_service_type, "SERVICETYPE" },
4379 { config_parse_service_restart, "SERVICERESTART" },
4380#ifdef HAVE_SYSV_COMPAT
4381 { config_parse_sysv_priority, "SYSVPRIORITY" },
f975e971
LP
4382#endif
4383 { config_parse_kill_mode, "KILLMODE" },
f757855e 4384 { config_parse_signal, "SIGNAL" },
f975e971
LP
4385 { config_parse_socket_listen, "SOCKET [...]" },
4386 { config_parse_socket_bind, "SOCKETBIND" },
4387 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 4388 { config_parse_sec, "SECONDS" },
d88a251b 4389 { config_parse_nsec, "NANOSECONDS" },
94828d2d 4390 { config_parse_namespace_path_strv, "PATH [...]" },
7c8fa05c 4391 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
4392 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
4393 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 4394 { config_parse_trigger_unit, "UNIT" },
f975e971 4395 { config_parse_timer, "TIMER" },
f975e971 4396 { config_parse_path_spec, "PATH" },
f975e971
LP
4397 { config_parse_notify_access, "ACCESS" },
4398 { config_parse_ip_tos, "TOS" },
4399 { config_parse_unit_condition_path, "CONDITION" },
4400 { config_parse_unit_condition_string, "CONDITION" },
4401 { config_parse_unit_condition_null, "CONDITION" },
a016b922 4402 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
4403 { config_parse_documentation, "URL" },
4404 { config_parse_service_timeout, "SECONDS" },
87a47f99 4405 { config_parse_emergency_action, "ACTION" },
7f0386f6
LP
4406 { config_parse_set_status, "STATUS" },
4407 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 4408 { config_parse_environ, "ENVIRON" },
c0467cf3 4409#ifdef HAVE_SECCOMP
17df7223 4410 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 4411 { config_parse_syscall_archs, "ARCHS" },
17df7223 4412 { config_parse_syscall_errno, "ERRNO" },
4298d0b5 4413 { config_parse_address_families, "FAMILIES" },
add00535 4414 { config_parse_restrict_namespaces, "NAMESPACES" },
c0467cf3 4415#endif
7f0386f6 4416 { config_parse_cpu_shares, "SHARES" },
66ebf6c0 4417 { config_parse_cpu_weight, "WEIGHT" },
7f0386f6
LP
4418 { config_parse_memory_limit, "LIMIT" },
4419 { config_parse_device_allow, "DEVICE" },
4420 { config_parse_device_policy, "POLICY" },
13c31542
TH
4421 { config_parse_io_limit, "LIMIT" },
4422 { config_parse_io_weight, "WEIGHT" },
4423 { config_parse_io_device_weight, "DEVICEWEIGHT" },
7f0386f6
LP
4424 { config_parse_blockio_bandwidth, "BANDWIDTH" },
4425 { config_parse_blockio_weight, "WEIGHT" },
4426 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
4427 { config_parse_long, "LONG" },
4428 { config_parse_socket_service, "SERVICE" },
6a6751fe
LP
4429#ifdef HAVE_SELINUX
4430 { config_parse_exec_selinux_context, "LABEL" },
4431#endif
4432 { config_parse_job_mode, "MODE" },
4433 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 4434 { config_parse_personality, "PERSONALITY" },
f975e971
LP
4435 };
4436
4437 const char *prev = NULL;
4438 const char *i;
4439
4440 assert(f);
e537352b 4441
f975e971
LP
4442 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
4443 const char *rvalue = "OTHER", *lvalue;
4444 unsigned j;
4445 size_t prefix_len;
4446 const char *dot;
4447 const ConfigPerfItem *p;
4448
4449 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
4450
4451 dot = strchr(i, '.');
4452 lvalue = dot ? dot + 1 : i;
4453 prefix_len = dot-i;
4454
4455 if (dot)
641906e9 4456 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
4457 if (prev)
4458 fputc('\n', f);
4459
4460 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
4461 }
4462
4463 for (j = 0; j < ELEMENTSOF(table); j++)
4464 if (p->parse == table[j].callback) {
4465 rvalue = table[j].rvalue;
4466 break;
4467 }
4468
4469 fprintf(f, "%s=%s\n", lvalue, rvalue);
4470 prev = i;
4471 }
e537352b 4472}