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