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