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