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