]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/load-fragment.c
core: resolve binary names immediately before execution
[thirdparty/systemd.git] / src / core / load-fragment.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
a7334b09 2/***
96b2fb93 3 Copyright © 2012 Holger Hans Peter Freyther
a7334b09
LP
4***/
5
3efd4195 6#include <errno.h>
87f0e418 7#include <fcntl.h>
25e870b5 8#include <linux/fs.h>
5f5d8eab 9#include <linux/oom.h>
349cc4a5 10#if HAVE_SECCOMP
618234a5
LP
11#include <seccomp.h>
12#endif
5f5d8eab 13#include <sched.h>
3d57c6ab 14#include <sys/resource.h>
3efd4195 15
bed0b7df
LP
16#include "sd-messages.h"
17
5f5d8eab 18#include "af-list.h"
cf0fbc49 19#include "alloc-util.h"
57b7a260 20#include "all-units.h"
fab34748 21#include "bpf-firewall.h"
5f5d8eab
LP
22#include "bus-error.h"
23#include "bus-internal.h"
24#include "bus-util.h"
25#include "cap-list.h"
a103496c 26#include "capability-util.h"
fdb3deca 27#include "cgroup-setup.h"
3efd4195 28#include "conf-parser.h"
618234a5 29#include "cpu-set-util.h"
5f5d8eab
LP
30#include "env-util.h"
31#include "errno-list.h"
4f5dd394 32#include "escape.h"
3ffd4af2 33#include "fd-util.h"
0389f4fa 34#include "fileio.h"
f4f15635 35#include "fs-util.h"
08f3be7a 36#include "hexdecoct.h"
d3070fbd 37#include "io-util.h"
9eba9da4 38#include "ioprio.h"
da96ad5a 39#include "ip-protocol-list.h"
d3070fbd 40#include "journal-util.h"
eefc66aa 41#include "limits-util.h"
3ffd4af2 42#include "load-fragment.h"
5f5d8eab 43#include "log.h"
049af8ad 44#include "mountpoint-util.h"
d8b4d14d 45#include "nulstr-util.h"
6bedfcbb 46#include "parse-util.h"
9eb977db 47#include "path-util.h"
7b3e062c 48#include "process-util.h"
349cc4a5 49#if HAVE_SECCOMP
57183d11
LP
50#include "seccomp-util.h"
51#endif
07d46372 52#include "securebits-util.h"
5f5d8eab 53#include "signal-util.h"
5c3fa98d 54#include "socket-netlink.h"
8fcde012 55#include "stat-util.h"
07630cea 56#include "string-util.h"
5f5d8eab 57#include "strv.h"
91dd5f7c
LP
58#include "syslog-util.h"
59#include "time-util.h"
5f5d8eab
LP
60#include "unit-name.h"
61#include "unit-printf.h"
66dccd8d 62#include "user-util.h"
bb0c0d6f 63#include "utf8.h"
49cf4170 64#include "web-util.h"
57183d11 65
d2b42d63 66static int parse_socket_protocol(const char *s) {
53577580
YW
67 int r;
68
d2b42d63 69 r = parse_ip_protocol(s);
53577580 70 if (r < 0)
acf4d158 71 return r;
53577580
YW
72 if (!IN_SET(r, IPPROTO_UDPLITE, IPPROTO_SCTP))
73 return -EPROTONOSUPPORT;
74
75 return r;
76}
77
a07a7324
FS
78int parse_crash_chvt(const char *value, int *data) {
79 int b;
80
81 if (safe_atoi(value, data) >= 0)
82 return 0;
83
84 b = parse_boolean(value);
85 if (b < 0)
86 return b;
87
88 if (b > 0)
89 *data = 0; /* switch to where kmsg goes */
90 else
91 *data = -1; /* turn off switching */
92
93 return 0;
94}
95
96int parse_confirm_spawn(const char *value, char **console) {
97 char *s;
98 int r;
99
100 r = value ? parse_boolean(value) : 1;
101 if (r == 0) {
102 *console = NULL;
103 return 0;
4a8daee7 104 } else if (r > 0) /* on with default tty */
a07a7324
FS
105 s = strdup("/dev/console");
106 else if (is_path(value)) /* on with fully qualified path */
107 s = strdup(value);
108 else /* on with only a tty file name, not a fully qualified path */
4a8daee7 109 s = path_join("/dev/", value);
a07a7324
FS
110 if (!s)
111 return -ENOMEM;
112
113 *console = s;
114 return 0;
115}
116
d2b42d63 117DEFINE_CONFIG_PARSE(config_parse_socket_protocol, parse_socket_protocol, "Failed to parse socket protocol");
53577580 118DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits");
5afe510c 119DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode");
53577580 120DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
53577580 121DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
4e399953
LP
122DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_proc, protect_proc, ProtectProc, "Failed to parse /proc/ protection mode");
123DEFINE_CONFIG_PARSE_ENUM(config_parse_proc_subset, proc_subset, ProcSubset, "Failed to parse /proc/ subset mode");
53577580
YW
124DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
125DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
53577580 126DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
1e8c7bd5
YW
127DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "Failed to parse protect home value");
128DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value");
53577580
YW
129DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
130DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
131DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
bf760801 132DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode");
53577580 133DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value");
afcfaa69 134DEFINE_CONFIG_PARSE_ENUM(config_parse_oom_policy, oom_policy, OOMPolicy, "Failed to parse OOM policy");
53577580
YW
135DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1, "Failed to parse IP TOS value");
136DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight, cg_blkio_weight_parse, uint64_t, "Invalid block IO weight");
137DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Invalid weight");
138DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
139DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
b070c7c0 140DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
6327aa9f 141DEFINE_CONFIG_PARSE_ENUM(config_parse_status_unit_format, status_unit_format, StatusUnitFormat, "Failed to parse status unit format");
5afe510c 142
f32b43bd
LP
143int config_parse_unit_deps(
144 const char *unit,
145 const char *filename,
146 unsigned line,
147 const char *section,
148 unsigned section_line,
149 const char *lvalue,
150 int ltype,
151 const char *rvalue,
152 void *data,
153 void *userdata) {
3efd4195 154
f975e971 155 UnitDependency d = ltype;
87f0e418 156 Unit *u = userdata;
3efd4195
LP
157
158 assert(filename);
159 assert(lvalue);
160 assert(rvalue);
3efd4195 161
323dda78 162 for (const char *p = rvalue;;) {
3d793d29 163 _cleanup_free_ char *word = NULL, *k = NULL;
3efd4195 164 int r;
3efd4195 165
c89f52ac 166 r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
3d793d29 167 if (r == 0)
323dda78 168 return 0;
3d793d29 169 if (r == -ENOMEM)
74051b9b 170 return log_oom();
3d793d29 171 if (r < 0) {
323dda78
YW
172 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
173 return 0;
3d793d29 174 }
3efd4195 175
3d793d29 176 r = unit_name_printf(u, word, &k);
19f6d710 177 if (r < 0) {
323dda78 178 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
19f6d710
LP
179 continue;
180 }
9e2f7c11 181
35d8c19a 182 r = unit_add_dependency_by_name(u, d, k, true, UNIT_DEPENDENCY_FILE);
57020a3a 183 if (r < 0)
323dda78 184 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
3efd4195 185 }
3efd4195
LP
186}
187
f32b43bd
LP
188int config_parse_obsolete_unit_deps(
189 const char *unit,
190 const char *filename,
191 unsigned line,
192 const char *section,
193 unsigned section_line,
194 const char *lvalue,
195 int ltype,
196 const char *rvalue,
197 void *data,
198 void *userdata) {
199
200 log_syntax(unit, LOG_WARNING, filename, line, 0,
201 "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue, unit_dependency_to_string(ltype));
202
203 return config_parse_unit_deps(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
204}
205
b02cb41c
LP
206int config_parse_unit_string_printf(
207 const char *unit,
208 const char *filename,
209 unsigned line,
210 const char *section,
211 unsigned section_line,
212 const char *lvalue,
213 int ltype,
214 const char *rvalue,
215 void *data,
216 void *userdata) {
932921b5 217
74051b9b 218 _cleanup_free_ char *k = NULL;
47538b76 219 const Unit *u = userdata;
19f6d710 220 int r;
932921b5
LP
221
222 assert(filename);
223 assert(lvalue);
224 assert(rvalue);
f2d3769a 225 assert(u);
932921b5 226
19f6d710 227 r = unit_full_printf(u, rvalue, &k);
b02cb41c 228 if (r < 0) {
323dda78 229 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
b02cb41c
LP
230 return 0;
231 }
932921b5 232
b02cb41c 233 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
932921b5
LP
234}
235
12ca818f
LP
236int config_parse_unit_strv_printf(
237 const char *unit,
238 const char *filename,
239 unsigned line,
240 const char *section,
241 unsigned section_line,
242 const char *lvalue,
243 int ltype,
244 const char *rvalue,
245 void *data,
246 void *userdata) {
8fef7659 247
47538b76 248 const Unit *u = userdata;
74051b9b 249 _cleanup_free_ char *k = NULL;
19f6d710 250 int r;
8fef7659
LP
251
252 assert(filename);
253 assert(lvalue);
254 assert(rvalue);
255 assert(u);
256
19f6d710 257 r = unit_full_printf(u, rvalue, &k);
12ca818f 258 if (r < 0) {
323dda78 259 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
12ca818f
LP
260 return 0;
261 }
8fef7659 262
12ca818f 263 return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
8fef7659
LP
264}
265
5f5d8eab
LP
266int config_parse_unit_path_printf(
267 const char *unit,
268 const char *filename,
269 unsigned line,
270 const char *section,
271 unsigned section_line,
272 const char *lvalue,
273 int ltype,
274 const char *rvalue,
275 void *data,
276 void *userdata) {
6ea832a2 277
74051b9b 278 _cleanup_free_ char *k = NULL;
47538b76 279 const Unit *u = userdata;
19f6d710 280 int r;
2c75fb73 281 bool fatal = ltype;
6ea832a2
LP
282
283 assert(filename);
284 assert(lvalue);
285 assert(rvalue);
6ea832a2
LP
286 assert(u);
287
e3c3d676
ZJS
288 /* Let's not bother with anything that is too long */
289 if (strlen(rvalue) >= PATH_MAX) {
323dda78 290 log_syntax(unit, fatal ? LOG_ERR : LOG_WARNING, filename, line, 0,
e3c3d676
ZJS
291 "%s value too long%s.",
292 lvalue, fatal ? "" : ", ignoring");
293 return fatal ? -ENAMETOOLONG : 0;
294 }
295
19f6d710 296 r = unit_full_printf(u, rvalue, &k);
811ba7a0 297 if (r < 0) {
323dda78 298 log_syntax(unit, fatal ? LOG_ERR : LOG_WARNING, filename, line, r,
063c4b1a 299 "Failed to resolve unit specifiers in '%s'%s: %m",
e3c3d676 300 rvalue, fatal ? "" : ", ignoring");
2c75fb73 301 return fatal ? -ENOEXEC : 0;
811ba7a0 302 }
6ea832a2 303
811ba7a0
LP
304 return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
305}
306
307int config_parse_unit_path_strv_printf(
308 const char *unit,
309 const char *filename,
310 unsigned line,
311 const char *section,
312 unsigned section_line,
313 const char *lvalue,
314 int ltype,
315 const char *rvalue,
316 void *data,
317 void *userdata) {
318
a2a5291b 319 char ***x = data;
47538b76 320 const Unit *u = userdata;
811ba7a0
LP
321 int r;
322
323 assert(filename);
324 assert(lvalue);
325 assert(rvalue);
326 assert(u);
327
499295fb 328 if (isempty(rvalue)) {
9f2d41a6 329 *x = strv_free(*x);
499295fb
YW
330 return 0;
331 }
332
323dda78 333 for (const char *p = rvalue;;) {
035fe294 334 _cleanup_free_ char *word = NULL, *k = NULL;
811ba7a0 335
4ec85141 336 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
035fe294
ZJS
337 if (r == 0)
338 return 0;
339 if (r == -ENOMEM)
340 return log_oom();
341 if (r < 0) {
342 log_syntax(unit, LOG_WARNING, filename, line, r,
343 "Invalid syntax, ignoring: %s", rvalue);
344 return 0;
345 }
811ba7a0 346
035fe294 347 r = unit_full_printf(u, word, &k);
811ba7a0 348 if (r < 0) {
323dda78 349 log_syntax(unit, LOG_WARNING, filename, line, r,
063c4b1a 350 "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
811ba7a0
LP
351 return 0;
352 }
353
2f4d31c1
YW
354 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
355 if (r < 0)
811ba7a0 356 return 0;
811ba7a0 357
7d2c9c6b 358 r = strv_consume(x, TAKE_PTR(k));
811ba7a0
LP
359 if (r < 0)
360 return log_oom();
811ba7a0 361 }
6ea832a2
LP
362}
363
4a66b5c9
LP
364static int patch_var_run(
365 const char *unit,
366 const char *filename,
367 unsigned line,
368 const char *lvalue,
369 char **path) {
370
371 const char *e;
372 char *z;
373
374 e = path_startswith(*path, "/var/run/");
375 if (!e)
376 return 0;
377
378 z = path_join("/run/", e);
379 if (!z)
380 return log_oom();
381
382 log_syntax(unit, LOG_NOTICE, filename, line, 0,
383 "%s= references a path below legacy directory /var/run/, updating %s → %s; "
384 "please update the unit file accordingly.", lvalue, *path, z);
385
386 free_and_replace(*path, z);
387
388 return 1;
389}
390
391int config_parse_socket_listen(
392 const char *unit,
393 const char *filename,
394 unsigned line,
395 const char *section,
396 unsigned section_line,
397 const char *lvalue,
398 int ltype,
399 const char *rvalue,
400 void *data,
401 void *userdata) {
42f4e3c4 402
b1389b0d
ZJS
403 _cleanup_free_ SocketPort *p = NULL;
404 SocketPort *tail;
542563ba 405 Socket *s;
19f6d710 406 int r;
16354eff 407
42f4e3c4
LP
408 assert(filename);
409 assert(lvalue);
410 assert(rvalue);
411 assert(data);
412
595ed347 413 s = SOCKET(data);
542563ba 414
74051b9b
LP
415 if (isempty(rvalue)) {
416 /* An empty assignment removes all ports */
417 socket_free_ports(s);
418 return 0;
419 }
420
7f110ff9
LP
421 p = new0(SocketPort, 1);
422 if (!p)
74051b9b 423 return log_oom();
916abb21 424
74051b9b 425 if (ltype != SOCKET_SOCKET) {
2f4d31c1 426 _cleanup_free_ char *k = NULL;
916abb21 427
2f4d31c1 428 r = unit_full_printf(UNIT(s), rvalue, &k);
19f6d710 429 if (r < 0) {
323dda78 430 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
12ca818f 431 return 0;
916abb21
LP
432 }
433
2f4d31c1
YW
434 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
435 if (r < 0)
436 return 0;
437
4a66b5c9
LP
438 if (ltype == SOCKET_FIFO) {
439 r = patch_var_run(unit, filename, line, lvalue, &k);
440 if (r < 0)
441 return r;
442 }
443
2f4d31c1
YW
444 free_and_replace(p->path, k);
445 p->type = ltype;
916abb21 446
7a22745a 447 } else if (streq(lvalue, "ListenNetlink")) {
74051b9b 448 _cleanup_free_ char *k = NULL;
1fd45a90 449
19f6d710 450 r = unit_full_printf(UNIT(s), rvalue, &k);
12ca818f 451 if (r < 0) {
323dda78 452 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
12ca818f
LP
453 return 0;
454 }
7a22745a 455
12ca818f 456 r = socket_address_parse_netlink(&p->address, k);
1fd45a90 457 if (r < 0) {
323dda78 458 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse address value in '%s', ignoring: %m", k);
7a22745a
LP
459 return 0;
460 }
461
2f4d31c1
YW
462 p->type = SOCKET_SOCKET;
463
542563ba 464 } else {
74051b9b 465 _cleanup_free_ char *k = NULL;
1fd45a90 466
19f6d710 467 r = unit_full_printf(UNIT(s), rvalue, &k);
12ca818f 468 if (r < 0) {
323dda78 469 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
12ca818f
LP
470 return 0;
471 }
542563ba 472
4a66b5c9
LP
473 if (k[0] == '/') { /* Only for AF_UNIX file system sockets… */
474 r = patch_var_run(unit, filename, line, lvalue, &k);
475 if (r < 0)
476 return r;
477 }
478
12ca818f 479 r = socket_address_parse_and_warn(&p->address, k);
1fd45a90 480 if (r < 0) {
f847b8b7 481 if (r != -EAFNOSUPPORT)
323dda78 482 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse address value in '%s', ignoring: %m", k);
c0b34696 483 return 0;
542563ba
LP
484 }
485
486 if (streq(lvalue, "ListenStream"))
487 p->address.type = SOCK_STREAM;
488 else if (streq(lvalue, "ListenDatagram"))
489 p->address.type = SOCK_DGRAM;
490 else {
491 assert(streq(lvalue, "ListenSequentialPacket"));
492 p->address.type = SOCK_SEQPACKET;
493 }
494
495 if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
323dda78 496 log_syntax(unit, LOG_WARNING, filename, line, 0, "Address family not supported, ignoring: %s", rvalue);
c0b34696 497 return 0;
542563ba 498 }
2f4d31c1
YW
499
500 p->type = SOCKET_SOCKET;
16354eff
LP
501 }
502
542563ba 503 p->fd = -1;
15087cdb
PS
504 p->auxiliary_fds = NULL;
505 p->n_auxiliary_fds = 0;
2e41a51e 506 p->socket = s;
49f91047 507
533f8a67
YW
508 LIST_FIND_TAIL(port, s->ports, tail);
509 LIST_INSERT_AFTER(port, s->ports, tail, p);
510
b1389b0d 511 p = NULL;
542563ba 512
16354eff 513 return 0;
42f4e3c4
LP
514}
515
41bf0590
LP
516int config_parse_exec_nice(
517 const char *unit,
518 const char *filename,
519 unsigned line,
520 const char *section,
521 unsigned section_line,
522 const char *lvalue,
523 int ltype,
524 const char *rvalue,
525 void *data,
526 void *userdata) {
034c6ed7 527
fb33a393 528 ExecContext *c = data;
e8e581bf 529 int priority, r;
034c6ed7
LP
530
531 assert(filename);
532 assert(lvalue);
533 assert(rvalue);
534 assert(data);
535
de5e6038
YW
536 if (isempty(rvalue)) {
537 c->nice_set = false;
538 return 0;
539 }
540
41bf0590 541 r = parse_nice(rvalue, &priority);
e8e581bf 542 if (r < 0) {
41bf0590 543 if (r == -ERANGE)
323dda78 544 log_syntax(unit, LOG_WARNING, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue);
41bf0590 545 else
323dda78 546 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse nice priority '%s', ignoring: %m", rvalue);
c0b34696 547 return 0;
034c6ed7
LP
548 }
549
fb33a393 550 c->nice = priority;
71155933 551 c->nice_set = true;
fb33a393 552
034c6ed7
LP
553 return 0;
554}
555
e9eb2c02
LP
556int config_parse_exec_oom_score_adjust(
557 const char* unit,
558 const char *filename,
559 unsigned line,
560 const char *section,
561 unsigned section_line,
562 const char *lvalue,
563 int ltype,
564 const char *rvalue,
565 void *data,
566 void *userdata) {
034c6ed7 567
fb33a393 568 ExecContext *c = data;
e8e581bf 569 int oa, r;
034c6ed7
LP
570
571 assert(filename);
572 assert(lvalue);
573 assert(rvalue);
574 assert(data);
575
e9eb2c02
LP
576 if (isempty(rvalue)) {
577 c->oom_score_adjust_set = false;
c0b34696 578 return 0;
034c6ed7
LP
579 }
580
e9eb2c02 581 r = parse_oom_score_adjust(rvalue, &oa);
e9eb2c02 582 if (r < 0) {
063c4b1a 583 if (r == -ERANGE)
323dda78 584 log_syntax(unit, LOG_WARNING, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue);
063c4b1a 585 else
323dda78 586 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse the OOM score adjust value '%s', ignoring: %m", rvalue);
c0b34696 587 return 0;
034c6ed7
LP
588 }
589
dd6c17b1
LP
590 c->oom_score_adjust = oa;
591 c->oom_score_adjust_set = true;
fb33a393 592
034c6ed7
LP
593 return 0;
594}
595
ad21e542
ZJS
596int config_parse_exec_coredump_filter(
597 const char* unit,
598 const char *filename,
599 unsigned line,
600 const char *section,
601 unsigned section_line,
602 const char *lvalue,
603 int ltype,
604 const char *rvalue,
605 void *data,
606 void *userdata) {
607
608 ExecContext *c = data;
609 int r;
610
611 assert(filename);
612 assert(lvalue);
613 assert(rvalue);
614 assert(data);
615
616 if (isempty(rvalue)) {
617 c->coredump_filter = 0;
618 c->coredump_filter_set = false;
619 return 0;
620 }
621
622 uint64_t f;
623 r = coredump_filter_mask_from_string(rvalue, &f);
624 if (r < 0) {
625 log_syntax(unit, LOG_WARNING, filename, line, r,
626 "Failed to parse the CoredumpFilter=%s, ignoring: %m", rvalue);
627 return 0;
628 }
629
630 c->coredump_filter |= f;
631 c->oom_score_adjust_set = true;
632 return 0;
633}
634
d068765b
LP
635int config_parse_kill_mode(
636 const char* unit,
637 const char *filename,
638 unsigned line,
639 const char *section,
640 unsigned section_line,
641 const char *lvalue,
642 int ltype,
643 const char *rvalue,
644 void *data,
645 void *userdata) {
646
647 KillMode *k = data, m;
648
649 assert(filename);
650 assert(lvalue);
651 assert(rvalue);
652 assert(data);
653
654 if (isempty(rvalue)) {
655 *k = KILL_CONTROL_GROUP;
656 return 0;
657 }
658
659 m = kill_mode_from_string(rvalue);
660 if (m < 0) {
661 log_syntax(unit, LOG_WARNING, filename, line, 0,
662 "Failed to parse kill mode specification, ignoring: %s", rvalue);
663 return 0;
664 }
665
666 if (m == KILL_NONE)
667 log_syntax(unit, LOG_WARNING, filename, line, 0,
668 "Unit configured to use KillMode=none. "
15e6a6e8 669 "This is unsafe, as it disables systemd's process lifecycle management for the service. "
d068765b
LP
670 "Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. "
671 "Support for KillMode=none is deprecated and will eventually be removed.");
672
673 *k = m;
674 return 0;
675}
676
527b7a42
LP
677int config_parse_exec(
678 const char *unit,
679 const char *filename,
680 unsigned line,
681 const char *section,
682 unsigned section_line,
683 const char *lvalue,
684 int ltype,
685 const char *rvalue,
686 void *data,
687 void *userdata) {
034c6ed7 688
46a0d98a 689 ExecCommand **e = data;
47538b76 690 const Unit *u = userdata;
46a0d98a
FB
691 const char *p;
692 bool semicolon;
7f110ff9 693 int r;
034c6ed7
LP
694
695 assert(filename);
696 assert(lvalue);
697 assert(rvalue);
61e5d8ed 698 assert(e);
034c6ed7 699
74051b9b 700 e += ltype;
c83f1f30 701
74051b9b
LP
702 if (isempty(rvalue)) {
703 /* An empty assignment resets the list */
f1acf85a 704 *e = exec_command_free_list(*e);
74051b9b
LP
705 return 0;
706 }
707
bd1b973f 708 p = rvalue;
46a0d98a 709 do {
dea7b6b0 710 _cleanup_free_ char *path = NULL, *firstword = NULL;
165a31c0
LP
711 ExecCommandFlags flags = 0;
712 bool ignore = false, separate_argv0 = false;
dea7b6b0 713 _cleanup_free_ ExecCommand *nce = NULL;
46a0d98a
FB
714 _cleanup_strv_free_ char **n = NULL;
715 size_t nlen = 0, nbufsize = 0;
5125e762 716 const char *f;
6c666e26 717
46a0d98a
FB
718 semicolon = false;
719
4ec85141 720 r = extract_first_word_and_warn(&p, &firstword, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
46a0d98a
FB
721 if (r <= 0)
722 return 0;
6c666e26 723
598c47c8
ZJS
724 /* A lone ";" is a separator. Let's make sure we don't treat it as an executable name. */
725 if (streq(firstword, ";")) {
726 semicolon = true;
727 continue;
728 }
729
46a0d98a 730 f = firstword;
007f48bb 731 for (;;) {
165a31c0
LP
732 /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
733 * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of
7ca69792
AZ
734 * argv[0]; if it's prefixed with :, we will not do environment variable substitution;
735 * if it's prefixed with +, it will be run with full privileges and no sandboxing; if
165a31c0
LP
736 * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if
737 * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient
738 * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most
739 * other sandboxing, with some special exceptions for changing UID.
740 *
741 * The idea is that '!!' may be used to write services that can take benefit of systemd's
742 * UID/GID dropping if the kernel supports ambient creds, but provide an automatic fallback to
743 * privilege dropping within the daemon if the kernel does not offer that. */
744
745 if (*f == '-' && !(flags & EXEC_COMMAND_IGNORE_FAILURE)) {
746 flags |= EXEC_COMMAND_IGNORE_FAILURE;
46a0d98a 747 ignore = true;
165a31c0 748 } else if (*f == '@' && !separate_argv0)
46a0d98a 749 separate_argv0 = true;
7ca69792
AZ
750 else if (*f == ':' && !(flags & EXEC_COMMAND_NO_ENV_EXPAND))
751 flags |= EXEC_COMMAND_NO_ENV_EXPAND;
165a31c0
LP
752 else if (*f == '+' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC)))
753 flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
754 else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC)))
755 flags |= EXEC_COMMAND_NO_SETUID;
756 else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))) {
757 flags &= ~EXEC_COMMAND_NO_SETUID;
758 flags |= EXEC_COMMAND_AMBIENT_MAGIC;
759 } else
46a0d98a 760 break;
313cefa1 761 f++;
61e5d8ed 762 }
46a0d98a 763
5125e762
LP
764 r = unit_full_printf(u, f, &path);
765 if (r < 0) {
323dda78 766 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
063c4b1a 767 "Failed to resolve unit specifiers in '%s'%s: %m",
bb28e684
ZJS
768 f, ignore ? ", ignoring" : "");
769 return ignore ? 0 : -ENOEXEC;
5125e762
LP
770 }
771
772 if (isempty(path)) {
46a0d98a 773 /* First word is either "-" or "@" with no command. */
323dda78 774 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
063c4b1a 775 "Empty path in command line%s: '%s'",
bb28e684
ZJS
776 ignore ? ", ignoring" : "", rvalue);
777 return ignore ? 0 : -ENOEXEC;
b2fadec6 778 }
5125e762 779 if (!string_is_safe(path)) {
323dda78 780 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
5008da1e
ZJS
781 "Executable name contains special characters%s: %s",
782 ignore ? ", ignoring" : "", path);
bb28e684 783 return ignore ? 0 : -ENOEXEC;
46a0d98a 784 }
5125e762 785 if (endswith(path, "/")) {
323dda78 786 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
bb28e684 787 "Executable path specifies a directory%s: %s",
5008da1e 788 ignore ? ", ignoring" : "", path);
bb28e684 789 return ignore ? 0 : -ENOEXEC;
46a0d98a 790 }
61e5d8ed 791
9f71ba8d
ZJS
792 if (!path_is_absolute(path) && !filename_is_valid(path)) {
793 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
794 "Neither a valid executable name nor an absolute path%s: %s",
795 ignore ? ", ignoring" : "", path);
796 return ignore ? 0 : -ENOEXEC;
5008da1e
ZJS
797 }
798
46a0d98a 799 if (!separate_argv0) {
5125e762
LP
800 char *w = NULL;
801
46a0d98a
FB
802 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
803 return log_oom();
5125e762
LP
804
805 w = strdup(path);
806 if (!w)
46a0d98a 807 return log_oom();
5125e762 808 n[nlen++] = w;
46a0d98a
FB
809 n[nlen] = NULL;
810 }
7f110ff9 811
858d36c1 812 path_simplify(path, false);
46a0d98a 813
4b1c1753 814 while (!isempty(p)) {
5125e762 815 _cleanup_free_ char *word = NULL, *resolved = NULL;
46a0d98a
FB
816
817 /* Check explicitly for an unquoted semicolon as
818 * command separator token. */
819 if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
313cefa1 820 p++;
46a0d98a
FB
821 p += strspn(p, WHITESPACE);
822 semicolon = true;
823 break;
c8539536 824 }
7f110ff9 825
5125e762
LP
826 /* Check for \; explicitly, to not confuse it with \\; or "\;" or "\\;" etc.
827 * extract_first_word() would return the same for all of those. */
46a0d98a 828 if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
5125e762
LP
829 char *w;
830
46a0d98a
FB
831 p += 2;
832 p += strspn(p, WHITESPACE);
5125e762 833
46a0d98a
FB
834 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
835 return log_oom();
5125e762
LP
836
837 w = strdup(";");
838 if (!w)
46a0d98a 839 return log_oom();
5125e762 840 n[nlen++] = w;
46a0d98a
FB
841 n[nlen] = NULL;
842 continue;
61e5d8ed 843 }
c8539536 844
4ec85141 845 r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
46a0d98a
FB
846 if (r == 0)
847 break;
5125e762 848 if (r < 0)
bb28e684 849 return ignore ? 0 : -ENOEXEC;
5125e762
LP
850
851 r = unit_full_printf(u, word, &resolved);
852 if (r < 0) {
323dda78 853 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
063c4b1a 854 "Failed to resolve unit specifiers in %s%s: %m",
bb28e684
ZJS
855 word, ignore ? ", ignoring" : "");
856 return ignore ? 0 : -ENOEXEC;
5125e762 857 }
46a0d98a
FB
858
859 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
860 return log_oom();
1cc6c93a
YW
861
862 n[nlen++] = TAKE_PTR(resolved);
46a0d98a 863 n[nlen] = NULL;
61e5d8ed
LP
864 }
865
46a0d98a 866 if (!n || !n[0]) {
323dda78 867 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
bb28e684
ZJS
868 "Empty executable name or zeroeth argument%s: %s",
869 ignore ? ", ignoring" : "", rvalue);
870 return ignore ? 0 : -ENOEXEC;
7f110ff9 871 }
6c666e26 872
7f110ff9 873 nce = new0(ExecCommand, 1);
46a0d98a
FB
874 if (!nce)
875 return log_oom();
61e5d8ed 876
1cc6c93a
YW
877 nce->argv = TAKE_PTR(n);
878 nce->path = TAKE_PTR(path);
165a31c0 879 nce->flags = flags;
034c6ed7 880
61e5d8ed 881 exec_command_append_list(e, nce);
01f78473 882
46a0d98a 883 /* Do not _cleanup_free_ these. */
46a0d98a 884 nce = NULL;
034c6ed7 885
46a0d98a
FB
886 rvalue = p;
887 } while (semicolon);
034c6ed7 888
46a0d98a 889 return 0;
034c6ed7
LP
890}
891
d31645ad
LP
892int config_parse_socket_bindtodevice(
893 const char* unit,
894 const char *filename,
895 unsigned line,
896 const char *section,
897 unsigned section_line,
898 const char *lvalue,
899 int ltype,
900 const char *rvalue,
901 void *data,
902 void *userdata) {
acbb0225
LP
903
904 Socket *s = data;
acbb0225
LP
905
906 assert(filename);
907 assert(lvalue);
908 assert(rvalue);
909 assert(data);
910
063c4b1a
YW
911 if (isempty(rvalue) || streq(rvalue, "*")) {
912 s->bind_to_device = mfree(s->bind_to_device);
913 return 0;
914 }
d31645ad 915
063c4b1a 916 if (!ifname_valid(rvalue)) {
323dda78 917 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", rvalue);
063c4b1a
YW
918 return 0;
919 }
acbb0225 920
3c381a67
YW
921 if (free_and_strdup(&s->bind_to_device, rvalue) < 0)
922 return log_oom();
acbb0225
LP
923
924 return 0;
925}
926
9bd6a50e
LP
927int config_parse_exec_input(
928 const char *unit,
929 const char *filename,
930 unsigned line,
931 const char *section,
932 unsigned section_line,
933 const char *lvalue,
934 int ltype,
935 const char *rvalue,
936 void *data,
937 void *userdata) {
52c239d7 938
52c239d7 939 ExecContext *c = data;
47538b76 940 const Unit *u = userdata;
2038c3f5
LP
941 const char *n;
942 ExecInput ei;
52c239d7
LB
943 int r;
944
945 assert(data);
946 assert(filename);
947 assert(line);
948 assert(rvalue);
949
2038c3f5
LP
950 n = startswith(rvalue, "fd:");
951 if (n) {
952 _cleanup_free_ char *resolved = NULL;
953
954 r = unit_full_printf(u, n, &resolved);
323dda78
YW
955 if (r < 0) {
956 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", n);
957 return 0;
958 }
2038c3f5
LP
959
960 if (isempty(resolved))
961 resolved = mfree(resolved);
962 else if (!fdname_is_valid(resolved)) {
323dda78
YW
963 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid file descriptor name, ignoring: %s", resolved);
964 return 0;
52c239d7 965 }
9bd6a50e 966
2038c3f5
LP
967 free_and_replace(c->stdio_fdname[STDIN_FILENO], resolved);
968
969 ei = EXEC_INPUT_NAMED_FD;
970
971 } else if ((n = startswith(rvalue, "file:"))) {
972 _cleanup_free_ char *resolved = NULL;
973
974 r = unit_full_printf(u, n, &resolved);
323dda78
YW
975 if (r < 0) {
976 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", n);
977 return 0;
978 }
9bd6a50e 979
2f4d31c1
YW
980 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
981 if (r < 0)
323dda78 982 return 0;
2038c3f5
LP
983
984 free_and_replace(c->stdio_file[STDIN_FILENO], resolved);
985
986 ei = EXEC_INPUT_FILE;
9bd6a50e 987
52c239d7 988 } else {
9bd6a50e
LP
989 ei = exec_input_from_string(rvalue);
990 if (ei < 0) {
323dda78 991 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse input specifier, ignoring: %s", rvalue);
9bd6a50e
LP
992 return 0;
993 }
52c239d7 994 }
9bd6a50e 995
2038c3f5 996 c->std_input = ei;
9bd6a50e 997 return 0;
52c239d7
LB
998}
999
08f3be7a
LP
1000int config_parse_exec_input_text(
1001 const char *unit,
1002 const char *filename,
1003 unsigned line,
1004 const char *section,
1005 unsigned section_line,
1006 const char *lvalue,
1007 int ltype,
1008 const char *rvalue,
1009 void *data,
1010 void *userdata) {
1011
1012 _cleanup_free_ char *unescaped = NULL, *resolved = NULL;
1013 ExecContext *c = data;
47538b76 1014 const Unit *u = userdata;
08f3be7a
LP
1015 size_t sz;
1016 void *p;
1017 int r;
1018
1019 assert(data);
1020 assert(filename);
1021 assert(line);
1022 assert(rvalue);
1023
1024 if (isempty(rvalue)) {
1025 /* Reset if the empty string is assigned */
1026 c->stdin_data = mfree(c->stdin_data);
1027 c->stdin_data_size = 0;
52c239d7
LB
1028 return 0;
1029 }
08f3be7a
LP
1030
1031 r = cunescape(rvalue, 0, &unescaped);
323dda78
YW
1032 if (r < 0) {
1033 log_syntax(unit, LOG_WARNING, filename, line, r,
1034 "Failed to decode C escaped text '%s', ignoring: %m", rvalue);
1035 return 0;
1036 }
08f3be7a
LP
1037
1038 r = unit_full_printf(u, unescaped, &resolved);
323dda78
YW
1039 if (r < 0) {
1040 log_syntax(unit, LOG_WARNING, filename, line, r,
1041 "Failed to resolve unit specifiers in '%s', ignoring: %m", unescaped);
1042 return 0;
1043 }
08f3be7a
LP
1044
1045 sz = strlen(resolved);
1046 if (c->stdin_data_size + sz + 1 < c->stdin_data_size || /* check for overflow */
1047 c->stdin_data_size + sz + 1 > EXEC_STDIN_DATA_MAX) {
323dda78
YW
1048 log_syntax(unit, LOG_WARNING, filename, line, 0,
1049 "Standard input data too large (%zu), maximum of %zu permitted, ignoring.",
1050 c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
1051 return 0;
08f3be7a
LP
1052 }
1053
1054 p = realloc(c->stdin_data, c->stdin_data_size + sz + 1);
1055 if (!p)
1056 return log_oom();
1057
1058 *((char*) mempcpy((char*) p + c->stdin_data_size, resolved, sz)) = '\n';
1059
1060 c->stdin_data = p;
1061 c->stdin_data_size += sz + 1;
1062
1063 return 0;
52c239d7
LB
1064}
1065
08f3be7a
LP
1066int config_parse_exec_input_data(
1067 const char *unit,
1068 const char *filename,
1069 unsigned line,
1070 const char *section,
1071 unsigned section_line,
1072 const char *lvalue,
1073 int ltype,
1074 const char *rvalue,
1075 void *data,
1076 void *userdata) {
1077
08f3be7a
LP
1078 _cleanup_free_ void *p = NULL;
1079 ExecContext *c = data;
1080 size_t sz;
1081 void *q;
1082 int r;
1083
1084 assert(data);
1085 assert(filename);
1086 assert(line);
1087 assert(rvalue);
1088
1089 if (isempty(rvalue)) {
1090 /* Reset if the empty string is assigned */
1091 c->stdin_data = mfree(c->stdin_data);
1092 c->stdin_data_size = 0;
1093 return 0;
1094 }
1095
081f36d8 1096 r = unbase64mem(rvalue, (size_t) -1, &p, &sz);
323dda78
YW
1097 if (r < 0) {
1098 log_syntax(unit, LOG_WARNING, filename, line, r,
1099 "Failed to decode base64 data, ignoring: %s", rvalue);
1100 return 0;
1101 }
08f3be7a
LP
1102
1103 assert(sz > 0);
1104
1105 if (c->stdin_data_size + sz < c->stdin_data_size || /* check for overflow */
1106 c->stdin_data_size + sz > EXEC_STDIN_DATA_MAX) {
323dda78
YW
1107 log_syntax(unit, LOG_WARNING, filename, line, 0,
1108 "Standard input data too large (%zu), maximum of %zu permitted, ignoring.",
1109 c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
1110 return 0;
08f3be7a
LP
1111 }
1112
1113 q = realloc(c->stdin_data, c->stdin_data_size + sz);
1114 if (!q)
1115 return log_oom();
1116
1117 memcpy((uint8_t*) q + c->stdin_data_size, p, sz);
1118
1119 c->stdin_data = q;
1120 c->stdin_data_size += sz;
1121
1122 return 0;
1123}
1124
2038c3f5
LP
1125int config_parse_exec_output(
1126 const char *unit,
1127 const char *filename,
1128 unsigned line,
1129 const char *section,
1130 unsigned section_line,
1131 const char *lvalue,
1132 int ltype,
1133 const char *rvalue,
1134 void *data,
1135 void *userdata) {
1136
1137 _cleanup_free_ char *resolved = NULL;
1138 const char *n;
52c239d7 1139 ExecContext *c = data;
47538b76 1140 const Unit *u = userdata;
f3dc6af2 1141 bool obsolete = false;
52c239d7 1142 ExecOutput eo;
52c239d7
LB
1143 int r;
1144
1145 assert(data);
1146 assert(filename);
1147 assert(line);
1148 assert(lvalue);
1149 assert(rvalue);
1150
2038c3f5
LP
1151 n = startswith(rvalue, "fd:");
1152 if (n) {
1153 r = unit_full_printf(u, n, &resolved);
323dda78
YW
1154 if (r < 0) {
1155 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n);
1156 return 0;
1157 }
2038c3f5
LP
1158
1159 if (isempty(resolved))
1160 resolved = mfree(resolved);
1161 else if (!fdname_is_valid(resolved)) {
323dda78
YW
1162 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid file descriptor name, ignoring: %s", resolved);
1163 return 0;
52c239d7 1164 }
2038c3f5 1165
52c239d7 1166 eo = EXEC_OUTPUT_NAMED_FD;
2038c3f5 1167
f3dc6af2
LP
1168 } else if (streq(rvalue, "syslog")) {
1169 eo = EXEC_OUTPUT_JOURNAL;
1170 obsolete = true;
1171
1172 } else if (streq(rvalue, "syslog+console")) {
1173 eo = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
1174 obsolete = true;
1175
2038c3f5
LP
1176 } else if ((n = startswith(rvalue, "file:"))) {
1177
1178 r = unit_full_printf(u, n, &resolved);
323dda78
YW
1179 if (r < 0) {
1180 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", n);
1181 return 0;
1182 }
2038c3f5 1183
2f4d31c1
YW
1184 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1185 if (r < 0)
323dda78 1186 return 0;
2038c3f5
LP
1187
1188 eo = EXEC_OUTPUT_FILE;
1189
566b7d23
ZD
1190 } else if ((n = startswith(rvalue, "append:"))) {
1191
1192 r = unit_full_printf(u, n, &resolved);
323dda78
YW
1193 if (r < 0) {
1194 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", n);
1195 return 0;
1196 }
566b7d23
ZD
1197
1198 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue);
1199 if (r < 0)
323dda78 1200 return 0;
566b7d23
ZD
1201
1202 eo = EXEC_OUTPUT_FILE_APPEND;
52c239d7
LB
1203 } else {
1204 eo = exec_output_from_string(rvalue);
2038c3f5 1205 if (eo < 0) {
323dda78 1206 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse output specifier, ignoring: %s", rvalue);
52c239d7
LB
1207 return 0;
1208 }
1209 }
1210
f3dc6af2
LP
1211 if (obsolete)
1212 log_syntax(unit, LOG_NOTICE, filename, line, 0,
1213 "Standard output type %s is obsolete, automatically updating to %s. Please update your unit file, and consider removing the setting altogether.",
1214 rvalue, exec_output_to_string(eo));
1215
52c239d7 1216 if (streq(lvalue, "StandardOutput")) {
2038c3f5
LP
1217 if (eo == EXEC_OUTPUT_NAMED_FD)
1218 free_and_replace(c->stdio_fdname[STDOUT_FILENO], resolved);
1219 else
1220 free_and_replace(c->stdio_file[STDOUT_FILENO], resolved);
1221
52c239d7 1222 c->std_output = eo;
2038c3f5 1223
52c239d7 1224 } else {
2038c3f5
LP
1225 assert(streq(lvalue, "StandardError"));
1226
1227 if (eo == EXEC_OUTPUT_NAMED_FD)
1228 free_and_replace(c->stdio_fdname[STDERR_FILENO], resolved);
1229 else
1230 free_and_replace(c->stdio_file[STDERR_FILENO], resolved);
1231
1232 c->std_error = eo;
52c239d7 1233 }
2038c3f5
LP
1234
1235 return 0;
52c239d7 1236}
87f0e418 1237
e8e581bf
ZJS
1238int config_parse_exec_io_class(const char *unit,
1239 const char *filename,
1240 unsigned line,
1241 const char *section,
71a61510 1242 unsigned section_line,
e8e581bf
ZJS
1243 const char *lvalue,
1244 int ltype,
1245 const char *rvalue,
1246 void *data,
1247 void *userdata) {
94f04347
LP
1248
1249 ExecContext *c = data;
1250 int x;
1251
1252 assert(filename);
1253 assert(lvalue);
1254 assert(rvalue);
1255 assert(data);
1256
617d253a
YW
1257 if (isempty(rvalue)) {
1258 c->ioprio_set = false;
1259 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
1260 return 0;
1261 }
1262
f8b69d1d
MS
1263 x = ioprio_class_from_string(rvalue);
1264 if (x < 0) {
323dda78 1265 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue);
c0b34696 1266 return 0;
0d87eb42 1267 }
94f04347
LP
1268
1269 c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
1270 c->ioprio_set = true;
1271
1272 return 0;
1273}
1274
e8e581bf
ZJS
1275int config_parse_exec_io_priority(const char *unit,
1276 const char *filename,
1277 unsigned line,
1278 const char *section,
71a61510 1279 unsigned section_line,
e8e581bf
ZJS
1280 const char *lvalue,
1281 int ltype,
1282 const char *rvalue,
1283 void *data,
1284 void *userdata) {
94f04347
LP
1285
1286 ExecContext *c = data;
e8e581bf 1287 int i, r;
94f04347
LP
1288
1289 assert(filename);
1290 assert(lvalue);
1291 assert(rvalue);
1292 assert(data);
1293
617d253a
YW
1294 if (isempty(rvalue)) {
1295 c->ioprio_set = false;
1296 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
1297 return 0;
1298 }
1299
7f452159
LP
1300 r = ioprio_parse_priority(rvalue, &i);
1301 if (r < 0) {
323dda78 1302 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue);
c0b34696 1303 return 0;
071830ff
LP
1304 }
1305
94f04347
LP
1306 c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
1307 c->ioprio_set = true;
1308
071830ff
LP
1309 return 0;
1310}
1311
e8e581bf
ZJS
1312int config_parse_exec_cpu_sched_policy(const char *unit,
1313 const char *filename,
1314 unsigned line,
1315 const char *section,
71a61510 1316 unsigned section_line,
e8e581bf
ZJS
1317 const char *lvalue,
1318 int ltype,
1319 const char *rvalue,
1320 void *data,
1321 void *userdata) {
9eba9da4 1322
94f04347
LP
1323 ExecContext *c = data;
1324 int x;
1325
1326 assert(filename);
1327 assert(lvalue);
1328 assert(rvalue);
1329 assert(data);
1330
b00e1a9e
YW
1331 if (isempty(rvalue)) {
1332 c->cpu_sched_set = false;
1333 c->cpu_sched_policy = SCHED_OTHER;
1334 c->cpu_sched_priority = 0;
1335 return 0;
1336 }
1337
f8b69d1d
MS
1338 x = sched_policy_from_string(rvalue);
1339 if (x < 0) {
323dda78 1340 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
c0b34696 1341 return 0;
0d87eb42 1342 }
94f04347
LP
1343
1344 c->cpu_sched_policy = x;
bb112710
HHPF
1345 /* Moving to or from real-time policy? We need to adjust the priority */
1346 c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
94f04347
LP
1347 c->cpu_sched_set = true;
1348
1349 return 0;
1350}
1351
b070c7c0
MS
1352int config_parse_numa_mask(const char *unit,
1353 const char *filename,
1354 unsigned line,
1355 const char *section,
1356 unsigned section_line,
1357 const char *lvalue,
1358 int ltype,
1359 const char *rvalue,
1360 void *data,
1361 void *userdata) {
1362 int r;
1363 NUMAPolicy *p = data;
1364
1365 assert(filename);
1366 assert(lvalue);
1367 assert(rvalue);
1368 assert(data);
1369
332d387f
MS
1370 if (streq(rvalue, "all")) {
1371 r = numa_mask_add_all(&p->nodes);
323dda78
YW
1372 if (r < 0)
1373 log_syntax(unit, LOG_WARNING, filename, line, r,
1374 "Failed to create NUMA mask representing \"all\" NUMA nodes, ignoring: %m");
332d387f
MS
1375 } else {
1376 r = parse_cpu_set_extend(rvalue, &p->nodes, true, unit, filename, line, lvalue);
323dda78
YW
1377 if (r < 0)
1378 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse NUMA node mask, ignoring: %s", rvalue);
b070c7c0
MS
1379 }
1380
323dda78 1381 return 0;
b070c7c0
MS
1382}
1383
e8e581bf
ZJS
1384int config_parse_exec_cpu_sched_prio(const char *unit,
1385 const char *filename,
1386 unsigned line,
1387 const char *section,
71a61510 1388 unsigned section_line,
e8e581bf
ZJS
1389 const char *lvalue,
1390 int ltype,
1391 const char *rvalue,
1392 void *data,
1393 void *userdata) {
9eba9da4
LP
1394
1395 ExecContext *c = data;
e8e581bf 1396 int i, min, max, r;
9eba9da4
LP
1397
1398 assert(filename);
1399 assert(lvalue);
1400 assert(rvalue);
1401 assert(data);
1402
e8e581bf
ZJS
1403 r = safe_atoi(rvalue, &i);
1404 if (r < 0) {
323dda78 1405 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse CPU scheduling priority, ignoring: %s", rvalue);
c0b34696 1406 return 0;
94f04347 1407 }
9eba9da4 1408
bb112710
HHPF
1409 /* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
1410 min = sched_get_priority_min(c->cpu_sched_policy);
1411 max = sched_get_priority_max(c->cpu_sched_policy);
1412
1413 if (i < min || i > max) {
323dda78 1414 log_syntax(unit, LOG_WARNING, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue);
bb112710
HHPF
1415 return 0;
1416 }
1417
94f04347
LP
1418 c->cpu_sched_priority = i;
1419 c->cpu_sched_set = true;
1420
1421 return 0;
1422}
1423
18d73705
LB
1424int config_parse_root_image_options(
1425 const char *unit,
1426 const char *filename,
1427 unsigned line,
1428 const char *section,
1429 unsigned section_line,
1430 const char *lvalue,
1431 int ltype,
1432 const char *rvalue,
1433 void *data,
1434 void *userdata) {
1435
1436 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
bc8d56d3
LB
1437 _cleanup_strv_free_ char **l = NULL;
1438 char **first = NULL, **second = NULL;
18d73705
LB
1439 ExecContext *c = data;
1440 const Unit *u = userdata;
18d73705
LB
1441 int r;
1442
1443 assert(filename);
1444 assert(lvalue);
1445 assert(rvalue);
1446 assert(data);
1447
1448 if (isempty(rvalue)) {
1449 c->root_image_options = mount_options_free_all(c->root_image_options);
1450 return 0;
1451 }
1452
bc8d56d3
LB
1453 r = strv_split_colon_pairs(&l, rvalue);
1454 if (r == -ENOMEM)
1455 return log_oom();
1456 if (r < 0) {
323dda78 1457 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
bc8d56d3
LB
1458 return 0;
1459 }
18d73705 1460
bc8d56d3 1461 STRV_FOREACH_PAIR(first, second, l) {
18d73705 1462 MountOptions *o = NULL;
9ece6444
LB
1463 _cleanup_free_ char *mount_options_resolved = NULL;
1464 const char *mount_options = NULL, *partition = "root";
569a0e42 1465 PartitionDesignator partition_designator;
18d73705 1466
9ece6444 1467 /* Format is either 'root:foo' or 'foo' (root is implied) */
bc8d56d3 1468 if (!isempty(*second)) {
9ece6444 1469 partition = *first;
bc8d56d3 1470 mount_options = *second;
18d73705 1471 } else
bc8d56d3 1472 mount_options = *first;
18d73705 1473
9ece6444
LB
1474 partition_designator = partition_designator_from_string(partition);
1475 if (partition_designator < 0) {
323dda78 1476 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid partition name %s, ignoring", partition);
18d73705
LB
1477 continue;
1478 }
18d73705
LB
1479 r = unit_full_printf(u, mount_options, &mount_options_resolved);
1480 if (r < 0) {
323dda78 1481 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
18d73705
LB
1482 continue;
1483 }
1484
1485 o = new(MountOptions, 1);
1486 if (!o)
1487 return log_oom();
1488 *o = (MountOptions) {
9ece6444 1489 .partition_designator = partition_designator,
18d73705
LB
1490 .options = TAKE_PTR(mount_options_resolved),
1491 };
9ece6444 1492 LIST_APPEND(mount_options, options, TAKE_PTR(o));
18d73705
LB
1493 }
1494
1495 /* empty spaces/separators only */
1496 if (LIST_IS_EMPTY(options))
1497 c->root_image_options = mount_options_free_all(c->root_image_options);
1498 else
1499 LIST_JOIN(mount_options, c->root_image_options, options);
1500
1501 return 0;
1502}
1503
0389f4fa
LB
1504int config_parse_exec_root_hash(
1505 const char *unit,
1506 const char *filename,
1507 unsigned line,
1508 const char *section,
1509 unsigned section_line,
1510 const char *lvalue,
1511 int ltype,
1512 const char *rvalue,
1513 void *data,
1514 void *userdata) {
1515
1516 _cleanup_free_ void *roothash_decoded = NULL;
1517 ExecContext *c = data;
1518 size_t roothash_decoded_size = 0;
1519 int r;
1520
1521 assert(data);
1522 assert(filename);
1523 assert(line);
1524 assert(rvalue);
1525
1526 if (isempty(rvalue)) {
1527 /* Reset if the empty string is assigned */
1528 c->root_hash_path = mfree(c->root_hash_path);
1529 c->root_hash = mfree(c->root_hash);
1530 c->root_hash_size = 0;
1531 return 0;
1532 }
1533
1534 if (path_is_absolute(rvalue)) {
1535 /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
1536 _cleanup_free_ char *p = NULL;
1537
1538 p = strdup(rvalue);
1539 if (!p)
1540 return -ENOMEM;
1541
1542 free_and_replace(c->root_hash_path, p);
1543 c->root_hash = mfree(c->root_hash);
1544 c->root_hash_size = 0;
1545 return 0;
1546 }
1547
1548 /* We have a roothash to decode, eg: RootHash=012345789abcdef */
1549 r = unhexmem(rvalue, strlen(rvalue), &roothash_decoded, &roothash_decoded_size);
323dda78
YW
1550 if (r < 0) {
1551 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode RootHash=, ignoring: %s", rvalue);
1552 return 0;
1553 }
1554 if (roothash_decoded_size < sizeof(sd_id128_t)) {
1555 log_syntax(unit, LOG_WARNING, filename, line, 0, "RootHash= is too short, ignoring: %s", rvalue);
1556 return 0;
1557 }
0389f4fa
LB
1558
1559 free_and_replace(c->root_hash, roothash_decoded);
1560 c->root_hash_size = roothash_decoded_size;
1561 c->root_hash_path = mfree(c->root_hash_path);
1562
1563 return 0;
1564}
1565
d4d55b0d
LB
1566int config_parse_exec_root_hash_sig(
1567 const char *unit,
1568 const char *filename,
1569 unsigned line,
1570 const char *section,
1571 unsigned section_line,
1572 const char *lvalue,
1573 int ltype,
1574 const char *rvalue,
1575 void *data,
1576 void *userdata) {
1577
1578 _cleanup_free_ void *roothash_sig_decoded = NULL;
1579 char *value;
1580 ExecContext *c = data;
1581 size_t roothash_sig_decoded_size = 0;
1582 int r;
1583
1584 assert(data);
1585 assert(filename);
1586 assert(line);
1587 assert(rvalue);
1588
1589 if (isempty(rvalue)) {
1590 /* Reset if the empty string is assigned */
1591 c->root_hash_sig_path = mfree(c->root_hash_sig_path);
1592 c->root_hash_sig = mfree(c->root_hash_sig);
1593 c->root_hash_sig_size = 0;
1594 return 0;
1595 }
1596
1597 if (path_is_absolute(rvalue)) {
1598 /* We have the path to a roothash signature to load and decode, eg: RootHashSignature=/foo/bar.roothash.p7s */
1599 _cleanup_free_ char *p = NULL;
1600
1601 p = strdup(rvalue);
1602 if (!p)
323dda78 1603 return log_oom();
d4d55b0d
LB
1604
1605 free_and_replace(c->root_hash_sig_path, p);
1606 c->root_hash_sig = mfree(c->root_hash_sig);
1607 c->root_hash_sig_size = 0;
1608 return 0;
1609 }
1610
323dda78
YW
1611 if (!(value = startswith(rvalue, "base64:"))) {
1612 log_syntax(unit, LOG_WARNING, filename, line, 0,
1613 "Failed to decode RootHashSignature=, not a path but doesn't start with 'base64:', ignoring: %s", rvalue);
1614 return 0;
1615 }
d4d55b0d
LB
1616
1617 /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
1618 r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
323dda78
YW
1619 if (r < 0) {
1620 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to decode RootHashSignature=, ignoring: %s", rvalue);
1621 return 0;
1622 }
d4d55b0d
LB
1623
1624 free_and_replace(c->root_hash_sig, roothash_sig_decoded);
1625 c->root_hash_sig_size = roothash_sig_decoded_size;
1626 c->root_hash_sig_path = mfree(c->root_hash_sig_path);
1627
1628 return 0;
1629}
1630
e8e581bf
ZJS
1631int config_parse_exec_cpu_affinity(const char *unit,
1632 const char *filename,
1633 unsigned line,
1634 const char *section,
71a61510 1635 unsigned section_line,
e8e581bf
ZJS
1636 const char *lvalue,
1637 int ltype,
1638 const char *rvalue,
1639 void *data,
1640 void *userdata) {
94f04347
LP
1641
1642 ExecContext *c = data;
e2b2fb7f 1643 int r;
94f04347
LP
1644
1645 assert(filename);
1646 assert(lvalue);
1647 assert(rvalue);
1648 assert(data);
1649
e2b2fb7f
MS
1650 if (streq(rvalue, "numa")) {
1651 c->cpu_affinity_from_numa = true;
1652 cpu_set_reset(&c->cpu_set);
1653
1654 return 0;
1655 }
1656
1657 r = parse_cpu_set_extend(rvalue, &c->cpu_set, true, unit, filename, line, lvalue);
1658 if (r >= 0)
1659 c->cpu_affinity_from_numa = false;
1660
1661 return r;
94f04347
LP
1662}
1663
a103496c 1664int config_parse_capability_set(
65dce264
LP
1665 const char *unit,
1666 const char *filename,
1667 unsigned line,
1668 const char *section,
1669 unsigned section_line,
1670 const char *lvalue,
1671 int ltype,
1672 const char *rvalue,
1673 void *data,
1674 void *userdata) {
94f04347 1675
a103496c
IP
1676 uint64_t *capability_set = data;
1677 uint64_t sum = 0, initial = 0;
260abb78 1678 bool invert = false;
dd1f5bd0 1679 int r;
94f04347
LP
1680
1681 assert(filename);
1682 assert(lvalue);
1683 assert(rvalue);
1684 assert(data);
1685
260abb78
LP
1686 if (rvalue[0] == '~') {
1687 invert = true;
1688 rvalue++;
1689 }
1690
70d54d90 1691 if (streq(lvalue, "CapabilityBoundingSet"))
a103496c 1692 initial = CAP_ALL; /* initialized to all bits on */
755d4b67 1693 /* else "AmbientCapabilities" initialized to all bits off */
260abb78 1694
dd1f5bd0 1695 r = capability_set_from_string(rvalue, &sum);
dd1f5bd0 1696 if (r < 0) {
323dda78 1697 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= specifier '%s', ignoring: %m", lvalue, rvalue);
dd1f5bd0 1698 return 0;
94f04347 1699 }
9eba9da4 1700
a103496c 1701 if (sum == 0 || *capability_set == initial)
c792ec2e
IP
1702 /* "", "~" or uninitialized data -> replace */
1703 *capability_set = invert ? ~sum : sum;
1704 else {
a103496c 1705 /* previous data -> merge */
c792ec2e
IP
1706 if (invert)
1707 *capability_set &= ~sum;
1708 else
1709 *capability_set |= sum;
1710 }
260abb78 1711
9eba9da4
LP
1712 return 0;
1713}
1714
5f8640fb
LP
1715int config_parse_exec_selinux_context(
1716 const char *unit,
1717 const char *filename,
1718 unsigned line,
1719 const char *section,
1720 unsigned section_line,
1721 const char *lvalue,
1722 int ltype,
1723 const char *rvalue,
1724 void *data,
1725 void *userdata) {
1726
1727 ExecContext *c = data;
47538b76 1728 const Unit *u = userdata;
5f8640fb
LP
1729 bool ignore;
1730 char *k;
1731 int r;
1732
1733 assert(filename);
1734 assert(lvalue);
1735 assert(rvalue);
1736 assert(data);
1737
1738 if (isempty(rvalue)) {
a1e58e8e 1739 c->selinux_context = mfree(c->selinux_context);
5f8640fb
LP
1740 c->selinux_context_ignore = false;
1741 return 0;
1742 }
1743
1744 if (rvalue[0] == '-') {
1745 ignore = true;
1746 rvalue++;
1747 } else
1748 ignore = false;
1749
18913df9 1750 r = unit_full_printf(u, rvalue, &k);
5f8640fb 1751 if (r < 0) {
323dda78 1752 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
063c4b1a
YW
1753 "Failed to resolve unit specifiers in '%s'%s: %m",
1754 rvalue, ignore ? ", ignoring" : "");
bb28e684 1755 return ignore ? 0 : -ENOEXEC;
5f8640fb
LP
1756 }
1757
063c4b1a 1758 free_and_replace(c->selinux_context, k);
5f8640fb
LP
1759 c->selinux_context_ignore = ignore;
1760
1761 return 0;
1762}
1763
eef65bf3
MS
1764int config_parse_exec_apparmor_profile(
1765 const char *unit,
1766 const char *filename,
1767 unsigned line,
1768 const char *section,
1769 unsigned section_line,
1770 const char *lvalue,
1771 int ltype,
1772 const char *rvalue,
1773 void *data,
1774 void *userdata) {
1775
1776 ExecContext *c = data;
47538b76 1777 const Unit *u = userdata;
eef65bf3
MS
1778 bool ignore;
1779 char *k;
1780 int r;
1781
1782 assert(filename);
1783 assert(lvalue);
1784 assert(rvalue);
1785 assert(data);
1786
1787 if (isempty(rvalue)) {
a1e58e8e 1788 c->apparmor_profile = mfree(c->apparmor_profile);
eef65bf3
MS
1789 c->apparmor_profile_ignore = false;
1790 return 0;
1791 }
1792
1793 if (rvalue[0] == '-') {
1794 ignore = true;
1795 rvalue++;
1796 } else
1797 ignore = false;
1798
18913df9 1799 r = unit_full_printf(u, rvalue, &k);
eef65bf3 1800 if (r < 0) {
323dda78 1801 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
063c4b1a
YW
1802 "Failed to resolve unit specifiers in '%s'%s: %m",
1803 rvalue, ignore ? ", ignoring" : "");
bb28e684 1804 return ignore ? 0 : -ENOEXEC;
eef65bf3
MS
1805 }
1806
063c4b1a 1807 free_and_replace(c->apparmor_profile, k);
eef65bf3
MS
1808 c->apparmor_profile_ignore = ignore;
1809
1810 return 0;
1811}
1812
2ca620c4
WC
1813int config_parse_exec_smack_process_label(
1814 const char *unit,
1815 const char *filename,
1816 unsigned line,
1817 const char *section,
1818 unsigned section_line,
1819 const char *lvalue,
1820 int ltype,
1821 const char *rvalue,
1822 void *data,
1823 void *userdata) {
1824
1825 ExecContext *c = data;
47538b76 1826 const Unit *u = userdata;
2ca620c4
WC
1827 bool ignore;
1828 char *k;
1829 int r;
1830
1831 assert(filename);
1832 assert(lvalue);
1833 assert(rvalue);
1834 assert(data);
1835
1836 if (isempty(rvalue)) {
a1e58e8e 1837 c->smack_process_label = mfree(c->smack_process_label);
2ca620c4
WC
1838 c->smack_process_label_ignore = false;
1839 return 0;
1840 }
1841
1842 if (rvalue[0] == '-') {
1843 ignore = true;
1844 rvalue++;
1845 } else
1846 ignore = false;
1847
18913df9 1848 r = unit_full_printf(u, rvalue, &k);
2ca620c4 1849 if (r < 0) {
323dda78 1850 log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
063c4b1a
YW
1851 "Failed to resolve unit specifiers in '%s'%s: %m",
1852 rvalue, ignore ? ", ignoring" : "");
bb28e684 1853 return ignore ? 0 : -ENOEXEC;
2ca620c4
WC
1854 }
1855
063c4b1a 1856 free_and_replace(c->smack_process_label, k);
2ca620c4
WC
1857 c->smack_process_label_ignore = ignore;
1858
1859 return 0;
1860}
1861
25a04ae5
LP
1862int config_parse_timer(
1863 const char *unit,
1864 const char *filename,
1865 unsigned line,
1866 const char *section,
1867 unsigned section_line,
1868 const char *lvalue,
1869 int ltype,
1870 const char *rvalue,
1871 void *data,
1872 void *userdata) {
871d7de4 1873
25a04ae5
LP
1874 _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
1875 _cleanup_free_ char *k = NULL;
47538b76 1876 const Unit *u = userdata;
871d7de4 1877 Timer *t = data;
2507992f 1878 usec_t usec = 0;
871d7de4 1879 TimerValue *v;
2507992f 1880 int r;
871d7de4
LP
1881
1882 assert(filename);
1883 assert(lvalue);
1884 assert(rvalue);
1885 assert(data);
1886
74051b9b
LP
1887 if (isempty(rvalue)) {
1888 /* Empty assignment resets list */
1889 timer_free_values(t);
1890 return 0;
1891 }
1892
2507992f
DC
1893 r = unit_full_printf(u, rvalue, &k);
1894 if (r < 0) {
323dda78 1895 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
2507992f
DC
1896 return 0;
1897 }
1898
25a04ae5 1899 if (ltype == TIMER_CALENDAR) {
dc44c96d
LP
1900 r = calendar_spec_from_string(k, &c);
1901 if (r < 0) {
323dda78 1902 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse calendar specification, ignoring: %s", k);
36697dc0
LP
1903 return 0;
1904 }
dc44c96d
LP
1905 } else {
1906 r = parse_sec(k, &usec);
1907 if (r < 0) {
323dda78 1908 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse timer value, ignoring: %s", k);
36697dc0
LP
1909 return 0;
1910 }
dc44c96d 1911 }
871d7de4 1912
25a04ae5 1913 v = new(TimerValue, 1);
921b5987 1914 if (!v)
74051b9b 1915 return log_oom();
871d7de4 1916
25a04ae5
LP
1917 *v = (TimerValue) {
1918 .base = ltype,
1919 .value = usec,
1920 .calendar_spec = TAKE_PTR(c),
1921 };
871d7de4 1922
71fda00f 1923 LIST_PREPEND(value, t->values, v);
871d7de4
LP
1924
1925 return 0;
1926}
1927
3ecaa09b
LP
1928int config_parse_trigger_unit(
1929 const char *unit,
1930 const char *filename,
1931 unsigned line,
1932 const char *section,
71a61510 1933 unsigned section_line,
3ecaa09b
LP
1934 const char *lvalue,
1935 int ltype,
1936 const char *rvalue,
1937 void *data,
1938 void *userdata) {
871d7de4 1939
74051b9b 1940 _cleanup_free_ char *p = NULL;
3ecaa09b
LP
1941 Unit *u = data;
1942 UnitType type;
1943 int r;
398ef8ba
LP
1944
1945 assert(filename);
1946 assert(lvalue);
1947 assert(rvalue);
1948 assert(data);
1949
eef85c4a 1950 if (!hashmap_isempty(u->dependencies[UNIT_TRIGGERS])) {
323dda78 1951 log_syntax(unit, LOG_WARNING, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
3ecaa09b
LP
1952 return 0;
1953 }
871d7de4 1954
19f6d710 1955 r = unit_name_printf(u, rvalue, &p);
12ca818f 1956 if (r < 0) {
323dda78 1957 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
12ca818f
LP
1958 return 0;
1959 }
74051b9b 1960
12ca818f 1961 type = unit_name_to_type(p);
3ecaa09b 1962 if (type < 0) {
323dda78 1963 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue);
c0b34696 1964 return 0;
871d7de4 1965 }
49219a1c 1966 if (unit_has_name(u, p)) {
323dda78 1967 log_syntax(unit, LOG_WARNING, filename, line, 0, "Units cannot trigger themselves, ignoring: %s", rvalue);
3ecaa09b
LP
1968 return 0;
1969 }
1970
5a724170 1971 r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, true, UNIT_DEPENDENCY_FILE);
57020a3a 1972 if (r < 0) {
323dda78 1973 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
c0b34696 1974 return 0;
871d7de4
LP
1975 }
1976
1977 return 0;
1978}
1979
e8e581bf
ZJS
1980int config_parse_path_spec(const char *unit,
1981 const char *filename,
1982 unsigned line,
1983 const char *section,
71a61510 1984 unsigned section_line,
e8e581bf
ZJS
1985 const char *lvalue,
1986 int ltype,
1987 const char *rvalue,
1988 void *data,
1989 void *userdata) {
01f78473
LP
1990
1991 Path *p = data;
1992 PathSpec *s;
1993 PathType b;
7fd1b19b 1994 _cleanup_free_ char *k = NULL;
19f6d710 1995 int r;
01f78473
LP
1996
1997 assert(filename);
1998 assert(lvalue);
1999 assert(rvalue);
2000 assert(data);
2001
74051b9b
LP
2002 if (isempty(rvalue)) {
2003 /* Empty assignment clears list */
2004 path_free_specs(p);
2005 return 0;
2006 }
2007
93e4c84b
LP
2008 b = path_type_from_string(lvalue);
2009 if (b < 0) {
323dda78 2010 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue);
c0b34696 2011 return 0;
01f78473
LP
2012 }
2013
19f6d710
LP
2014 r = unit_full_printf(UNIT(p), rvalue, &k);
2015 if (r < 0) {
323dda78 2016 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
12ca818f 2017 return 0;
487060c2 2018 }
93e4c84b 2019
2f4d31c1
YW
2020 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2021 if (r < 0)
c0b34696 2022 return 0;
01f78473 2023
93e4c84b 2024 s = new0(PathSpec, 1);
543295ad 2025 if (!s)
93e4c84b 2026 return log_oom();
01f78473 2027
718db961 2028 s->unit = UNIT(p);
063c4b1a 2029 s->path = TAKE_PTR(k);
01f78473
LP
2030 s->type = b;
2031 s->inotify_fd = -1;
2032
71fda00f 2033 LIST_PREPEND(spec, p->specs, s);
01f78473
LP
2034
2035 return 0;
2036}
2037
b02cb41c
LP
2038int config_parse_socket_service(
2039 const char *unit,
2040 const char *filename,
2041 unsigned line,
2042 const char *section,
2043 unsigned section_line,
2044 const char *lvalue,
2045 int ltype,
2046 const char *rvalue,
2047 void *data,
2048 void *userdata) {
d9ff321a 2049
4afd3348 2050 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
8dd4c05b 2051 _cleanup_free_ char *p = NULL;
d9ff321a 2052 Socket *s = data;
4ff77f66 2053 Unit *x;
8dd4c05b 2054 int r;
d9ff321a
LP
2055
2056 assert(filename);
2057 assert(lvalue);
2058 assert(rvalue);
2059 assert(data);
2060
19f6d710 2061 r = unit_name_printf(UNIT(s), rvalue, &p);
613b411c 2062 if (r < 0) {
323dda78
YW
2063 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
2064 return 0;
613b411c 2065 }
74051b9b 2066
613b411c 2067 if (!endswith(p, ".service")) {
323dda78
YW
2068 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
2069 return 0;
d9ff321a
LP
2070 }
2071
613b411c 2072 r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
4ff77f66 2073 if (r < 0) {
323dda78
YW
2074 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
2075 return 0;
d9ff321a
LP
2076 }
2077
7f7d01ed 2078 unit_ref_set(&s->service, UNIT(s), x);
4ff77f66 2079
d9ff321a
LP
2080 return 0;
2081}
2082
8dd4c05b
LP
2083int config_parse_fdname(
2084 const char *unit,
2085 const char *filename,
2086 unsigned line,
2087 const char *section,
2088 unsigned section_line,
2089 const char *lvalue,
2090 int ltype,
2091 const char *rvalue,
2092 void *data,
2093 void *userdata) {
2094
2095 _cleanup_free_ char *p = NULL;
2096 Socket *s = data;
2097 int r;
2098
2099 assert(filename);
2100 assert(lvalue);
2101 assert(rvalue);
2102 assert(data);
2103
2104 if (isempty(rvalue)) {
2105 s->fdname = mfree(s->fdname);
2106 return 0;
2107 }
2108
18913df9 2109 r = unit_full_printf(UNIT(s), rvalue, &p);
8dd4c05b 2110 if (r < 0) {
323dda78 2111 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
8dd4c05b
LP
2112 return 0;
2113 }
2114
2115 if (!fdname_is_valid(p)) {
323dda78 2116 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
8dd4c05b
LP
2117 return 0;
2118 }
2119
3b319885 2120 return free_and_replace(s->fdname, p);
8dd4c05b
LP
2121}
2122
b02cb41c
LP
2123int config_parse_service_sockets(
2124 const char *unit,
2125 const char *filename,
2126 unsigned line,
2127 const char *section,
2128 unsigned section_line,
2129 const char *lvalue,
2130 int ltype,
2131 const char *rvalue,
2132 void *data,
2133 void *userdata) {
f976f3f6
LP
2134
2135 Service *s = data;
b02cb41c 2136 int r;
f976f3f6
LP
2137
2138 assert(filename);
2139 assert(lvalue);
2140 assert(rvalue);
2141 assert(data);
2142
323dda78 2143 for (const char *p = rvalue;;) {
6a0f3175 2144 _cleanup_free_ char *word = NULL, *k = NULL;
f976f3f6 2145
7b2313f5
SS
2146 r = extract_first_word(&p, &word, NULL, 0);
2147 if (r == 0)
323dda78 2148 return 0;
7b2313f5 2149 if (r == -ENOMEM)
74051b9b 2150 return log_oom();
7b2313f5 2151 if (r < 0) {
323dda78
YW
2152 log_syntax(unit, LOG_WARNING, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
2153 return 0;
7b2313f5 2154 }
f976f3f6 2155
7b2313f5 2156 r = unit_name_printf(UNIT(s), word, &k);
b02cb41c 2157 if (r < 0) {
323dda78 2158 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
b02cb41c
LP
2159 continue;
2160 }
57020a3a 2161
b02cb41c 2162 if (!endswith(k, ".socket")) {
323dda78 2163 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
f976f3f6
LP
2164 continue;
2165 }
2166
5a724170 2167 r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, true, UNIT_DEPENDENCY_FILE);
57020a3a 2168 if (r < 0)
323dda78 2169 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
f976f3f6 2170
35d8c19a 2171 r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, true, UNIT_DEPENDENCY_FILE);
57020a3a 2172 if (r < 0)
323dda78 2173 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
f976f3f6 2174 }
f976f3f6
LP
2175}
2176
b02cb41c
LP
2177int config_parse_bus_name(
2178 const char *unit,
2179 const char *filename,
2180 unsigned line,
2181 const char *section,
2182 unsigned section_line,
2183 const char *lvalue,
2184 int ltype,
2185 const char *rvalue,
2186 void *data,
2187 void *userdata) {
2188
2189 _cleanup_free_ char *k = NULL;
47538b76 2190 const Unit *u = userdata;
b02cb41c
LP
2191 int r;
2192
2193 assert(filename);
2194 assert(lvalue);
2195 assert(rvalue);
2196 assert(u);
2197
2198 r = unit_full_printf(u, rvalue, &k);
2199 if (r < 0) {
323dda78 2200 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
b02cb41c
LP
2201 return 0;
2202 }
2203
5453a4b1 2204 if (!sd_bus_service_name_is_valid(k)) {
323dda78 2205 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid bus name, ignoring: %s", k);
b02cb41c
LP
2206 return 0;
2207 }
2208
2209 return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
2210}
2211
aad41f08
LP
2212int config_parse_service_timeout(
2213 const char *unit,
2214 const char *filename,
2215 unsigned line,
2216 const char *section,
2217 unsigned section_line,
2218 const char *lvalue,
2219 int ltype,
2220 const char *rvalue,
2221 void *data,
2222 void *userdata) {
98709151
LN
2223
2224 Service *s = userdata;
aad41f08 2225 usec_t usec;
98709151
LN
2226 int r;
2227
2228 assert(filename);
2229 assert(lvalue);
2230 assert(rvalue);
2231 assert(s);
2232
6c58305a 2233 /* This is called for two cases: TimeoutSec= and TimeoutStartSec=. */
98709151 2234
fb27be3f
YW
2235 /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
2236 * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
2237 * all other timeouts. */
2238 r = parse_sec_fix_0(rvalue, &usec);
aad41f08 2239 if (r < 0) {
323dda78 2240 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
aad41f08
LP
2241 return 0;
2242 }
d568a335 2243
6c58305a
YW
2244 s->start_timeout_defined = true;
2245 s->timeout_start_usec = usec;
36c16a7c 2246
6c58305a 2247 if (streq(lvalue, "TimeoutSec"))
aad41f08 2248 s->timeout_stop_usec = usec;
36c16a7c 2249
d568a335 2250 return 0;
98709151
LN
2251}
2252
a61d6874 2253int config_parse_timeout_abort(
dc653bf4
JK
2254 const char *unit,
2255 const char *filename,
2256 unsigned line,
2257 const char *section,
2258 unsigned section_line,
2259 const char *lvalue,
2260 int ltype,
2261 const char *rvalue,
2262 void *data,
2263 void *userdata) {
2264
a61d6874 2265 usec_t *ret = data;
dc653bf4
JK
2266 int r;
2267
2268 assert(filename);
2269 assert(lvalue);
2270 assert(rvalue);
a61d6874
ZJS
2271 assert(ret);
2272
2273 /* Note: apart from setting the arg, this returns an extra bit of information in the return value. */
dc653bf4 2274
dc653bf4 2275 if (isempty(rvalue)) {
a61d6874
ZJS
2276 *ret = 0;
2277 return 0; /* "not set" */
dc653bf4
JK
2278 }
2279
a61d6874
ZJS
2280 r = parse_sec(rvalue, ret);
2281 if (r < 0)
323dda78 2282 return log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= setting, ignoring: %s", lvalue, rvalue);
a61d6874
ZJS
2283
2284 return 1; /* "set" */
2285}
2286
2287int config_parse_service_timeout_abort(
2288 const char *unit,
2289 const char *filename,
2290 unsigned line,
2291 const char *section,
2292 unsigned section_line,
2293 const char *lvalue,
2294 int ltype,
2295 const char *rvalue,
2296 void *data,
2297 void *userdata) {
2298
2299 Service *s = userdata;
2300 int r;
dc653bf4 2301
a61d6874
ZJS
2302 assert(s);
2303
2304 r = config_parse_timeout_abort(unit, filename, line, section, section_line, lvalue, ltype, rvalue,
2305 &s->timeout_abort_usec, s);
2306 if (r >= 0)
2307 s->timeout_abort_set = r;
dc653bf4
JK
2308 return 0;
2309}
2310
89beff89
LP
2311int config_parse_sec_fix_0(
2312 const char *unit,
2313 const char *filename,
2314 unsigned line,
2315 const char *section,
2316 unsigned section_line,
2317 const char *lvalue,
2318 int ltype,
2319 const char *rvalue,
2320 void *data,
2321 void *userdata) {
2322
2323 usec_t *usec = data;
2324 int r;
2325
2326 assert(filename);
2327 assert(lvalue);
2328 assert(rvalue);
2329 assert(usec);
2330
2331 /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
2332 * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
2333 * timeout. */
2334
0004f698 2335 r = parse_sec_fix_0(rvalue, usec);
323dda78
YW
2336 if (r < 0)
2337 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
89beff89 2338
89beff89
LP
2339 return 0;
2340}
2341
ae480f0b 2342int config_parse_user_group_compat(
66dccd8d
LP
2343 const char *unit,
2344 const char *filename,
2345 unsigned line,
2346 const char *section,
2347 unsigned section_line,
2348 const char *lvalue,
2349 int ltype,
2350 const char *rvalue,
2351 void *data,
2352 void *userdata) {
2353
063c4b1a
YW
2354 _cleanup_free_ char *k = NULL;
2355 char **user = data;
47538b76 2356 const Unit *u = userdata;
66dccd8d
LP
2357 int r;
2358
2359 assert(filename);
2360 assert(lvalue);
2361 assert(rvalue);
2362 assert(u);
2363
063c4b1a
YW
2364 if (isempty(rvalue)) {
2365 *user = mfree(*user);
2366 return 0;
2367 }
66dccd8d 2368
063c4b1a
YW
2369 r = unit_full_printf(u, rvalue, &k);
2370 if (r < 0) {
2371 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue);
2372 return -ENOEXEC;
66dccd8d
LP
2373 }
2374
7a8867ab 2375 if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) {
063c4b1a
YW
2376 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2377 return -ENOEXEC;
2378 }
66dccd8d 2379
bed0b7df
LP
2380 if (strstr(lvalue, "User") && streq(k, NOBODY_USER_NAME))
2381 log_struct(LOG_NOTICE,
2382 "MESSAGE=%s:%u: Special user %s configured, this is not safe!", filename, line, k,
2383 "UNIT=%s", unit,
2384 "MESSAGE_ID=" SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR,
2385 "OFFENDING_USER=%s", k,
2386 "CONFIG_FILE=%s", filename,
2387 "CONFIG_LINE=%u", line);
2388
063c4b1a 2389 return free_and_replace(*user, k);
66dccd8d
LP
2390}
2391
ae480f0b 2392int config_parse_user_group_strv_compat(
66dccd8d
LP
2393 const char *unit,
2394 const char *filename,
2395 unsigned line,
2396 const char *section,
2397 unsigned section_line,
2398 const char *lvalue,
2399 int ltype,
2400 const char *rvalue,
2401 void *data,
2402 void *userdata) {
2403
2404 char ***users = data;
47538b76 2405 const Unit *u = userdata;
66dccd8d
LP
2406 int r;
2407
2408 assert(filename);
2409 assert(lvalue);
2410 assert(rvalue);
2411 assert(u);
2412
2413 if (isempty(rvalue)) {
9f2d41a6 2414 *users = strv_free(*users);
66dccd8d
LP
2415 return 0;
2416 }
2417
323dda78 2418 for (const char *p = rvalue;;) {
66dccd8d
LP
2419 _cleanup_free_ char *word = NULL, *k = NULL;
2420
9a82ab95 2421 r = extract_first_word(&p, &word, NULL, 0);
66dccd8d 2422 if (r == 0)
323dda78 2423 return 0;
66dccd8d
LP
2424 if (r == -ENOMEM)
2425 return log_oom();
2426 if (r < 0) {
bb28e684
ZJS
2427 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue);
2428 return -ENOEXEC;
66dccd8d
LP
2429 }
2430
2431 r = unit_full_printf(u, word, &k);
2432 if (r < 0) {
bb28e684
ZJS
2433 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word);
2434 return -ENOEXEC;
66dccd8d
LP
2435 }
2436
7a8867ab 2437 if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) {
bb28e684
ZJS
2438 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
2439 return -ENOEXEC;
66dccd8d
LP
2440 }
2441
2442 r = strv_push(users, k);
2443 if (r < 0)
2444 return log_oom();
2445
2446 k = NULL;
2447 }
66dccd8d
LP
2448}
2449
5f5d8eab
LP
2450int config_parse_working_directory(
2451 const char *unit,
2452 const char *filename,
2453 unsigned line,
2454 const char *section,
2455 unsigned section_line,
2456 const char *lvalue,
2457 int ltype,
2458 const char *rvalue,
2459 void *data,
2460 void *userdata) {
2461
2462 ExecContext *c = data;
47538b76 2463 const Unit *u = userdata;
5f5d8eab
LP
2464 bool missing_ok;
2465 int r;
2466
2467 assert(filename);
2468 assert(lvalue);
2469 assert(rvalue);
2470 assert(c);
2471 assert(u);
2472
862fcffd
YW
2473 if (isempty(rvalue)) {
2474 c->working_directory_home = false;
2475 c->working_directory = mfree(c->working_directory);
2476 return 0;
2477 }
2478
5f5d8eab
LP
2479 if (rvalue[0] == '-') {
2480 missing_ok = true;
2481 rvalue++;
2482 } else
2483 missing_ok = false;
2484
2485 if (streq(rvalue, "~")) {
2486 c->working_directory_home = true;
2487 c->working_directory = mfree(c->working_directory);
2488 } else {
2489 _cleanup_free_ char *k = NULL;
2490
2491 r = unit_full_printf(u, rvalue, &k);
2492 if (r < 0) {
323dda78 2493 log_syntax(unit, missing_ok ? LOG_WARNING : LOG_ERR, filename, line, r,
bb28e684
ZJS
2494 "Failed to resolve unit specifiers in working directory path '%s'%s: %m",
2495 rvalue, missing_ok ? ", ignoring" : "");
2496 return missing_ok ? 0 : -ENOEXEC;
5f5d8eab
LP
2497 }
2498
2f4d31c1
YW
2499 r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE | (missing_ok ? 0 : PATH_CHECK_FATAL), unit, filename, line, lvalue);
2500 if (r < 0)
bb28e684 2501 return missing_ok ? 0 : -ENOEXEC;
5f5d8eab 2502
5f5d8eab 2503 c->working_directory_home = false;
bb28e684 2504 free_and_replace(c->working_directory, k);
5f5d8eab
LP
2505 }
2506
2507 c->working_directory_missing_ok = missing_ok;
2508 return 0;
2509}
2510
e8e581bf
ZJS
2511int config_parse_unit_env_file(const char *unit,
2512 const char *filename,
2513 unsigned line,
2514 const char *section,
71a61510 2515 unsigned section_line,
e8e581bf
ZJS
2516 const char *lvalue,
2517 int ltype,
2518 const char *rvalue,
2519 void *data,
2520 void *userdata) {
ddb26e18 2521
853b8397 2522 char ***env = data;
47538b76 2523 const Unit *u = userdata;
19f6d710 2524 _cleanup_free_ char *n = NULL;
853b8397 2525 int r;
ddb26e18
LP
2526
2527 assert(filename);
2528 assert(lvalue);
2529 assert(rvalue);
2530 assert(data);
2531
74051b9b
LP
2532 if (isempty(rvalue)) {
2533 /* Empty assignment frees the list */
6796073e 2534 *env = strv_free(*env);
74051b9b
LP
2535 return 0;
2536 }
2537
19f6d710 2538 r = unit_full_printf(u, rvalue, &n);
12ca818f 2539 if (r < 0) {
323dda78 2540 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
12ca818f
LP
2541 return 0;
2542 }
8fef7659 2543
2f4d31c1
YW
2544 r = path_simplify_and_warn(n[0] == '-' ? n + 1 : n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2545 if (r < 0)
afe4bfe2 2546 return 0;
afe4bfe2 2547
2f4d31c1 2548 r = strv_push(env, n);
853b8397
LP
2549 if (r < 0)
2550 return log_oom();
2551
2f4d31c1
YW
2552 n = NULL;
2553
853b8397
LP
2554 return 0;
2555}
2556
f7f3f5c3
LP
2557int config_parse_environ(
2558 const char *unit,
2559 const char *filename,
2560 unsigned line,
2561 const char *section,
2562 unsigned section_line,
2563 const char *lvalue,
2564 int ltype,
2565 const char *rvalue,
2566 void *data,
2567 void *userdata) {
853b8397 2568
47538b76 2569 const Unit *u = userdata;
035fe294 2570 char ***env = data;
19f6d710 2571 int r;
853b8397
LP
2572
2573 assert(filename);
2574 assert(lvalue);
2575 assert(rvalue);
97d0e5f8 2576 assert(data);
853b8397
LP
2577
2578 if (isempty(rvalue)) {
2579 /* Empty assignment resets the list */
6796073e 2580 *env = strv_free(*env);
853b8397
LP
2581 return 0;
2582 }
2583
323dda78 2584 for (const char *p = rvalue;; ) {
035fe294 2585 _cleanup_free_ char *word = NULL, *k = NULL;
035fe294 2586
4ec85141 2587 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
035fe294
ZJS
2588 if (r == 0)
2589 return 0;
2590 if (r == -ENOMEM)
2591 return log_oom();
12ca818f 2592 if (r < 0) {
035fe294
ZJS
2593 log_syntax(unit, LOG_WARNING, filename, line, r,
2594 "Invalid syntax, ignoring: %s", rvalue);
12ca818f
LP
2595 return 0;
2596 }
97d0e5f8 2597
035fe294
ZJS
2598 if (u) {
2599 r = unit_full_printf(u, word, &k);
2600 if (r < 0) {
323dda78 2601 log_syntax(unit, LOG_WARNING, filename, line, r,
063c4b1a 2602 "Failed to resolve unit specifiers in %s, ignoring: %m", word);
035fe294
ZJS
2603 continue;
2604 }
ae2a15bc
LP
2605 } else
2606 k = TAKE_PTR(word);
853b8397 2607
035fe294 2608 if (!env_assignment_is_valid(k)) {
323dda78 2609 log_syntax(unit, LOG_WARNING, filename, line, 0,
035fe294 2610 "Invalid environment assignment, ignoring: %s", k);
853b8397
LP
2611 continue;
2612 }
2613
54ac3494
ZJS
2614 r = strv_env_replace(env, k);
2615 if (r < 0)
853b8397 2616 return log_oom();
f7f3f5c3 2617
54ac3494 2618 k = NULL;
853b8397 2619 }
ddb26e18
LP
2620}
2621
00819cc1
LP
2622int config_parse_pass_environ(
2623 const char *unit,
2624 const char *filename,
2625 unsigned line,
2626 const char *section,
2627 unsigned section_line,
2628 const char *lvalue,
2629 int ltype,
2630 const char *rvalue,
2631 void *data,
2632 void *userdata) {
b4c14404 2633
b4c14404
FB
2634 _cleanup_strv_free_ char **n = NULL;
2635 size_t nlen = 0, nbufsize = 0;
41de9cc2 2636 char*** passenv = data;
47538b76 2637 const Unit *u = userdata;
b4c14404
FB
2638 int r;
2639
2640 assert(filename);
2641 assert(lvalue);
2642 assert(rvalue);
2643 assert(data);
2644
2645 if (isempty(rvalue)) {
2646 /* Empty assignment resets the list */
2647 *passenv = strv_free(*passenv);
2648 return 0;
2649 }
2650
323dda78 2651 for (const char *p = rvalue;;) {
41de9cc2 2652 _cleanup_free_ char *word = NULL, *k = NULL;
b4c14404 2653
4ec85141 2654 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
b4c14404
FB
2655 if (r == 0)
2656 break;
2657 if (r == -ENOMEM)
2658 return log_oom();
2659 if (r < 0) {
323dda78 2660 log_syntax(unit, LOG_WARNING, filename, line, r,
063c4b1a 2661 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
b4c14404
FB
2662 break;
2663 }
2664
41de9cc2
LP
2665 if (u) {
2666 r = unit_full_printf(u, word, &k);
2667 if (r < 0) {
323dda78 2668 log_syntax(unit, LOG_WARNING, filename, line, r,
063c4b1a 2669 "Failed to resolve specifiers in %s, ignoring: %m", word);
41de9cc2
LP
2670 continue;
2671 }
ae2a15bc
LP
2672 } else
2673 k = TAKE_PTR(word);
41de9cc2
LP
2674
2675 if (!env_name_is_valid(k)) {
323dda78 2676 log_syntax(unit, LOG_WARNING, filename, line, 0,
41de9cc2 2677 "Invalid environment name for %s, ignoring: %s", lvalue, k);
b4c14404
FB
2678 continue;
2679 }
2680
2681 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2682 return log_oom();
41de9cc2 2683
1cc6c93a 2684 n[nlen++] = TAKE_PTR(k);
b4c14404 2685 n[nlen] = NULL;
b4c14404
FB
2686 }
2687
2688 if (n) {
2689 r = strv_extend_strv(passenv, n, true);
2690 if (r < 0)
2691 return r;
2692 }
2693
2694 return 0;
2695}
2696
00819cc1
LP
2697int config_parse_unset_environ(
2698 const char *unit,
2699 const char *filename,
2700 unsigned line,
2701 const char *section,
2702 unsigned section_line,
2703 const char *lvalue,
2704 int ltype,
2705 const char *rvalue,
2706 void *data,
2707 void *userdata) {
2708
2709 _cleanup_strv_free_ char **n = NULL;
00819cc1
LP
2710 size_t nlen = 0, nbufsize = 0;
2711 char*** unsetenv = data;
47538b76 2712 const Unit *u = userdata;
00819cc1
LP
2713 int r;
2714
2715 assert(filename);
2716 assert(lvalue);
2717 assert(rvalue);
2718 assert(data);
2719
2720 if (isempty(rvalue)) {
2721 /* Empty assignment resets the list */
2722 *unsetenv = strv_free(*unsetenv);
2723 return 0;
2724 }
2725
323dda78 2726 for (const char *p = rvalue;;) {
00819cc1
LP
2727 _cleanup_free_ char *word = NULL, *k = NULL;
2728
4ec85141 2729 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
00819cc1
LP
2730 if (r == 0)
2731 break;
2732 if (r == -ENOMEM)
2733 return log_oom();
2734 if (r < 0) {
323dda78 2735 log_syntax(unit, LOG_WARNING, filename, line, r,
063c4b1a 2736 "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
00819cc1
LP
2737 break;
2738 }
2739
2740 if (u) {
2741 r = unit_full_printf(u, word, &k);
2742 if (r < 0) {
323dda78 2743 log_syntax(unit, LOG_WARNING, filename, line, r,
063c4b1a 2744 "Failed to resolve unit specifiers in %s, ignoring: %m", word);
00819cc1
LP
2745 continue;
2746 }
ae2a15bc
LP
2747 } else
2748 k = TAKE_PTR(word);
00819cc1
LP
2749
2750 if (!env_assignment_is_valid(k) && !env_name_is_valid(k)) {
323dda78 2751 log_syntax(unit, LOG_WARNING, filename, line, 0,
00819cc1
LP
2752 "Invalid environment name or assignment %s, ignoring: %s", lvalue, k);
2753 continue;
2754 }
2755
2756 if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
2757 return log_oom();
2758
1cc6c93a 2759 n[nlen++] = TAKE_PTR(k);
00819cc1 2760 n[nlen] = NULL;
00819cc1
LP
2761 }
2762
2763 if (n) {
2764 r = strv_extend_strv(unsetenv, n, true);
2765 if (r < 0)
2766 return r;
2767 }
2768
2769 return 0;
2770}
2771
d3070fbd
LP
2772int config_parse_log_extra_fields(
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 ExecContext *c = data;
47538b76 2785 const Unit *u = userdata;
d3070fbd
LP
2786 int r;
2787
2788 assert(filename);
2789 assert(lvalue);
2790 assert(rvalue);
2791 assert(c);
2792
2793 if (isempty(rvalue)) {
2794 exec_context_free_log_extra_fields(c);
2795 return 0;
2796 }
2797
323dda78 2798 for (const char *p = rvalue;;) {
d3070fbd
LP
2799 _cleanup_free_ char *word = NULL, *k = NULL;
2800 struct iovec *t;
2801 const char *eq;
2802
4ec85141 2803 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
d3070fbd 2804 if (r == 0)
063c4b1a 2805 return 0;
d3070fbd
LP
2806 if (r == -ENOMEM)
2807 return log_oom();
2808 if (r < 0) {
2809 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
2810 return 0;
2811 }
2812
2813 r = unit_full_printf(u, word, &k);
2814 if (r < 0) {
323dda78 2815 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word);
d3070fbd
LP
2816 continue;
2817 }
2818
2819 eq = strchr(k, '=');
2820 if (!eq) {
323dda78 2821 log_syntax(unit, LOG_WARNING, filename, line, 0, "Log field lacks '=' character, ignoring: %s", k);
d3070fbd
LP
2822 continue;
2823 }
2824
2825 if (!journal_field_valid(k, eq-k, false)) {
323dda78 2826 log_syntax(unit, LOG_WARNING, filename, line, 0, "Log field name is invalid, ignoring: %s", k);
d3070fbd
LP
2827 continue;
2828 }
2829
aa484f35 2830 t = reallocarray(c->log_extra_fields, c->n_log_extra_fields+1, sizeof(struct iovec));
d3070fbd
LP
2831 if (!t)
2832 return log_oom();
2833
2834 c->log_extra_fields = t;
2835 c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE_STRING(k);
2836
2837 k = NULL;
2838 }
d3070fbd
LP
2839}
2840
91dd5f7c
LP
2841int config_parse_log_namespace(
2842 const char *unit,
2843 const char *filename,
2844 unsigned line,
2845 const char *section,
2846 unsigned section_line,
2847 const char *lvalue,
2848 int ltype,
2849 const char *rvalue,
2850 void *data,
2851 void *userdata) {
2852
2853 _cleanup_free_ char *k = NULL;
2854 ExecContext *c = data;
2855 const Unit *u = userdata;
2856 int r;
2857
2858 assert(filename);
2859 assert(lvalue);
2860 assert(rvalue);
2861 assert(c);
2862
2863 if (isempty(rvalue)) {
2864 c->log_namespace = mfree(c->log_namespace);
2865 return 0;
2866 }
2867
2868 r = unit_full_printf(u, rvalue, &k);
2869 if (r < 0) {
323dda78 2870 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
91dd5f7c
LP
2871 return 0;
2872 }
2873
2874 if (!log_namespace_name_valid(k)) {
323dda78 2875 log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified log namespace name is not valid, ignoring: %s", k);
91dd5f7c
LP
2876 return 0;
2877 }
2878
2879 free_and_replace(c->log_namespace, k);
2880 return 0;
2881}
2882
59fccdc5
LP
2883int config_parse_unit_condition_path(
2884 const char *unit,
2885 const char *filename,
2886 unsigned line,
2887 const char *section,
2888 unsigned section_line,
2889 const char *lvalue,
2890 int ltype,
2891 const char *rvalue,
2892 void *data,
2893 void *userdata) {
52661efd 2894
2fbe635a 2895 _cleanup_free_ char *p = NULL;
59fccdc5
LP
2896 Condition **list = data, *c;
2897 ConditionType t = ltype;
2898 bool trigger, negate;
47538b76 2899 const Unit *u = userdata;
19f6d710 2900 int r;
52661efd
LP
2901
2902 assert(filename);
2903 assert(lvalue);
2904 assert(rvalue);
2905 assert(data);
2906
74051b9b
LP
2907 if (isempty(rvalue)) {
2908 /* Empty assignment resets the list */
447021aa 2909 *list = condition_free_list(*list);
74051b9b
LP
2910 return 0;
2911 }
2912
ab7f148f
LP
2913 trigger = rvalue[0] == '|';
2914 if (trigger)
267632f0
LP
2915 rvalue++;
2916
ab7f148f
LP
2917 negate = rvalue[0] == '!';
2918 if (negate)
52661efd
LP
2919 rvalue++;
2920
19f6d710 2921 r = unit_full_printf(u, rvalue, &p);
59fccdc5 2922 if (r < 0) {
323dda78 2923 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
59fccdc5 2924 return 0;
19f6d710 2925 }
095b2d7a 2926
2f4d31c1
YW
2927 r = path_simplify_and_warn(p, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
2928 if (r < 0)
52661efd 2929 return 0;
52661efd 2930
59fccdc5 2931 c = condition_new(t, p, trigger, negate);
ab7f148f 2932 if (!c)
74051b9b 2933 return log_oom();
52661efd 2934
59fccdc5 2935 LIST_PREPEND(conditions, *list, c);
52661efd
LP
2936 return 0;
2937}
2938
59fccdc5
LP
2939int config_parse_unit_condition_string(
2940 const char *unit,
2941 const char *filename,
2942 unsigned line,
2943 const char *section,
2944 unsigned section_line,
2945 const char *lvalue,
2946 int ltype,
2947 const char *rvalue,
2948 void *data,
2949 void *userdata) {
039655a4 2950
2fbe635a 2951 _cleanup_free_ char *s = NULL;
59fccdc5
LP
2952 Condition **list = data, *c;
2953 ConditionType t = ltype;
2954 bool trigger, negate;
47538b76 2955 const Unit *u = userdata;
19f6d710 2956 int r;
039655a4
LP
2957
2958 assert(filename);
2959 assert(lvalue);
2960 assert(rvalue);
2961 assert(data);
2962
74051b9b
LP
2963 if (isempty(rvalue)) {
2964 /* Empty assignment resets the list */
447021aa 2965 *list = condition_free_list(*list);
74051b9b
LP
2966 return 0;
2967 }
2968
9266f31e 2969 trigger = *rvalue == '|';
c0d6e764 2970 if (trigger)
9266f31e 2971 rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
267632f0 2972
9266f31e 2973 negate = *rvalue == '!';
c0d6e764 2974 if (negate)
9266f31e 2975 rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
039655a4 2976
19f6d710 2977 r = unit_full_printf(u, rvalue, &s);
59fccdc5 2978 if (r < 0) {
323dda78 2979 log_syntax(unit, LOG_WARNING, filename, line, r,
cae90de3 2980 "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
59fccdc5 2981 return 0;
19f6d710 2982 }
095b2d7a 2983
59fccdc5 2984 c = condition_new(t, s, trigger, negate);
c0d6e764
LP
2985 if (!c)
2986 return log_oom();
039655a4 2987
59fccdc5 2988 LIST_PREPEND(conditions, *list, c);
039655a4
LP
2989 return 0;
2990}
2991
a57f7e2c
LP
2992int config_parse_unit_requires_mounts_for(
2993 const char *unit,
2994 const char *filename,
2995 unsigned line,
2996 const char *section,
71a61510 2997 unsigned section_line,
a57f7e2c
LP
2998 const char *lvalue,
2999 int ltype,
3000 const char *rvalue,
3001 void *data,
3002 void *userdata) {
7c8fa05c
LP
3003
3004 Unit *u = userdata;
035fe294 3005 int r;
7c8fa05c
LP
3006
3007 assert(filename);
3008 assert(lvalue);
3009 assert(rvalue);
3010 assert(data);
3011
323dda78 3012 for (const char *p = rvalue;;) {
744bb5b1 3013 _cleanup_free_ char *word = NULL, *resolved = NULL;
a57f7e2c 3014
4ec85141 3015 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
035fe294
ZJS
3016 if (r == 0)
3017 return 0;
3018 if (r == -ENOMEM)
a57f7e2c 3019 return log_oom();
035fe294
ZJS
3020 if (r < 0) {
3021 log_syntax(unit, LOG_WARNING, filename, line, r,
3022 "Invalid syntax, ignoring: %s", rvalue);
3023 return 0;
3024 }
7c8fa05c 3025
744bb5b1
LP
3026 r = unit_full_printf(u, word, &resolved);
3027 if (r < 0) {
323dda78 3028 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", word);
744bb5b1
LP
3029 continue;
3030 }
3031
2f4d31c1
YW
3032 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
3033 if (r < 0)
3034 continue;
3035
eef85c4a 3036 r = unit_require_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE);
a57f7e2c 3037 if (r < 0) {
323dda78 3038 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to add required mount '%s', ignoring: %m", resolved);
a57f7e2c
LP
3039 continue;
3040 }
3041 }
7c8fa05c 3042}
9e372868 3043
e8e581bf
ZJS
3044int config_parse_documentation(const char *unit,
3045 const char *filename,
3046 unsigned line,
3047 const char *section,
71a61510 3048 unsigned section_line,
e8e581bf
ZJS
3049 const char *lvalue,
3050 int ltype,
3051 const char *rvalue,
3052 void *data,
3053 void *userdata) {
49dbfa7b
LP
3054
3055 Unit *u = userdata;
3056 int r;
3057 char **a, **b;
3058
3059 assert(filename);
3060 assert(lvalue);
3061 assert(rvalue);
3062 assert(u);
3063
74051b9b
LP
3064 if (isempty(rvalue)) {
3065 /* Empty assignment resets the list */
6796073e 3066 u->documentation = strv_free(u->documentation);
74051b9b
LP
3067 return 0;
3068 }
3069
71a61510 3070 r = config_parse_unit_strv_printf(unit, filename, line, section, section_line, lvalue, ltype,
e8e581bf 3071 rvalue, data, userdata);
49dbfa7b
LP
3072 if (r < 0)
3073 return r;
3074
3075 for (a = b = u->documentation; a && *a; a++) {
3076
a2e03378 3077 if (documentation_url_is_valid(*a))
49dbfa7b
LP
3078 *(b++) = *a;
3079 else {
323dda78 3080 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid URL, ignoring: %s", *a);
49dbfa7b
LP
3081 free(*a);
3082 }
3083 }
f6d2d421
ZJS
3084 if (b)
3085 *b = NULL;
49dbfa7b
LP
3086
3087 return r;
3088}
3089
349cc4a5 3090#if HAVE_SECCOMP
17df7223
LP
3091int config_parse_syscall_filter(
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
8351ceae 3103 ExecContext *c = data;
0a0e594a 3104 _unused_ const Unit *u = userdata;
b5fb3789 3105 bool invert = false;
17df7223 3106 int r;
8351ceae
LP
3107
3108 assert(filename);
3109 assert(lvalue);
3110 assert(rvalue);
3111 assert(u);
3112
74051b9b
LP
3113 if (isempty(rvalue)) {
3114 /* Empty assignment resets the list */
8cfa775f 3115 c->syscall_filter = hashmap_free(c->syscall_filter);
6b000af4 3116 c->syscall_allow_list = false;
74051b9b
LP
3117 return 0;
3118 }
3119
8351ceae
LP
3120 if (rvalue[0] == '~') {
3121 invert = true;
3122 rvalue++;
3123 }
3124
17df7223 3125 if (!c->syscall_filter) {
8cfa775f 3126 c->syscall_filter = hashmap_new(NULL);
17df7223
LP
3127 if (!c->syscall_filter)
3128 return log_oom();
3129
c0467cf3 3130 if (invert)
17df7223 3131 /* Allow everything but the ones listed */
6b000af4 3132 c->syscall_allow_list = false;
c0467cf3 3133 else {
17df7223 3134 /* Allow nothing but the ones listed */
6b000af4 3135 c->syscall_allow_list = true;
8351ceae 3136
6b000af4 3137 /* Accept default syscalls if we are on a allow_list */
2f6b9110
LP
3138 r = seccomp_parse_syscall_filter(
3139 "@default", -1, c->syscall_filter,
6b000af4 3140 SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_ALLOW_LIST,
58f6ab44
ZJS
3141 unit,
3142 NULL, 0);
201c1cc2
TM
3143 if (r < 0)
3144 return r;
c0467cf3 3145 }
8351ceae
LP
3146 }
3147
323dda78 3148 for (const char *p = rvalue;;) {
8cfa775f
YW
3149 _cleanup_free_ char *word = NULL, *name = NULL;
3150 int num;
8351ceae 3151
8130926d
LP
3152 r = extract_first_word(&p, &word, NULL, 0);
3153 if (r == 0)
063c4b1a 3154 return 0;
8130926d 3155 if (r == -ENOMEM)
74051b9b 3156 return log_oom();
8130926d
LP
3157 if (r < 0) {
3158 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
063c4b1a 3159 return 0;
8130926d 3160 }
8351ceae 3161
8cfa775f
YW
3162 r = parse_syscall_and_errno(word, &name, &num);
3163 if (r < 0) {
3164 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse syscall:errno, ignoring: %s", word);
3165 continue;
3166 }
3167
58f6ab44 3168 r = seccomp_parse_syscall_filter(
acd142af
LP
3169 name, num, c->syscall_filter,
3170 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
3171 (invert ? SECCOMP_PARSE_INVERT : 0)|
6b000af4 3172 (c->syscall_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
acd142af 3173 unit, filename, line);
201c1cc2
TM
3174 if (r < 0)
3175 return r;
c0467cf3 3176 }
17df7223
LP
3177}
3178
9df2cdd8
TM
3179int config_parse_syscall_log(
3180 const char *unit,
3181 const char *filename,
3182 unsigned line,
3183 const char *section,
3184 unsigned section_line,
3185 const char *lvalue,
3186 int ltype,
3187 const char *rvalue,
3188 void *data,
3189 void *userdata) {
3190
3191 ExecContext *c = data;
3192 _unused_ const Unit *u = userdata;
3193 bool invert = false;
3194 const char *p;
3195 int r;
3196
3197 assert(filename);
3198 assert(lvalue);
3199 assert(rvalue);
3200 assert(u);
3201
3202 if (isempty(rvalue)) {
3203 /* Empty assignment resets the list */
3204 c->syscall_log = hashmap_free(c->syscall_log);
3205 c->syscall_log_allow_list = false;
3206 return 0;
3207 }
3208
3209 if (rvalue[0] == '~') {
3210 invert = true;
3211 rvalue++;
3212 }
3213
3214 if (!c->syscall_log) {
3215 c->syscall_log = hashmap_new(NULL);
3216 if (!c->syscall_log)
3217 return log_oom();
3218
3219 if (invert)
3220 /* Log everything but the ones listed */
3221 c->syscall_log_allow_list = false;
3222 else
3223 /* Log nothing but the ones listed */
3224 c->syscall_log_allow_list = true;
3225 }
3226
3227 p = rvalue;
3228 for (;;) {
3229 _cleanup_free_ char *word = NULL, *name = NULL;
3230 int num;
3231
3232 r = extract_first_word(&p, &word, NULL, 0);
3233 if (r == 0)
3234 return 0;
3235 if (r == -ENOMEM)
3236 return log_oom();
3237 if (r < 0) {
3238 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
3239 return 0;
3240 }
3241
3242 r = parse_syscall_and_errno(word, &name, &num);
3243 if (r < 0 || num >= 0) { /* errno code not allowed */
3244 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse syscall, ignoring: %s", word);
3245 continue;
3246 }
3247
3248 r = seccomp_parse_syscall_filter(
3249 name, 0, c->syscall_log,
3250 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
3251 (invert ? SECCOMP_PARSE_INVERT : 0)|
3252 (c->syscall_log_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
3253 unit, filename, line);
3254 if (r < 0)
3255 return r;
3256 }
3257}
3258
57183d11
LP
3259int config_parse_syscall_archs(
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
d3b1c508 3271 Set **archs = data;
57183d11
LP
3272 int r;
3273
3274 if (isempty(rvalue)) {
525d3cc7 3275 *archs = set_free(*archs);
57183d11
LP
3276 return 0;
3277 }
3278
323dda78 3279 for (const char *p = rvalue;;) {
035fe294 3280 _cleanup_free_ char *word = NULL;
57183d11
LP
3281 uint32_t a;
3282
4ec85141 3283 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
035fe294
ZJS
3284 if (r == 0)
3285 return 0;
3286 if (r == -ENOMEM)
57183d11 3287 return log_oom();
035fe294
ZJS
3288 if (r < 0) {
3289 log_syntax(unit, LOG_WARNING, filename, line, r,
3290 "Invalid syntax, ignoring: %s", rvalue);
3291 return 0;
3292 }
57183d11 3293
035fe294 3294 r = seccomp_arch_from_string(word, &a);
57183d11 3295 if (r < 0) {
323dda78 3296 log_syntax(unit, LOG_WARNING, filename, line, r,
035fe294 3297 "Failed to parse system call architecture \"%s\", ignoring: %m", word);
57183d11
LP
3298 continue;
3299 }
3300
de7fef4b 3301 r = set_ensure_put(archs, NULL, UINT32_TO_PTR(a + 1));
57183d11
LP
3302 if (r < 0)
3303 return log_oom();
3304 }
57183d11
LP
3305}
3306
17df7223
LP
3307int config_parse_syscall_errno(
3308 const char *unit,
3309 const char *filename,
3310 unsigned line,
3311 const char *section,
3312 unsigned section_line,
3313 const char *lvalue,
3314 int ltype,
3315 const char *rvalue,
3316 void *data,
3317 void *userdata) {
3318
3319 ExecContext *c = data;
3320 int e;
3321
3322 assert(filename);
3323 assert(lvalue);
3324 assert(rvalue);
3325
005bfaf1 3326 if (isempty(rvalue) || streq(rvalue, "kill")) {
17df7223 3327 /* Empty assignment resets to KILL */
005bfaf1 3328 c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
17df7223 3329 return 0;
8351ceae
LP
3330 }
3331
3df90f24
YW
3332 e = parse_errno(rvalue);
3333 if (e <= 0) {
323dda78 3334 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
17df7223
LP
3335 return 0;
3336 }
8351ceae 3337
17df7223 3338 c->syscall_errno = e;
8351ceae
LP
3339 return 0;
3340}
4298d0b5
LP
3341
3342int config_parse_address_families(
3343 const char *unit,
3344 const char *filename,
3345 unsigned line,
3346 const char *section,
3347 unsigned section_line,
3348 const char *lvalue,
3349 int ltype,
3350 const char *rvalue,
3351 void *data,
3352 void *userdata) {
3353
3354 ExecContext *c = data;
4298d0b5 3355 bool invert = false;
4298d0b5
LP
3356 int r;
3357
3358 assert(filename);
3359 assert(lvalue);
3360 assert(rvalue);
4298d0b5
LP
3361
3362 if (isempty(rvalue)) {
3363 /* Empty assignment resets the list */
525d3cc7 3364 c->address_families = set_free(c->address_families);
6b000af4 3365 c->address_families_allow_list = false;
4298d0b5
LP
3366 return 0;
3367 }
3368
3369 if (rvalue[0] == '~') {
3370 invert = true;
3371 rvalue++;
3372 }
3373
3374 if (!c->address_families) {
d5099efc 3375 c->address_families = set_new(NULL);
4298d0b5
LP
3376 if (!c->address_families)
3377 return log_oom();
3378
6b000af4 3379 c->address_families_allow_list = !invert;
4298d0b5
LP
3380 }
3381
323dda78 3382 for (const char *p = rvalue;;) {
035fe294 3383 _cleanup_free_ char *word = NULL;
4298d0b5
LP
3384 int af;
3385
4ec85141 3386 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
035fe294
ZJS
3387 if (r == 0)
3388 return 0;
3389 if (r == -ENOMEM)
4298d0b5 3390 return log_oom();
035fe294
ZJS
3391 if (r < 0) {
3392 log_syntax(unit, LOG_WARNING, filename, line, r,
3393 "Invalid syntax, ignoring: %s", rvalue);
3394 return 0;
3395 }
4298d0b5 3396
035fe294 3397 af = af_from_name(word);
acf4d158 3398 if (af < 0) {
323dda78 3399 log_syntax(unit, LOG_WARNING, filename, line, af,
063c4b1a 3400 "Failed to parse address family, ignoring: %s", word);
4298d0b5
LP
3401 continue;
3402 }
3403
3404 /* If we previously wanted to forbid an address family and now
035fe294 3405 * we want to allow it, then just remove it from the list.
4298d0b5 3406 */
6b000af4 3407 if (!invert == c->address_families_allow_list) {
4298d0b5 3408 r = set_put(c->address_families, INT_TO_PTR(af));
4298d0b5
LP
3409 if (r < 0)
3410 return log_oom();
3411 } else
3412 set_remove(c->address_families, INT_TO_PTR(af));
3413 }
4298d0b5 3414}
add00535
LP
3415
3416int config_parse_restrict_namespaces(
3417 const char *unit,
3418 const char *filename,
3419 unsigned line,
3420 const char *section,
3421 unsigned section_line,
3422 const char *lvalue,
3423 int ltype,
3424 const char *rvalue,
3425 void *data,
3426 void *userdata) {
3427
3428 ExecContext *c = data;
aa9d574d 3429 unsigned long flags;
add00535
LP
3430 bool invert = false;
3431 int r;
3432
3433 if (isempty(rvalue)) {
3434 /* Reset to the default. */
aa9d574d
YW
3435 c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL;
3436 return 0;
3437 }
3438
3439 /* Boolean parameter ignores the previous settings */
3440 r = parse_boolean(rvalue);
3441 if (r > 0) {
3442 c->restrict_namespaces = 0;
3443 return 0;
3444 } else if (r == 0) {
add00535
LP
3445 c->restrict_namespaces = NAMESPACE_FLAGS_ALL;
3446 return 0;
3447 }
3448
3449 if (rvalue[0] == '~') {
3450 invert = true;
3451 rvalue++;
3452 }
3453
aa9d574d
YW
3454 /* Not a boolean argument, in this case it's a list of namespace types. */
3455 r = namespace_flags_from_string(rvalue, &flags);
3456 if (r < 0) {
323dda78 3457 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue);
aa9d574d 3458 return 0;
add00535
LP
3459 }
3460
aa9d574d
YW
3461 if (c->restrict_namespaces == NAMESPACE_FLAGS_INITIAL)
3462 /* Initial assignment. Just set the value. */
3463 c->restrict_namespaces = invert ? (~flags) & NAMESPACE_FLAGS_ALL : flags;
3464 else
3465 /* Merge the value with the previous one. */
3466 SET_FLAG(c->restrict_namespaces, flags, !invert);
add00535
LP
3467
3468 return 0;
3469}
c0467cf3 3470#endif
8351ceae 3471
a016b922
LP
3472int config_parse_unit_slice(
3473 const char *unit,
3474 const char *filename,
3475 unsigned line,
3476 const char *section,
71a61510 3477 unsigned section_line,
a016b922
LP
3478 const char *lvalue,
3479 int ltype,
3480 const char *rvalue,
3481 void *data,
3482 void *userdata) {
3483
063c4b1a 3484 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a016b922 3485 _cleanup_free_ char *k = NULL;
abc9fa1c 3486 Unit *u = userdata, *slice;
a016b922
LP
3487 int r;
3488
3489 assert(filename);
3490 assert(lvalue);
3491 assert(rvalue);
3492 assert(u);
3493
19f6d710 3494 r = unit_name_printf(u, rvalue, &k);
d79200e2 3495 if (r < 0) {
323dda78 3496 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
d79200e2 3497 return 0;
19f6d710 3498 }
a016b922 3499
063c4b1a 3500 r = manager_load_unit(u->manager, k, NULL, &error, &slice);
a016b922 3501 if (r < 0) {
323dda78 3502 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to load slice unit %s, ignoring: %s", k, bus_error_message(&error, r));
a016b922
LP
3503 return 0;
3504 }
3505
d79200e2
LP
3506 r = unit_set_slice(u, slice);
3507 if (r < 0) {
323dda78 3508 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to assign slice %s to unit %s, ignoring: %m", slice->id, u->id);
a016b922
LP
3509 return 0;
3510 }
3511
a016b922
LP
3512 return 0;
3513}
3514
b2f8b02e
LP
3515int config_parse_cpu_quota(
3516 const char *unit,
3517 const char *filename,
3518 unsigned line,
3519 const char *section,
3520 unsigned section_line,
3521 const char *lvalue,
3522 int ltype,
3523 const char *rvalue,
3524 void *data,
3525 void *userdata) {
3526
3527 CGroupContext *c = data;
9184ca48 3528 int r;
b2f8b02e
LP
3529
3530 assert(filename);
3531 assert(lvalue);
3532 assert(rvalue);
3533
3534 if (isempty(rvalue)) {
3a43da28 3535 c->cpu_quota_per_sec_usec = USEC_INFINITY;
b2f8b02e
LP
3536 return 0;
3537 }
3538
f806dfd3 3539 r = parse_permille_unbounded(rvalue);
9184ca48 3540 if (r <= 0) {
323dda78 3541 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid CPU quota '%s', ignoring.", rvalue);
9a054909 3542 return 0;
b2f8b02e
LP
3543 }
3544
f806dfd3 3545 c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 1000U;
b2f8b02e
LP
3546 return 0;
3547}
3548
0b8d3075 3549int config_parse_allowed_cpus(
047f5d63
PH
3550 const char *unit,
3551 const char *filename,
3552 unsigned line,
3553 const char *section,
3554 unsigned section_line,
3555 const char *lvalue,
3556 int ltype,
3557 const char *rvalue,
3558 void *data,
3559 void *userdata) {
3560
3561 CGroupContext *c = data;
3562
3563 (void) parse_cpu_set_extend(rvalue, &c->cpuset_cpus, true, unit, filename, line, lvalue);
3564
3565 return 0;
3566}
3567
0b8d3075 3568int config_parse_allowed_mems(
047f5d63
PH
3569 const char *unit,
3570 const char *filename,
3571 unsigned line,
3572 const char *section,
3573 unsigned section_line,
3574 const char *lvalue,
3575 int ltype,
3576 const char *rvalue,
3577 void *data,
3578 void *userdata) {
3579
3580 CGroupContext *c = data;
3581
3582 (void) parse_cpu_set_extend(rvalue, &c->cpuset_mems, true, unit, filename, line, lvalue);
3583
3584 return 0;
3585}
3586
4ad49000
LP
3587int config_parse_memory_limit(
3588 const char *unit,
3589 const char *filename,
3590 unsigned line,
3591 const char *section,
71a61510 3592 unsigned section_line,
4ad49000
LP
3593 const char *lvalue,
3594 int ltype,
3595 const char *rvalue,
3596 void *data,
3597 void *userdata) {
3598
3599 CGroupContext *c = data;
da4d897e 3600 uint64_t bytes = CGROUP_LIMIT_MAX;
4ad49000
LP
3601 int r;
3602
67e2baff
MK
3603 if (isempty(rvalue) && STR_IN_SET(lvalue, "DefaultMemoryLow",
3604 "DefaultMemoryMin",
3605 "MemoryLow",
3606 "MemoryMin"))
db2b8d2e 3607 bytes = CGROUP_LIMIT_MIN;
67e2baff 3608 else if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
875ae566 3609
f806dfd3 3610 r = parse_permille(rvalue);
875ae566
LP
3611 if (r < 0) {
3612 r = parse_size(rvalue, 1024, &bytes);
3613 if (r < 0) {
323dda78 3614 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid memory limit '%s', ignoring: %m", rvalue);
875ae566
LP
3615 return 0;
3616 }
3617 } else
f806dfd3 3618 bytes = physical_memory_scale(r, 1000U);
875ae566 3619
906bdbf5 3620 if (bytes >= UINT64_MAX ||
22bf131b 3621 (bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) {
323dda78 3622 log_syntax(unit, LOG_WARNING, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue);
da4d897e
TH
3623 return 0;
3624 }
4ad49000
LP
3625 }
3626
c52db42b 3627 if (streq(lvalue, "DefaultMemoryLow")) {
db2b8d2e 3628 c->default_memory_low = bytes;
c52db42b 3629 c->default_memory_low_set = true;
7ad5439e 3630 } else if (streq(lvalue, "DefaultMemoryMin")) {
db2b8d2e 3631 c->default_memory_min = bytes;
7ad5439e 3632 c->default_memory_min_set = true;
7ad5439e 3633 } else if (streq(lvalue, "MemoryMin")) {
48422635 3634 c->memory_min = bytes;
311a0e2e 3635 c->memory_min_set = true;
7ad5439e 3636 } else if (streq(lvalue, "MemoryLow")) {
da4d897e 3637 c->memory_low = bytes;
311a0e2e 3638 c->memory_low_set = true;
c52db42b 3639 } else if (streq(lvalue, "MemoryHigh"))
da4d897e
TH
3640 c->memory_high = bytes;
3641 else if (streq(lvalue, "MemoryMax"))
3642 c->memory_max = bytes;
96e131ea
WC
3643 else if (streq(lvalue, "MemorySwapMax"))
3644 c->memory_swap_max = bytes;
3645 else if (streq(lvalue, "MemoryLimit"))
da4d897e 3646 c->memory_limit = bytes;
96e131ea
WC
3647 else
3648 return -EINVAL;
4ad49000 3649
4ad49000
LP
3650 return 0;
3651}
3652
03a7b521
LP
3653int config_parse_tasks_max(
3654 const char *unit,
3655 const char *filename,
3656 unsigned line,
3657 const char *section,
3658 unsigned section_line,
3659 const char *lvalue,
3660 int ltype,
3661 const char *rvalue,
3662 void *data,
3663 void *userdata) {
3664
47538b76 3665 const Unit *u = userdata;
3a0f06c4
ZJS
3666 TasksMax *tasks_max = data;
3667 uint64_t v;
03a7b521
LP
3668 int r;
3669
f5058264 3670 if (isempty(rvalue)) {
3a0f06c4 3671 *tasks_max = u ? u->manager->default_tasks_max : TASKS_MAX_UNSET;
f5058264
TH
3672 return 0;
3673 }
3674
3675 if (streq(rvalue, "infinity")) {
3a0f06c4 3676 *tasks_max = TASKS_MAX_UNSET;
03a7b521
LP
3677 return 0;
3678 }
3679
f806dfd3 3680 r = parse_permille(rvalue);
3a0f06c4
ZJS
3681 if (r >= 0)
3682 *tasks_max = (TasksMax) { r, 1000U }; /* r‰ */
3683 else {
f5058264 3684 r = safe_atou64(rvalue, &v);
83f8e808 3685 if (r < 0) {
323dda78 3686 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid maximum tasks value '%s', ignoring: %m", rvalue);
83f8e808
LP
3687 return 0;
3688 }
83f8e808 3689
3a0f06c4 3690 if (v <= 0 || v >= UINT64_MAX) {
323dda78 3691 log_syntax(unit, LOG_WARNING, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
3a0f06c4
ZJS
3692 return 0;
3693 }
3694
3695 *tasks_max = (TasksMax) { v };
03a7b521
LP
3696 }
3697
3698 return 0;
3699}
3700
02638280
LP
3701int config_parse_delegate(
3702 const char *unit,
3703 const char *filename,
3704 unsigned line,
3705 const char *section,
3706 unsigned section_line,
3707 const char *lvalue,
3708 int ltype,
3709 const char *rvalue,
3710 void *data,
3711 void *userdata) {
3712
3713 CGroupContext *c = data;
ecae73d7 3714 UnitType t;
02638280
LP
3715 int r;
3716
ecae73d7
ZJS
3717 t = unit_name_to_type(unit);
3718 assert(t != _UNIT_TYPE_INVALID);
3719
3720 if (!unit_vtable[t]->can_delegate) {
323dda78 3721 log_syntax(unit, LOG_WARNING, filename, line, 0, "Delegate= setting not supported for this unit type, ignoring.");
ecae73d7
ZJS
3722 return 0;
3723 }
3724
02638280
LP
3725 /* We either accept a boolean value, which may be used to turn on delegation for all controllers, or turn it
3726 * off for all. Or it takes a list of controller names, in which case we add the specified controllers to the
3727 * mask to delegate. */
3728
1bdfc7b9 3729 if (isempty(rvalue)) {
d48013f8
YW
3730 /* An empty string resets controllers and set Delegate=yes. */
3731 c->delegate = true;
1bdfc7b9
YW
3732 c->delegate_controllers = 0;
3733 return 0;
3734 }
3735
02638280
LP
3736 r = parse_boolean(rvalue);
3737 if (r < 0) {
02638280
LP
3738 CGroupMask mask = 0;
3739
323dda78 3740 for (const char *p = rvalue;;) {
02638280
LP
3741 _cleanup_free_ char *word = NULL;
3742 CGroupController cc;
3743
4ec85141 3744 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
02638280
LP
3745 if (r == 0)
3746 break;
3747 if (r == -ENOMEM)
3748 return log_oom();
3749 if (r < 0) {
323dda78 3750 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
ff1b8455 3751 return 0;
02638280
LP
3752 }
3753
3754 cc = cgroup_controller_from_string(word);
3755 if (cc < 0) {
323dda78 3756 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid controller name '%s', ignoring", word);
02638280
LP
3757 continue;
3758 }
3759
3760 mask |= CGROUP_CONTROLLER_TO_MASK(cc);
3761 }
3762
3763 c->delegate = true;
3764 c->delegate_controllers |= mask;
3765
3766 } else if (r > 0) {
3767 c->delegate = true;
3768 c->delegate_controllers = _CGROUP_MASK_ALL;
3769 } else {
3770 c->delegate = false;
3771 c->delegate_controllers = 0;
3772 }
3773
3774 return 0;
3775}
3776
4ad49000
LP
3777int config_parse_device_allow(
3778 const char *unit,
3779 const char *filename,
3780 unsigned line,
3781 const char *section,
71a61510 3782 unsigned section_line,
4ad49000
LP
3783 const char *lvalue,
3784 int ltype,
3785 const char *rvalue,
3786 void *data,
3787 void *userdata) {
3788
c9f620bf 3789 _cleanup_free_ char *path = NULL, *resolved = NULL;
4ad49000 3790 CGroupContext *c = data;
c9f620bf 3791 const char *p = rvalue;
1116e14c 3792 int r;
4ad49000
LP
3793
3794 if (isempty(rvalue)) {
3795 while (c->device_allow)
3796 cgroup_context_free_device_allow(c, c->device_allow);
3797
3798 return 0;
3799 }
3800
4ec85141 3801 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
c9f620bf
YW
3802 if (r == -ENOMEM)
3803 return log_oom();
508f63b4 3804 if (r < 0) {
1116e14c 3805 log_syntax(unit, LOG_WARNING, filename, line, r,
c9f620bf
YW
3806 "Invalid syntax, ignoring: %s", rvalue);
3807 return 0;
3808 }
3809 if (r == 0) {
3810 log_syntax(unit, LOG_WARNING, filename, line, 0,
3811 "Failed to extract device path and rights from '%s', ignoring.", rvalue);
20d52ab6 3812 return 0;
1116e14c
NBS
3813 }
3814
c9f620bf
YW
3815 r = unit_full_printf(userdata, path, &resolved);
3816 if (r < 0) {
3817 log_syntax(unit, LOG_WARNING, filename, line, r,
3818 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4ad49000
LP
3819 return 0;
3820 }
3821
49fe5c09 3822 if (!STARTSWITH_SET(resolved, "block-", "char-")) {
2f4d31c1 3823
57e84e75
LP
3824 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3825 if (r < 0)
3826 return 0;
3827
3828 if (!valid_device_node_path(resolved)) {
323dda78 3829 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid device node path '%s', ignoring.", resolved);
57e84e75
LP
3830 return 0;
3831 }
c9f620bf 3832 }
4ad49000 3833
c9f620bf 3834 if (!isempty(p) && !in_charset(p, "rwm")) {
323dda78 3835 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid device rights '%s', ignoring.", p);
4ad49000
LP
3836 return 0;
3837 }
3838
fd870bac 3839 return cgroup_add_device_allow(c, resolved, p);
4ad49000
LP
3840}
3841
13c31542
TH
3842int config_parse_io_device_weight(
3843 const char *unit,
3844 const char *filename,
3845 unsigned line,
3846 const char *section,
3847 unsigned section_line,
3848 const char *lvalue,
3849 int ltype,
3850 const char *rvalue,
3851 void *data,
3852 void *userdata) {
3853
c9f620bf 3854 _cleanup_free_ char *path = NULL, *resolved = NULL;
13c31542
TH
3855 CGroupIODeviceWeight *w;
3856 CGroupContext *c = data;
c9f620bf 3857 const char *p = rvalue;
13c31542 3858 uint64_t u;
13c31542
TH
3859 int r;
3860
3861 assert(filename);
3862 assert(lvalue);
3863 assert(rvalue);
3864
3865 if (isempty(rvalue)) {
3866 while (c->io_device_weights)
3867 cgroup_context_free_io_device_weight(c, c->io_device_weights);
3868
3869 return 0;
3870 }
3871
4ec85141 3872 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
c9f620bf
YW
3873 if (r == -ENOMEM)
3874 return log_oom();
3875 if (r < 0) {
3876 log_syntax(unit, LOG_WARNING, filename, line, r,
3877 "Invalid syntax, ignoring: %s", rvalue);
3878 return 0;
3879 }
3880 if (r == 0 || isempty(p)) {
3881 log_syntax(unit, LOG_WARNING, filename, line, 0,
3882 "Failed to extract device path and weight from '%s', ignoring.", rvalue);
13c31542
TH
3883 return 0;
3884 }
3885
c9f620bf
YW
3886 r = unit_full_printf(userdata, path, &resolved);
3887 if (r < 0) {
3888 log_syntax(unit, LOG_WARNING, filename, line, r,
3889 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3890 return 0;
3891 }
13c31542 3892
2f4d31c1
YW
3893 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3894 if (r < 0)
3895 return 0;
3896
c9f620bf 3897 r = cg_weight_parse(p, &u);
13c31542 3898 if (r < 0) {
323dda78 3899 log_syntax(unit, LOG_WARNING, filename, line, r, "IO weight '%s' invalid, ignoring: %m", p);
13c31542
TH
3900 return 0;
3901 }
3902
3903 assert(u != CGROUP_WEIGHT_INVALID);
3904
3905 w = new0(CGroupIODeviceWeight, 1);
3906 if (!w)
3907 return log_oom();
3908
c9f620bf 3909 w->path = TAKE_PTR(resolved);
13c31542
TH
3910 w->weight = u;
3911
3912 LIST_PREPEND(device_weights, c->io_device_weights, w);
3913 return 0;
3914}
3915
6ae4283c
TH
3916int config_parse_io_device_latency(
3917 const char *unit,
3918 const char *filename,
3919 unsigned line,
3920 const char *section,
3921 unsigned section_line,
3922 const char *lvalue,
3923 int ltype,
3924 const char *rvalue,
3925 void *data,
3926 void *userdata) {
3927
3928 _cleanup_free_ char *path = NULL, *resolved = NULL;
3929 CGroupIODeviceLatency *l;
3930 CGroupContext *c = data;
3931 const char *p = rvalue;
3932 usec_t usec;
3933 int r;
3934
3935 assert(filename);
3936 assert(lvalue);
3937 assert(rvalue);
3938
3939 if (isempty(rvalue)) {
3940 while (c->io_device_latencies)
3941 cgroup_context_free_io_device_latency(c, c->io_device_latencies);
3942
3943 return 0;
3944 }
3945
4ec85141 3946 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
6ae4283c
TH
3947 if (r == -ENOMEM)
3948 return log_oom();
3949 if (r < 0) {
3950 log_syntax(unit, LOG_WARNING, filename, line, r,
3951 "Invalid syntax, ignoring: %s", rvalue);
3952 return 0;
3953 }
3954 if (r == 0 || isempty(p)) {
3955 log_syntax(unit, LOG_WARNING, filename, line, 0,
3956 "Failed to extract device path and latency from '%s', ignoring.", rvalue);
3957 return 0;
3958 }
3959
3960 r = unit_full_printf(userdata, path, &resolved);
3961 if (r < 0) {
3962 log_syntax(unit, LOG_WARNING, filename, line, r,
3963 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
3964 return 0;
3965 }
3966
3967 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
3968 if (r < 0)
3969 return 0;
3970
323dda78
YW
3971 r = parse_sec(p, &usec);
3972 if (r < 0) {
3973 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse timer value, ignoring: %s", p);
6ae4283c
TH
3974 return 0;
3975 }
3976
3977 l = new0(CGroupIODeviceLatency, 1);
3978 if (!l)
3979 return log_oom();
3980
3981 l->path = TAKE_PTR(resolved);
3982 l->target_usec = usec;
3983
3984 LIST_PREPEND(device_latencies, c->io_device_latencies, l);
3985 return 0;
3986}
3987
13c31542
TH
3988int config_parse_io_limit(
3989 const char *unit,
3990 const char *filename,
3991 unsigned line,
3992 const char *section,
3993 unsigned section_line,
3994 const char *lvalue,
3995 int ltype,
3996 const char *rvalue,
3997 void *data,
3998 void *userdata) {
3999
c9f620bf 4000 _cleanup_free_ char *path = NULL, *resolved = NULL;
13c31542
TH
4001 CGroupIODeviceLimit *l = NULL, *t;
4002 CGroupContext *c = data;
9be57249 4003 CGroupIOLimitType type;
c9f620bf 4004 const char *p = rvalue;
13c31542 4005 uint64_t num;
13c31542
TH
4006 int r;
4007
4008 assert(filename);
4009 assert(lvalue);
4010 assert(rvalue);
4011
9be57249
TH
4012 type = cgroup_io_limit_type_from_string(lvalue);
4013 assert(type >= 0);
13c31542
TH
4014
4015 if (isempty(rvalue)) {
4016 LIST_FOREACH(device_limits, l, c->io_device_limits)
9be57249 4017 l->limits[type] = cgroup_io_limit_defaults[type];
13c31542
TH
4018 return 0;
4019 }
4020
4ec85141 4021 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
c9f620bf
YW
4022 if (r == -ENOMEM)
4023 return log_oom();
4024 if (r < 0) {
4025 log_syntax(unit, LOG_WARNING, filename, line, r,
4026 "Invalid syntax, ignoring: %s", rvalue);
4027 return 0;
4028 }
4029 if (r == 0 || isempty(p)) {
4030 log_syntax(unit, LOG_WARNING, filename, line, 0,
4031 "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue);
13c31542
TH
4032 return 0;
4033 }
4034
c9f620bf
YW
4035 r = unit_full_printf(userdata, path, &resolved);
4036 if (r < 0) {
4037 log_syntax(unit, LOG_WARNING, filename, line, r,
4038 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4039 return 0;
4040 }
13c31542 4041
2f4d31c1
YW
4042 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4043 if (r < 0)
4044 return 0;
4045
9d5e9b4a 4046 if (streq("infinity", p))
13c31542 4047 num = CGROUP_LIMIT_MAX;
9d5e9b4a 4048 else {
c9f620bf 4049 r = parse_size(p, 1000, &num);
13c31542 4050 if (r < 0 || num <= 0) {
323dda78 4051 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid IO limit '%s', ignoring.", p);
13c31542
TH
4052 return 0;
4053 }
4054 }
4055
4056 LIST_FOREACH(device_limits, t, c->io_device_limits) {
c9f620bf 4057 if (path_equal(resolved, t->path)) {
13c31542
TH
4058 l = t;
4059 break;
4060 }
4061 }
4062
4063 if (!l) {
9be57249
TH
4064 CGroupIOLimitType ttype;
4065
13c31542
TH
4066 l = new0(CGroupIODeviceLimit, 1);
4067 if (!l)
4068 return log_oom();
4069
c9f620bf 4070 l->path = TAKE_PTR(resolved);
9be57249
TH
4071 for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
4072 l->limits[ttype] = cgroup_io_limit_defaults[ttype];
13c31542
TH
4073
4074 LIST_PREPEND(device_limits, c->io_device_limits, l);
4075 }
4076
9be57249 4077 l->limits[type] = num;
13c31542
TH
4078
4079 return 0;
4080}
4081
8e7076ca
LP
4082int config_parse_blockio_device_weight(
4083 const char *unit,
4084 const char *filename,
4085 unsigned line,
4086 const char *section,
71a61510 4087 unsigned section_line,
8e7076ca
LP
4088 const char *lvalue,
4089 int ltype,
4090 const char *rvalue,
4091 void *data,
4092 void *userdata) {
4093
c9f620bf 4094 _cleanup_free_ char *path = NULL, *resolved = NULL;
8e7076ca 4095 CGroupBlockIODeviceWeight *w;
4ad49000 4096 CGroupContext *c = data;
c9f620bf 4097 const char *p = rvalue;
d53d9474 4098 uint64_t u;
4ad49000
LP
4099 int r;
4100
4101 assert(filename);
4102 assert(lvalue);
4103 assert(rvalue);
4104
4105 if (isempty(rvalue)) {
4ad49000
LP
4106 while (c->blockio_device_weights)
4107 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
4108
4109 return 0;
4110 }
4111
4ec85141 4112 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
c9f620bf
YW
4113 if (r == -ENOMEM)
4114 return log_oom();
4115 if (r < 0) {
4116 log_syntax(unit, LOG_WARNING, filename, line, r,
4117 "Invalid syntax, ignoring: %s", rvalue);
4118 return 0;
4119 }
4120 if (r == 0 || isempty(p)) {
4121 log_syntax(unit, LOG_WARNING, filename, line, 0,
4122 "Failed to extract device node and weight from '%s', ignoring.", rvalue);
8e7076ca
LP
4123 return 0;
4124 }
4ad49000 4125
c9f620bf
YW
4126 r = unit_full_printf(userdata, path, &resolved);
4127 if (r < 0) {
4128 log_syntax(unit, LOG_WARNING, filename, line, r,
4129 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4130 return 0;
4131 }
4ad49000 4132
2f4d31c1
YW
4133 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4134 if (r < 0)
4135 return 0;
4136
c9f620bf 4137 r = cg_blkio_weight_parse(p, &u);
d53d9474 4138 if (r < 0) {
323dda78 4139 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid block IO weight '%s', ignoring: %m", p);
4ad49000
LP
4140 return 0;
4141 }
4142
d53d9474
LP
4143 assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
4144
8e7076ca
LP
4145 w = new0(CGroupBlockIODeviceWeight, 1);
4146 if (!w)
4147 return log_oom();
4ad49000 4148
c9f620bf 4149 w->path = TAKE_PTR(resolved);
d53d9474 4150 w->weight = u;
4ad49000 4151
71fda00f 4152 LIST_PREPEND(device_weights, c->blockio_device_weights, w);
4ad49000
LP
4153 return 0;
4154}
4155
4156int config_parse_blockio_bandwidth(
4157 const char *unit,
4158 const char *filename,
4159 unsigned line,
4160 const char *section,
71a61510 4161 unsigned section_line,
4ad49000
LP
4162 const char *lvalue,
4163 int ltype,
4164 const char *rvalue,
4165 void *data,
4166 void *userdata) {
4167
c9f620bf 4168 _cleanup_free_ char *path = NULL, *resolved = NULL;
979d0311 4169 CGroupBlockIODeviceBandwidth *b = NULL, *t;
4ad49000 4170 CGroupContext *c = data;
c9f620bf 4171 const char *p = rvalue;
59f448cf 4172 uint64_t bytes;
47c0980d 4173 bool read;
4ad49000
LP
4174 int r;
4175
4176 assert(filename);
4177 assert(lvalue);
4178 assert(rvalue);
4179
47c0980d
G
4180 read = streq("BlockIOReadBandwidth", lvalue);
4181
4ad49000 4182 if (isempty(rvalue)) {
979d0311
TH
4183 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
4184 b->rbps = CGROUP_LIMIT_MAX;
4185 b->wbps = CGROUP_LIMIT_MAX;
4186 }
4ad49000
LP
4187 return 0;
4188 }
4189
4ec85141 4190 r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
c9f620bf
YW
4191 if (r == -ENOMEM)
4192 return log_oom();
4193 if (r < 0) {
4194 log_syntax(unit, LOG_WARNING, filename, line, r,
4195 "Invalid syntax, ignoring: %s", rvalue);
4196 return 0;
4197 }
4198 if (r == 0 || isempty(p)) {
4199 log_syntax(unit, LOG_WARNING, filename, line, 0,
4200 "Failed to extract device node and bandwidth from '%s', ignoring.", rvalue);
4ad49000
LP
4201 return 0;
4202 }
4203
c9f620bf
YW
4204 r = unit_full_printf(userdata, path, &resolved);
4205 if (r < 0) {
4206 log_syntax(unit, LOG_WARNING, filename, line, r,
4207 "Failed to resolve unit specifiers in '%s', ignoring: %m", path);
4208 return 0;
4209 }
4ad49000 4210
2f4d31c1
YW
4211 r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue);
4212 if (r < 0)
4213 return 0;
4214
c9f620bf 4215 r = parse_size(p, 1000, &bytes);
4ad49000 4216 if (r < 0 || bytes <= 0) {
323dda78 4217 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid Block IO Bandwidth '%s', ignoring.", p);
4ad49000
LP
4218 return 0;
4219 }
4220
979d0311 4221 LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
c9f620bf 4222 if (path_equal(resolved, t->path)) {
979d0311
TH
4223 b = t;
4224 break;
4225 }
4226 }
4ad49000 4227
979d0311
TH
4228 if (!t) {
4229 b = new0(CGroupBlockIODeviceBandwidth, 1);
4230 if (!b)
4231 return log_oom();
4232
c9f620bf 4233 b->path = TAKE_PTR(resolved);
979d0311
TH
4234 b->rbps = CGROUP_LIMIT_MAX;
4235 b->wbps = CGROUP_LIMIT_MAX;
4236
4237 LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
4238 }
4ad49000 4239
979d0311
TH
4240 if (read)
4241 b->rbps = bytes;
4242 else
4243 b->wbps = bytes;
4ad49000
LP
4244
4245 return 0;
4246}
4247
d420282b
LP
4248int config_parse_job_mode_isolate(
4249 const char *unit,
4250 const char *filename,
4251 unsigned line,
4252 const char *section,
4253 unsigned section_line,
4254 const char *lvalue,
4255 int ltype,
4256 const char *rvalue,
4257 void *data,
4258 void *userdata) {
4259
4260 JobMode *m = data;
4261 int r;
4262
4263 assert(filename);
4264 assert(lvalue);
4265 assert(rvalue);
4266
4267 r = parse_boolean(rvalue);
4268 if (r < 0) {
323dda78 4269 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
d420282b
LP
4270 return 0;
4271 }
4272
8ab39347
YW
4273 log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue);
4274
d420282b
LP
4275 *m = r ? JOB_ISOLATE : JOB_REPLACE;
4276 return 0;
4277}
4278
3536f49e 4279int config_parse_exec_directories(
e66cf1a3
LP
4280 const char *unit,
4281 const char *filename,
4282 unsigned line,
4283 const char *section,
4284 unsigned section_line,
4285 const char *lvalue,
4286 int ltype,
4287 const char *rvalue,
4288 void *data,
4289 void *userdata) {
4290
a2a5291b 4291 char***rt = data;
47538b76 4292 const Unit *u = userdata;
e66cf1a3
LP
4293 int r;
4294
4295 assert(filename);
4296 assert(lvalue);
4297 assert(rvalue);
4298 assert(data);
4299
4300 if (isempty(rvalue)) {
4301 /* Empty assignment resets the list */
6796073e 4302 *rt = strv_free(*rt);
e66cf1a3
LP
4303 return 0;
4304 }
4305
323dda78 4306 for (const char *p = rvalue;;) {
035fe294 4307 _cleanup_free_ char *word = NULL, *k = NULL;
e66cf1a3 4308
4ec85141 4309 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
035fe294 4310 if (r == -ENOMEM)
e66cf1a3 4311 return log_oom();
035fe294
ZJS
4312 if (r < 0) {
4313 log_syntax(unit, LOG_WARNING, filename, line, r,
4314 "Invalid syntax, ignoring: %s", rvalue);
4315 return 0;
4316 }
091e9efe
LP
4317 if (r == 0)
4318 return 0;
e66cf1a3 4319
18913df9 4320 r = unit_full_printf(u, word, &k);
9b5864d9 4321 if (r < 0) {
330f8990 4322 log_syntax(unit, LOG_WARNING, filename, line, r,
063c4b1a 4323 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
9b5864d9
MG
4324 continue;
4325 }
4326
2f4d31c1
YW
4327 r = path_simplify_and_warn(k, PATH_CHECK_RELATIVE, unit, filename, line, lvalue);
4328 if (r < 0)
e8865688 4329 continue;
e8865688
LP
4330
4331 if (path_startswith(k, "private")) {
330f8990 4332 log_syntax(unit, LOG_WARNING, filename, line, 0,
4605de11 4333 "%s= path can't be 'private', ignoring assignment: %s", lvalue, word);
e66cf1a3
LP
4334 continue;
4335 }
4336
035fe294 4337 r = strv_push(rt, k);
e66cf1a3
LP
4338 if (r < 0)
4339 return log_oom();
035fe294 4340 k = NULL;
e66cf1a3 4341 }
e66cf1a3
LP
4342}
4343
bb0c0d6f
LP
4344int config_parse_set_credential(
4345 const char *unit,
4346 const char *filename,
4347 unsigned line,
4348 const char *section,
4349 unsigned section_line,
4350 const char *lvalue,
4351 int ltype,
4352 const char *rvalue,
4353 void *data,
4354 void *userdata) {
4355
4356 _cleanup_free_ char *word = NULL, *k = NULL, *unescaped = NULL;
4357 ExecContext *context = data;
4358 ExecSetCredential *old;
4359 Unit *u = userdata;
4360 const char *p;
4361 int r, l;
4362
4363 assert(filename);
4364 assert(lvalue);
4365 assert(rvalue);
4366 assert(context);
4367
4368 if (isempty(rvalue)) {
4369 /* Empty assignment resets the list */
4370 context->set_credentials = hashmap_free(context->set_credentials);
4371 return 0;
4372 }
4373
4374 p = rvalue;
4375 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4376 if (r == -ENOMEM)
4377 return log_oom();
4378 if (r <= 0 || !p) {
4379 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
4380 return 0;
4381 }
4382
4383 r = unit_full_printf(u, word, &k);
4384 if (r < 0) {
4385 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4386 return 0;
4387 }
4388 if (!credential_name_valid(k)) {
4389 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", k);
4390 return 0;
4391 }
4392
4393 /* We support escape codes here, so that users can insert trailing \n if they like */
4394 l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped);
4395 if (l < 0) {
4396 log_syntax(unit, LOG_WARNING, filename, line, l, "Can't unescape \"%s\", ignoring: %m", p);
4397 return 0;
4398 }
4399
4400 old = hashmap_get(context->set_credentials, k);
4401 if (old) {
4402 free_and_replace(old->data, unescaped);
4403 old->size = l;
4404 } else {
4405 _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL;
4406
4407 sc = new0(ExecSetCredential, 1);
4408 if (!sc)
4409 return log_oom();
4410
4411 sc->id = TAKE_PTR(k);
4412 sc->data = TAKE_PTR(unescaped);
4413 sc->size = l;
4414
4415 r = hashmap_ensure_allocated(&context->set_credentials, &exec_set_credential_hash_ops);
4416 if (r < 0)
4417 return r;
4418
4419 r = hashmap_put(context->set_credentials, sc->id, sc);
4420 if (r < 0)
4421 return log_oom();
4422
4423 TAKE_PTR(sc);
4424 }
4425
4426 return 0;
4427}
4428
4429int config_parse_load_credential(
4430 const char *unit,
4431 const char *filename,
4432 unsigned line,
4433 const char *section,
4434 unsigned section_line,
4435 const char *lvalue,
4436 int ltype,
4437 const char *rvalue,
4438 void *data,
4439 void *userdata) {
4440
4441 _cleanup_free_ char *word = NULL, *k = NULL, *q = NULL;
4442 ExecContext *context = data;
4443 Unit *u = userdata;
4444 const char *p;
4445 int r;
4446
4447 assert(filename);
4448 assert(lvalue);
4449 assert(rvalue);
4450 assert(context);
4451
4452 if (isempty(rvalue)) {
4453 /* Empty assignment resets the list */
4454 context->load_credentials = strv_free(context->load_credentials);
4455 return 0;
4456 }
4457
4458 p = rvalue;
4459 r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
4460 if (r == -ENOMEM)
4461 return log_oom();
4462 if (r <= 0) {
4463 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
4464 return 0;
4465 }
4466
4467 r = unit_full_printf(u, word, &k);
4468 if (r < 0) {
4469 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
4470 return 0;
4471 }
4472 if (!credential_name_valid(k)) {
4473 log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", k);
4474 return 0;
4475 }
4476 r = unit_full_printf(u, p, &q);
4477 if (r < 0) {
4478 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", p);
4479 return 0;
4480 }
4481 if (path_is_absolute(q) ? !path_is_normalized(q) : !credential_name_valid(q)) {
4482 log_syntax(unit, LOG_WARNING, filename, line, r, "Credential source \"%s\" not valid, ignoring.", q);
4483 return 0;
4484 }
4485
4486 r = strv_consume_pair(&context->load_credentials, TAKE_PTR(k), TAKE_PTR(q));
4487 if (r < 0)
4488 return log_oom();
4489
4490 return 0;
4491}
4492
3af00fb8
LP
4493int config_parse_set_status(
4494 const char *unit,
4495 const char *filename,
4496 unsigned line,
4497 const char *section,
4498 unsigned section_line,
4499 const char *lvalue,
4500 int ltype,
4501 const char *rvalue,
4502 void *data,
4503 void *userdata) {
4504
3af00fb8 4505 ExitStatusSet *status_set = data;
7896ad8f 4506 int r;
3af00fb8
LP
4507
4508 assert(filename);
4509 assert(lvalue);
4510 assert(rvalue);
7896ad8f 4511 assert(status_set);
3af00fb8 4512
3e2d435b 4513 /* Empty assignment resets the list */
3af00fb8 4514 if (isempty(rvalue)) {
3e2d435b 4515 exit_status_set_free(status_set);
3af00fb8
LP
4516 return 0;
4517 }
4518
7896ad8f
ZJS
4519 for (const char *p = rvalue;;) {
4520 _cleanup_free_ char *word = NULL;
23d5dd16 4521 Bitmap *bitmap;
3af00fb8 4522
7896ad8f 4523 r = extract_first_word(&p, &word, NULL, 0);
323dda78
YW
4524 if (r == -ENOMEM)
4525 return log_oom();
4526 if (r < 0) {
4527 log_syntax(unit, LOG_WARNING, filename, line, r,
4528 "Failed to parse %s=%s, ignoring: %m", lvalue, rvalue);
4529 return 0;
4530 }
7896ad8f
ZJS
4531 if (r == 0)
4532 return 0;
3af00fb8 4533
2e2ed880
ZJS
4534 /* We need to call exit_status_from_string() first, because we want
4535 * to parse numbers as exit statuses, not signals. */
3af00fb8 4536
7896ad8f 4537 r = exit_status_from_string(word);
2e2ed880
ZJS
4538 if (r >= 0) {
4539 assert(r >= 0 && r < 256);
4540 bitmap = &status_set->status;
3af00fb8 4541 } else {
7896ad8f
ZJS
4542 r = signal_from_string(word);
4543 if (r < 0) {
323dda78 4544 log_syntax(unit, LOG_WARNING, filename, line, 0,
2e2ed880 4545 "Failed to parse value, ignoring: %s", word);
1e2fd62d 4546 continue;
3af00fb8 4547 }
2e2ed880 4548 bitmap = &status_set->signal;
3af00fb8 4549 }
1e2fd62d 4550
2e2ed880 4551 r = bitmap_set(bitmap, r);
063c4b1a 4552 if (r < 0)
323dda78
YW
4553 log_syntax(unit, LOG_WARNING, filename, line, r,
4554 "Failed to set signal or status %s, ignoring: %m", word);
3af00fb8 4555 }
3af00fb8
LP
4556}
4557
94828d2d
LP
4558int config_parse_namespace_path_strv(
4559 const char *unit,
4560 const char *filename,
4561 unsigned line,
4562 const char *section,
4563 unsigned section_line,
4564 const char *lvalue,
4565 int ltype,
4566 const char *rvalue,
4567 void *data,
4568 void *userdata) {
4569
47538b76 4570 const Unit *u = userdata;
a2a5291b 4571 char*** sv = data;
94828d2d
LP
4572 int r;
4573
4574 assert(filename);
4575 assert(lvalue);
4576 assert(rvalue);
4577 assert(data);
4578
4579 if (isempty(rvalue)) {
4580 /* Empty assignment resets the list */
6796073e 4581 *sv = strv_free(*sv);
94828d2d
LP
4582 return 0;
4583 }
4584
323dda78 4585 for (const char *p = rvalue;;) {
7b07e993 4586 _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL;
20b7a007
LP
4587 const char *w;
4588 bool ignore_enoent = false, shall_prefix = false;
94828d2d 4589
4ec85141 4590 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
0293a7a8
EV
4591 if (r == 0)
4592 break;
4593 if (r == -ENOMEM)
4594 return log_oom();
727f76d7 4595 if (r < 0) {
323dda78 4596 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
727f76d7
EV
4597 return 0;
4598 }
94828d2d 4599
20b7a007
LP
4600 w = word;
4601 if (startswith(w, "-")) {
4602 ignore_enoent = true;
4603 w++;
4604 }
4605 if (startswith(w, "+")) {
4606 shall_prefix = true;
4607 w++;
4608 }
7b07e993 4609
20b7a007 4610 r = unit_full_printf(u, w, &resolved);
7b07e993 4611 if (r < 0) {
323dda78 4612 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s: %m", w);
94828d2d
LP
4613 continue;
4614 }
4615
2f4d31c1
YW
4616 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4617 if (r < 0)
7b07e993 4618 continue;
94828d2d 4619
20b7a007
LP
4620 joined = strjoin(ignore_enoent ? "-" : "",
4621 shall_prefix ? "+" : "",
4622 resolved);
7b07e993
LP
4623
4624 r = strv_push(sv, joined);
94828d2d
LP
4625 if (r < 0)
4626 return log_oom();
4627
7b07e993 4628 joined = NULL;
94828d2d
LP
4629 }
4630
4631 return 0;
4632}
4633
2abd4e38
YW
4634int config_parse_temporary_filesystems(
4635 const char *unit,
4636 const char *filename,
4637 unsigned line,
4638 const char *section,
4639 unsigned section_line,
4640 const char *lvalue,
4641 int ltype,
4642 const char *rvalue,
4643 void *data,
4644 void *userdata) {
4645
47538b76 4646 const Unit *u = userdata;
2abd4e38 4647 ExecContext *c = data;
2abd4e38
YW
4648 int r;
4649
4650 assert(filename);
4651 assert(lvalue);
4652 assert(rvalue);
4653 assert(data);
4654
4655 if (isempty(rvalue)) {
4656 /* Empty assignment resets the list */
4657 temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
4658 c->temporary_filesystems = NULL;
4659 c->n_temporary_filesystems = 0;
4660 return 0;
4661 }
4662
323dda78 4663 for (const char *p = rvalue;;) {
2abd4e38
YW
4664 _cleanup_free_ char *word = NULL, *path = NULL, *resolved = NULL;
4665 const char *w;
4666
4ec85141 4667 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
2abd4e38 4668 if (r == 0)
063c4b1a 4669 return 0;
2abd4e38
YW
4670 if (r == -ENOMEM)
4671 return log_oom();
4672 if (r < 0) {
323dda78 4673 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
2abd4e38
YW
4674 return 0;
4675 }
4676
4677 w = word;
4678 r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
063c4b1a
YW
4679 if (r == -ENOMEM)
4680 return log_oom();
4681 if (r < 0) {
323dda78 4682 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", word);
063c4b1a
YW
4683 continue;
4684 }
4685 if (r == 0) {
323dda78 4686 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid syntax, ignoring: %s", word);
063c4b1a
YW
4687 continue;
4688 }
2abd4e38
YW
4689
4690 r = unit_full_printf(u, path, &resolved);
4691 if (r < 0) {
323dda78 4692 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", path);
2abd4e38
YW
4693 continue;
4694 }
4695
2f4d31c1
YW
4696 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4697 if (r < 0)
2abd4e38 4698 continue;
2abd4e38 4699
a26fec24 4700 r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, resolved, w);
6302d1ea 4701 if (r < 0)
2abd4e38 4702 return log_oom();
2abd4e38 4703 }
2abd4e38
YW
4704}
4705
d2d6c096
LP
4706int config_parse_bind_paths(
4707 const char *unit,
4708 const char *filename,
4709 unsigned line,
4710 const char *section,
4711 unsigned section_line,
4712 const char *lvalue,
4713 int ltype,
4714 const char *rvalue,
4715 void *data,
4716 void *userdata) {
4717
4718 ExecContext *c = data;
47538b76 4719 const Unit *u = userdata;
d2d6c096
LP
4720 int r;
4721
4722 assert(filename);
4723 assert(lvalue);
4724 assert(rvalue);
4725 assert(data);
4726
4727 if (isempty(rvalue)) {
4728 /* Empty assignment resets the list */
4729 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
4730 c->bind_mounts = NULL;
4731 c->n_bind_mounts = 0;
4732 return 0;
4733 }
4734
323dda78 4735 for (const char *p = rvalue;;) {
d2d6c096 4736 _cleanup_free_ char *source = NULL, *destination = NULL;
42d43f21 4737 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
d2d6c096
LP
4738 char *s = NULL, *d = NULL;
4739 bool rbind = true, ignore_enoent = false;
4740
4ec85141 4741 r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
d2d6c096
LP
4742 if (r == 0)
4743 break;
4744 if (r == -ENOMEM)
4745 return log_oom();
4746 if (r < 0) {
323dda78 4747 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
d2d6c096
LP
4748 return 0;
4749 }
4750
42d43f21
DC
4751 r = unit_full_printf(u, source, &sresolved);
4752 if (r < 0) {
323dda78 4753 log_syntax(unit, LOG_WARNING, filename, line, r,
556a7bbe 4754 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", source);
2f4d31c1 4755 continue;
42d43f21
DC
4756 }
4757
4758 s = sresolved;
d2d6c096
LP
4759 if (s[0] == '-') {
4760 ignore_enoent = true;
4761 s++;
4762 }
4763
2f4d31c1
YW
4764 r = path_simplify_and_warn(s, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4765 if (r < 0)
4766 continue;
d2d6c096
LP
4767
4768 /* Optionally, the destination is specified. */
4769 if (p && p[-1] == ':') {
4ec85141 4770 r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
d2d6c096
LP
4771 if (r == -ENOMEM)
4772 return log_oom();
4773 if (r < 0) {
323dda78 4774 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
d2d6c096
LP
4775 return 0;
4776 }
4777 if (r == 0) {
323dda78 4778 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing argument after ':', ignoring: %s", s);
2f4d31c1 4779 continue;
d2d6c096
LP
4780 }
4781
42d43f21
DC
4782 r = unit_full_printf(u, destination, &dresolved);
4783 if (r < 0) {
323dda78 4784 log_syntax(unit, LOG_WARNING, filename, line, r,
556a7bbe 4785 "Failed to resolve specifiers in \"%s\", ignoring: %m", destination);
2f4d31c1 4786 continue;
42d43f21
DC
4787 }
4788
2f4d31c1
YW
4789 r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4790 if (r < 0)
4791 continue;
d2d6c096 4792
2f4d31c1 4793 d = dresolved;
d2d6c096
LP
4794
4795 /* Optionally, there's also a short option string specified */
4796 if (p && p[-1] == ':') {
4797 _cleanup_free_ char *options = NULL;
4798
4ec85141 4799 r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
d2d6c096
LP
4800 if (r == -ENOMEM)
4801 return log_oom();
4802 if (r < 0) {
323dda78 4803 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s: %s", lvalue, rvalue);
d2d6c096
LP
4804 return 0;
4805 }
4806
4807 if (isempty(options) || streq(options, "rbind"))
4808 rbind = true;
4809 else if (streq(options, "norbind"))
4810 rbind = false;
4811 else {
323dda78 4812 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
2f4d31c1 4813 continue;
d2d6c096
LP
4814 }
4815 }
4816 } else
4817 d = s;
4818
4819 r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
4820 &(BindMount) {
4821 .source = s,
4822 .destination = d,
4823 .read_only = !!strstr(lvalue, "ReadOnly"),
4824 .recursive = rbind,
4825 .ignore_enoent = ignore_enoent,
4826 });
4827 if (r < 0)
4828 return log_oom();
4829 }
4830
4831 return 0;
4832}
4833
b3d13314
LB
4834int config_parse_mount_images(
4835 const char *unit,
4836 const char *filename,
4837 unsigned line,
4838 const char *section,
4839 unsigned section_line,
4840 const char *lvalue,
4841 int ltype,
4842 const char *rvalue,
4843 void *data,
4844 void *userdata) {
4845
b3d13314
LB
4846 ExecContext *c = data;
4847 const Unit *u = userdata;
b3d13314
LB
4848 int r;
4849
4850 assert(filename);
4851 assert(lvalue);
4852 assert(rvalue);
4853 assert(data);
4854
4855 if (isempty(rvalue)) {
4856 /* Empty assignment resets the list */
4857 c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
4858 return 0;
4859 }
4860
323dda78 4861 for (const char *p = rvalue;;) {
427353f6
LB
4862 _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
4863 _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL;
b3d13314 4864 _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
427353f6 4865 const char *q = NULL;
b3d13314
LB
4866 char *s = NULL;
4867 bool permissive = false;
4868
427353f6 4869 r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
323dda78
YW
4870 if (r == -ENOMEM)
4871 return log_oom();
4872 if (r < 0) {
4873 log_syntax(unit, LOG_WARNING, filename, line, r,
4874 "Invalid syntax %s=%s, ignoring: %m", lvalue, rvalue);
4875 return 0;
4876 }
427353f6 4877 if (r == 0)
323dda78 4878 return 0;
427353f6
LB
4879
4880 q = tuple;
4881 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second, NULL);
323dda78
YW
4882 if (r == -ENOMEM)
4883 return log_oom();
4884 if (r < 0) {
4885 log_syntax(unit, LOG_WARNING, filename, line, r,
4886 "Invalid syntax in %s=, ignoring: %s", lvalue, tuple);
4887 return 0;
4888 }
427353f6
LB
4889 if (r == 0)
4890 continue;
4891
4892 r = unit_full_printf(u, first, &sresolved);
b3d13314 4893 if (r < 0) {
323dda78 4894 log_syntax(unit, LOG_WARNING, filename, line, r,
427353f6 4895 "Failed to resolve unit specifiers in \"%s\", ignoring: %m", first);
b3d13314
LB
4896 continue;
4897 }
4898
4899 s = sresolved;
4900 if (s[0] == '-') {
4901 permissive = true;
4902 s++;
4903 }
4904
4905 r = path_simplify_and_warn(s, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4906 if (r < 0)
4907 continue;
4908
427353f6 4909 if (isempty(second)) {
323dda78 4910 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing destination in %s, ignoring: %s", lvalue, rvalue);
b3d13314
LB
4911 continue;
4912 }
4913
427353f6 4914 r = unit_full_printf(u, second, &dresolved);
b3d13314 4915 if (r < 0) {
323dda78 4916 log_syntax(unit, LOG_WARNING, filename, line, r,
427353f6 4917 "Failed to resolve specifiers in \"%s\", ignoring: %m", second);
b3d13314
LB
4918 continue;
4919 }
4920
4921 r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
4922 if (r < 0)
4923 continue;
4924
427353f6
LB
4925 for (;;) {
4926 _cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
4927 MountOptions *o = NULL;
569a0e42 4928 PartitionDesignator partition_designator;
427353f6
LB
4929
4930 r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options, NULL);
323dda78
YW
4931 if (r == -ENOMEM)
4932 return log_oom();
4933 if (r < 0) {
4934 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", q);
4935 return 0;
4936 }
427353f6
LB
4937 if (r == 0)
4938 break;
4939 /* Single set of options, applying to the root partition/single filesystem */
4940 if (r == 1) {
4941 r = unit_full_printf(u, partition, &mount_options_resolved);
4942 if (r < 0) {
323dda78 4943 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", first);
427353f6
LB
4944 continue;
4945 }
4946
4947 o = new(MountOptions, 1);
4948 if (!o)
4949 return log_oom();
4950 *o = (MountOptions) {
4951 .partition_designator = PARTITION_ROOT,
4952 .options = TAKE_PTR(mount_options_resolved),
4953 };
4954 LIST_APPEND(mount_options, options, o);
4955
4956 break;
4957 }
4958
4959 partition_designator = partition_designator_from_string(partition);
4960 if (partition_designator < 0) {
323dda78 4961 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid partition name %s, ignoring", partition);
427353f6
LB
4962 continue;
4963 }
4964 r = unit_full_printf(u, mount_options, &mount_options_resolved);
4965 if (r < 0) {
323dda78 4966 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
427353f6
LB
4967 continue;
4968 }
4969
4970 o = new(MountOptions, 1);
4971 if (!o)
4972 return log_oom();
4973 *o = (MountOptions) {
4974 .partition_designator = partition_designator,
4975 .options = TAKE_PTR(mount_options_resolved),
4976 };
4977 LIST_APPEND(mount_options, options, o);
4978 }
4979
b3d13314
LB
4980 r = mount_image_add(&c->mount_images, &c->n_mount_images,
4981 &(MountImage) {
4982 .source = s,
4983 .destination = dresolved,
427353f6 4984 .mount_options = options,
b3d13314
LB
4985 .ignore_enoent = permissive,
4986 });
4987 if (r < 0)
4988 return log_oom();
4989 }
b3d13314
LB
4990}
4991
eae51da3
LP
4992int config_parse_job_timeout_sec(
4993 const char* unit,
4994 const char *filename,
4995 unsigned line,
4996 const char *section,
4997 unsigned section_line,
4998 const char *lvalue,
4999 int ltype,
5000 const char *rvalue,
5001 void *data,
5002 void *userdata) {
5003
5004 Unit *u = data;
5005 usec_t usec;
5006 int r;
5007
5008 assert(filename);
5009 assert(lvalue);
5010 assert(rvalue);
5011 assert(u);
5012
5013 r = parse_sec_fix_0(rvalue, &usec);
5014 if (r < 0) {
323dda78 5015 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse JobTimeoutSec= parameter, ignoring: %s", rvalue);
eae51da3
LP
5016 return 0;
5017 }
5018
5019 /* If the user explicitly changed JobTimeoutSec= also change JobRunningTimeoutSec=, for compatibility with old
c05f3c8f 5020 * versions. If JobRunningTimeoutSec= was explicitly set, avoid this however as whatever the user picked should
eae51da3
LP
5021 * count. */
5022
5023 if (!u->job_running_timeout_set)
5024 u->job_running_timeout = usec;
5025
5026 u->job_timeout = usec;
5027
5028 return 0;
5029}
5030
5031int config_parse_job_running_timeout_sec(
5032 const char* unit,
5033 const char *filename,
5034 unsigned line,
5035 const char *section,
5036 unsigned section_line,
5037 const char *lvalue,
5038 int ltype,
5039 const char *rvalue,
5040 void *data,
5041 void *userdata) {
5042
5043 Unit *u = data;
5044 usec_t usec;
5045 int r;
5046
5047 assert(filename);
5048 assert(lvalue);
5049 assert(rvalue);
5050 assert(u);
5051
5052 r = parse_sec_fix_0(rvalue, &usec);
5053 if (r < 0) {
323dda78 5054 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse JobRunningTimeoutSec= parameter, ignoring: %s", rvalue);
eae51da3
LP
5055 return 0;
5056 }
5057
5058 u->job_running_timeout = usec;
5059 u->job_running_timeout_set = true;
5060
5061 return 0;
5062}
5063
54fcb619
ZJS
5064int config_parse_emergency_action(
5065 const char* unit,
5066 const char *filename,
5067 unsigned line,
5068 const char *section,
5069 unsigned section_line,
5070 const char *lvalue,
5071 int ltype,
5072 const char *rvalue,
5073 void *data,
5074 void *userdata) {
5075
5076 Manager *m = NULL;
5077 EmergencyAction *x = data;
5078 int r;
5079
5080 assert(filename);
5081 assert(lvalue);
5082 assert(rvalue);
5083 assert(data);
5084
5085 if (unit)
5086 m = ((Unit*) userdata)->manager;
5087 else
5088 m = data;
5089
5090 r = parse_emergency_action(rvalue, MANAGER_IS_SYSTEM(m), x);
5091 if (r < 0) {
469f76f1
ZJS
5092 if (r == -EOPNOTSUPP && MANAGER_IS_USER(m)) {
5093 /* Compat mode: remove for systemd 241. */
5094
5095 log_syntax(unit, LOG_INFO, filename, line, r,
5096 "%s= in user mode specified as \"%s\", using \"exit-force\" instead.",
5097 lvalue, rvalue);
5098 *x = EMERGENCY_ACTION_EXIT_FORCE;
5099 return 0;
5100 }
5101
54fcb619 5102 if (r == -EOPNOTSUPP)
323dda78 5103 log_syntax(unit, LOG_WARNING, filename, line, r,
54fcb619
ZJS
5104 "%s= specified as %s mode action, ignoring: %s",
5105 lvalue, MANAGER_IS_SYSTEM(m) ? "user" : "system", rvalue);
5106 else
323dda78 5107 log_syntax(unit, LOG_WARNING, filename, line, r,
54fcb619
ZJS
5108 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
5109 return 0;
5110 }
5111
5112 return 0;
5113}
5114
a9353a5c
LP
5115int config_parse_pid_file(
5116 const char *unit,
5117 const char *filename,
5118 unsigned line,
5119 const char *section,
5120 unsigned section_line,
5121 const char *lvalue,
5122 int ltype,
5123 const char *rvalue,
5124 void *data,
5125 void *userdata) {
5126
5127 _cleanup_free_ char *k = NULL, *n = NULL;
47538b76 5128 const Unit *u = userdata;
a9353a5c 5129 char **s = data;
a9353a5c
LP
5130 int r;
5131
5132 assert(filename);
5133 assert(lvalue);
5134 assert(rvalue);
5135 assert(u);
5136
b8055c05
YW
5137 if (isempty(rvalue)) {
5138 /* An empty assignment removes already set value. */
5139 *s = mfree(*s);
5140 return 0;
5141 }
5142
a9353a5c
LP
5143 r = unit_full_printf(u, rvalue, &k);
5144 if (r < 0) {
323dda78 5145 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
a9353a5c
LP
5146 return 0;
5147 }
5148
5149 /* If this is a relative path make it absolute by prefixing the /run */
5150 n = path_make_absolute(k, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
5151 if (!n)
5152 return log_oom();
5153
5154 /* Check that the result is a sensible path */
5155 r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5156 if (r < 0)
5157 return r;
5158
4a66b5c9
LP
5159 r = patch_var_run(unit, filename, line, lvalue, &n);
5160 if (r < 0)
5161 return r;
a9353a5c 5162
4a66b5c9 5163 free_and_replace(*s, n);
a9353a5c
LP
5164 return 0;
5165}
5166
7af67e9a
LP
5167int config_parse_exit_status(
5168 const char *unit,
5169 const char *filename,
5170 unsigned line,
5171 const char *section,
5172 unsigned section_line,
5173 const char *lvalue,
5174 int ltype,
5175 const char *rvalue,
5176 void *data,
5177 void *userdata) {
5178
5179 int *exit_status = data, r;
5180 uint8_t u;
5181
5182 assert(filename);
5183 assert(lvalue);
5184 assert(rvalue);
5185 assert(exit_status);
5186
5187 if (isempty(rvalue)) {
5188 *exit_status = -1;
5189 return 0;
5190 }
5191
5192 r = safe_atou8(rvalue, &u);
5193 if (r < 0) {
323dda78 5194 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse exit status '%s', ignoring: %m", rvalue);
7af67e9a
LP
5195 return 0;
5196 }
5197
5198 *exit_status = u;
5199 return 0;
5200}
5201
c72703e2
CD
5202int config_parse_disable_controllers(
5203 const char *unit,
5204 const char *filename,
5205 unsigned line,
5206 const char *section,
5207 unsigned section_line,
5208 const char *lvalue,
5209 int ltype,
5210 const char *rvalue,
5211 void *data,
5212 void *userdata) {
5213
5214 int r;
5215 CGroupContext *c = data;
5216 CGroupMask disabled_mask;
5217
5218 /* 1. If empty, make all controllers eligible for use again.
5219 * 2. If non-empty, merge all listed controllers, space separated. */
5220
5221 if (isempty(rvalue)) {
5222 c->disable_controllers = 0;
5223 return 0;
5224 }
5225
5226 r = cg_mask_from_string(rvalue, &disabled_mask);
5227 if (r < 0 || disabled_mask <= 0) {
323dda78 5228 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid cgroup string: %s, ignoring", rvalue);
c72703e2
CD
5229 return 0;
5230 }
5231
5232 c->disable_controllers |= disabled_mask;
5233
5234 return 0;
5235}
5236
fab34748
KL
5237int config_parse_ip_filter_bpf_progs(
5238 const char *unit,
5239 const char *filename,
5240 unsigned line,
5241 const char *section,
5242 unsigned section_line,
5243 const char *lvalue,
5244 int ltype,
5245 const char *rvalue,
5246 void *data,
5247 void *userdata) {
5248
5249 _cleanup_free_ char *resolved = NULL;
47538b76 5250 const Unit *u = userdata;
fab34748
KL
5251 char ***paths = data;
5252 int r;
5253
5254 assert(filename);
5255 assert(lvalue);
5256 assert(rvalue);
5257 assert(paths);
5258
5259 if (isempty(rvalue)) {
5260 *paths = strv_free(*paths);
5261 return 0;
5262 }
5263
5264 r = unit_full_printf(u, rvalue, &resolved);
5265 if (r < 0) {
323dda78 5266 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
fab34748
KL
5267 return 0;
5268 }
5269
5270 r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
5271 if (r < 0)
5272 return 0;
5273
5274 if (strv_contains(*paths, resolved))
5275 return 0;
5276
5277 r = strv_extend(paths, resolved);
5278 if (r < 0)
5279 return log_oom();
5280
5281 r = bpf_firewall_supported();
5282 if (r < 0)
5283 return r;
5284 if (r != BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
5285 static bool warned = false;
5286
5287 log_full(warned ? LOG_DEBUG : LOG_WARNING,
5288 "File %s:%u configures an IP firewall with BPF programs (%s=%s), but the local system does not support BPF/cgroup based firewalling with multiple filters.\n"
5289 "Starting this unit will fail! (This warning is only shown for the first loaded unit using IP firewalling.)", filename, line, lvalue, rvalue);
5290
5291 warned = true;
5292 }
5293
5294 return 0;
5295}
5296
23a177ef
LP
5297static int merge_by_names(Unit **u, Set *names, const char *id) {
5298 char *k;
5299 int r;
5300
5301 assert(u);
5302 assert(*u);
23a177ef 5303
e8630e69 5304 /* Let's try to add in all names that are aliases of this unit */
23a177ef 5305 while ((k = set_steal_first(names))) {
e8630e69 5306 _cleanup_free_ _unused_ char *free_k = k;
23a177ef 5307
e8630e69 5308 /* First try to merge in the other name into our unit */
9946996c
LP
5309 r = unit_merge_by_name(*u, k);
5310 if (r < 0) {
23a177ef
LP
5311 Unit *other;
5312
e8630e69
ZJS
5313 /* Hmm, we couldn't merge the other unit into ours? Then let's try it the other way
5314 * round. */
036643a2 5315
e8630e69
ZJS
5316 other = manager_get_unit((*u)->manager, k);
5317 if (!other)
5318 return r; /* return previous failure */
036643a2 5319
e8630e69
ZJS
5320 r = unit_merge(other, *u);
5321 if (r < 0)
a837f088 5322 return r;
fe51822e 5323
e8630e69
ZJS
5324 *u = other;
5325 return merge_by_names(u, names, NULL);
036643a2 5326 }
034c6ed7 5327
e8630e69
ZJS
5328 if (streq_ptr(id, k))
5329 unit_choose_id(*u, id);
1b64d026
LP
5330 }
5331
e48614c4 5332 return 0;
0301abf4
LP
5333}
5334
e537352b 5335int unit_load_fragment(Unit *u) {
e8630e69
ZJS
5336 const char *fragment;
5337 _cleanup_set_free_free_ Set *names = NULL;
23a177ef 5338 int r;
0301abf4
LP
5339
5340 assert(u);
ac155bb8
MS
5341 assert(u->load_state == UNIT_STUB);
5342 assert(u->id);
23a177ef 5343
3f5e8115
LP
5344 if (u->transient) {
5345 u->load_state = UNIT_LOADED;
5346 return 0;
5347 }
5348
91e0ee5f
ZJS
5349 /* Possibly rebuild the fragment map to catch new units */
5350 r = unit_file_build_name_map(&u->manager->lookup_paths,
c2911d48 5351 &u->manager->unit_cache_timestamp_hash,
91e0ee5f
ZJS
5352 &u->manager->unit_id_map,
5353 &u->manager->unit_name_map,
5354 &u->manager->unit_path_cache);
9946996c 5355 if (r < 0)
14140908 5356 return log_error_errno(r, "Failed to rebuild name map: %m");
91e0ee5f 5357
e8630e69
ZJS
5358 r = unit_file_find_fragment(u->manager->unit_id_map,
5359 u->manager->unit_name_map,
5360 u->id,
5361 &fragment,
5362 &names);
5363 if (r < 0 && r != -ENOENT)
294d81f1
LP
5364 return r;
5365
e8630e69
ZJS
5366 if (fragment) {
5367 /* Open the file, check if this is a mask, otherwise read. */
5368 _cleanup_fclose_ FILE *f = NULL;
c9e06956 5369 struct stat st;
0301abf4 5370
e8630e69
ZJS
5371 /* Try to open the file name. A symlink is OK, for example for linked files or masks. We
5372 * expect that all symlinks within the lookup paths have been already resolved, but we don't
5373 * verify this here. */
5374 f = fopen(fragment, "re");
5375 if (!f)
5376 return log_unit_notice_errno(u, errno, "Failed to open %s: %m", fragment);
6ccb1b44 5377
e8630e69
ZJS
5378 if (fstat(fileno(f), &st) < 0)
5379 return -errno;
294d81f1 5380
e8630e69 5381 r = free_and_strdup(&u->fragment_path, fragment);
7410616c
LP
5382 if (r < 0)
5383 return r;
294d81f1 5384
e8630e69 5385 if (null_or_empty(&st)) {
88414eed
LP
5386 /* Unit file is masked */
5387
5388 u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
e8630e69
ZJS
5389 u->fragment_mtime = 0;
5390 } else {
5391 u->load_state = UNIT_LOADED;
5392 u->fragment_mtime = timespec_load(&st.st_mtim);
5393
5394 /* Now, parse the file contents */
5395 r = config_parse(u->id, fragment, f,
5396 UNIT_VTABLE(u)->sections,
5397 config_item_perf_lookup, load_fragment_gperf_lookup,
7ade8982 5398 0,
4f9ff96a
LP
5399 u,
5400 NULL);
bb28e684 5401 if (r == -ENOEXEC)
e8630e69
ZJS
5402 log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
5403 if (r < 0)
5404 return r;
bb28e684 5405 }
e8630e69 5406 }
890f434c 5407
e8630e69
ZJS
5408 /* We do the merge dance here because for some unit types, the unit might have aliases which are not
5409 * declared in the file system. In particular, this is true (and frequent) for device and swap units.
5410 */
5411 Unit *merged;
5412 const char *id = u->id;
5413 _cleanup_free_ char *free_id = NULL;
294d81f1 5414
e8630e69
ZJS
5415 if (fragment) {
5416 id = basename(fragment);
5417 if (unit_name_is_valid(id, UNIT_NAME_TEMPLATE)) {
5418 assert(u->instance); /* If we're not trying to use a template for non-instanced unit,
5419 * this must be set. */
890f434c 5420
e8630e69
ZJS
5421 r = unit_name_replace_instance(id, u->instance, &free_id);
5422 if (r < 0)
5423 return log_debug_errno(r, "Failed to build id (%s + %s): %m", id, u->instance);
5424 id = free_id;
abc08d4d 5425 }
071830ff
LP
5426 }
5427
e8630e69
ZJS
5428 merged = u;
5429 r = merge_by_names(&merged, names, id);
5430 if (r < 0)
5431 return r;
5432
5433 if (merged != u)
5434 u->load_state = UNIT_MERGED;
5435
23a177ef 5436 return 0;
3efd4195 5437}
e537352b
LP
5438
5439void unit_dump_config_items(FILE *f) {
f975e971
LP
5440 static const struct {
5441 const ConfigParserCallback callback;
5442 const char *rvalue;
5443 } table[] = {
17df7223 5444 { config_parse_warn_compat, "NOTSUPPORTED" },
f975e971
LP
5445 { config_parse_int, "INTEGER" },
5446 { config_parse_unsigned, "UNSIGNED" },
5556b5fe 5447 { config_parse_iec_size, "SIZE" },
59f448cf 5448 { config_parse_iec_uint64, "SIZE" },
50299121 5449 { config_parse_si_uint64, "SIZE" },
f975e971
LP
5450 { config_parse_bool, "BOOLEAN" },
5451 { config_parse_string, "STRING" },
5452 { config_parse_path, "PATH" },
5453 { config_parse_unit_path_printf, "PATH" },
5454 { config_parse_strv, "STRING [...]" },
5455 { config_parse_exec_nice, "NICE" },
5456 { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
5457 { config_parse_exec_io_class, "IOCLASS" },
5458 { config_parse_exec_io_priority, "IOPRIORITY" },
5459 { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
5460 { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
5461 { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
5462 { config_parse_mode, "MODE" },
5463 { config_parse_unit_env_file, "FILE" },
52c239d7
LB
5464 { config_parse_exec_output, "OUTPUT" },
5465 { config_parse_exec_input, "INPUT" },
ca37242e
LP
5466 { config_parse_log_facility, "FACILITY" },
5467 { config_parse_log_level, "LEVEL" },
f975e971 5468 { config_parse_exec_secure_bits, "SECUREBITS" },
a103496c 5469 { config_parse_capability_set, "BOUNDINGSET" },
4f424df7 5470 { config_parse_rlimit, "LIMIT" },
f975e971 5471 { config_parse_unit_deps, "UNIT [...]" },
f975e971
LP
5472 { config_parse_exec, "PATH [ARGUMENT [...]]" },
5473 { config_parse_service_type, "SERVICETYPE" },
5474 { config_parse_service_restart, "SERVICERESTART" },
bf760801 5475 { config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
f975e971 5476 { config_parse_kill_mode, "KILLMODE" },
f757855e 5477 { config_parse_signal, "SIGNAL" },
f975e971
LP
5478 { config_parse_socket_listen, "SOCKET [...]" },
5479 { config_parse_socket_bind, "SOCKETBIND" },
5480 { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
7f602784 5481 { config_parse_sec, "SECONDS" },
d88a251b 5482 { config_parse_nsec, "NANOSECONDS" },
94828d2d 5483 { config_parse_namespace_path_strv, "PATH [...]" },
d2d6c096 5484 { config_parse_bind_paths, "PATH[:PATH[:OPTIONS]] [...]" },
7c8fa05c 5485 { config_parse_unit_requires_mounts_for, "PATH [...]" },
f975e971
LP
5486 { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
5487 { config_parse_unit_string_printf, "STRING" },
3ecaa09b 5488 { config_parse_trigger_unit, "UNIT" },
f975e971 5489 { config_parse_timer, "TIMER" },
f975e971 5490 { config_parse_path_spec, "PATH" },
f975e971
LP
5491 { config_parse_notify_access, "ACCESS" },
5492 { config_parse_ip_tos, "TOS" },
5493 { config_parse_unit_condition_path, "CONDITION" },
5494 { config_parse_unit_condition_string, "CONDITION" },
a016b922 5495 { config_parse_unit_slice, "SLICE" },
7f0386f6
LP
5496 { config_parse_documentation, "URL" },
5497 { config_parse_service_timeout, "SECONDS" },
87a47f99 5498 { config_parse_emergency_action, "ACTION" },
7f0386f6
LP
5499 { config_parse_set_status, "STATUS" },
5500 { config_parse_service_sockets, "SOCKETS" },
7f0386f6 5501 { config_parse_environ, "ENVIRON" },
349cc4a5 5502#if HAVE_SECCOMP
17df7223 5503 { config_parse_syscall_filter, "SYSCALLS" },
6a6751fe 5504 { config_parse_syscall_archs, "ARCHS" },
17df7223 5505 { config_parse_syscall_errno, "ERRNO" },
9df2cdd8 5506 { config_parse_syscall_log, "SYSCALLS" },
4298d0b5 5507 { config_parse_address_families, "FAMILIES" },
add00535 5508 { config_parse_restrict_namespaces, "NAMESPACES" },
c0467cf3 5509#endif
7f0386f6 5510 { config_parse_cpu_shares, "SHARES" },
984faf29 5511 { config_parse_cg_weight, "WEIGHT" },
7f0386f6
LP
5512 { config_parse_memory_limit, "LIMIT" },
5513 { config_parse_device_allow, "DEVICE" },
5514 { config_parse_device_policy, "POLICY" },
13c31542 5515 { config_parse_io_limit, "LIMIT" },
13c31542 5516 { config_parse_io_device_weight, "DEVICEWEIGHT" },
6ae4283c 5517 { config_parse_io_device_latency, "DEVICELATENCY" },
7f0386f6
LP
5518 { config_parse_blockio_bandwidth, "BANDWIDTH" },
5519 { config_parse_blockio_weight, "WEIGHT" },
5520 { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
5521 { config_parse_long, "LONG" },
5522 { config_parse_socket_service, "SERVICE" },
349cc4a5 5523#if HAVE_SELINUX
6a6751fe
LP
5524 { config_parse_exec_selinux_context, "LABEL" },
5525#endif
5526 { config_parse_job_mode, "MODE" },
5527 { config_parse_job_mode_isolate, "BOOLEAN" },
4298d0b5 5528 { config_parse_personality, "PERSONALITY" },
f975e971
LP
5529 };
5530
5531 const char *prev = NULL;
5532 const char *i;
5533
5534 assert(f);
e537352b 5535
f975e971
LP
5536 NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
5537 const char *rvalue = "OTHER", *lvalue;
313b7856 5538 const ConfigPerfItem *p;
f975e971
LP
5539 size_t prefix_len;
5540 const char *dot;
313b7856 5541 unsigned j;
f975e971
LP
5542
5543 assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
5544
313b7856
LP
5545 /* Hide legacy settings */
5546 if (p->parse == config_parse_warn_compat &&
5547 p->ltype == DISABLED_LEGACY)
5548 continue;
5549
5550 for (j = 0; j < ELEMENTSOF(table); j++)
5551 if (p->parse == table[j].callback) {
5552 rvalue = table[j].rvalue;
5553 break;
5554 }
5555
f975e971
LP
5556 dot = strchr(i, '.');
5557 lvalue = dot ? dot + 1 : i;
5558 prefix_len = dot-i;
5559
5560 if (dot)
641906e9 5561 if (!prev || !strneq(prev, i, prefix_len+1)) {
f975e971
LP
5562 if (prev)
5563 fputc('\n', f);
5564
5565 fprintf(f, "[%.*s]\n", (int) prefix_len, i);
5566 }
5567
f975e971
LP
5568 fprintf(f, "%s=%s\n", lvalue, rvalue);
5569 prev = i;
5570 }
e537352b 5571}
a07a7324
FS
5572
5573int config_parse_cpu_affinity2(
5574 const char *unit,
5575 const char *filename,
5576 unsigned line,
5577 const char *section,
5578 unsigned section_line,
5579 const char *lvalue,
5580 int ltype,
5581 const char *rvalue,
5582 void *data,
5583 void *userdata) {
5584
5585 CPUSet *affinity = data;
5586
5587 assert(affinity);
5588
5589 (void) parse_cpu_set_extend(rvalue, affinity, true, unit, filename, line, lvalue);
5590
5591 return 0;
5592}
5593
5594int config_parse_show_status(
5595 const char* unit,
5596 const char *filename,
5597 unsigned line,
5598 const char *section,
5599 unsigned section_line,
5600 const char *lvalue,
5601 int ltype,
5602 const char *rvalue,
5603 void *data,
5604 void *userdata) {
5605
5606 int k;
5607 ShowStatus *b = data;
5608
5609 assert(filename);
5610 assert(lvalue);
5611 assert(rvalue);
5612 assert(data);
5613
5614 k = parse_show_status(rvalue, b);
323dda78
YW
5615 if (k < 0)
5616 log_syntax(unit, LOG_WARNING, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
a07a7324
FS
5617
5618 return 0;
5619}
5620
5621int config_parse_output_restricted(
5622 const char* unit,
5623 const char *filename,
5624 unsigned line,
5625 const char *section,
5626 unsigned section_line,
5627 const char *lvalue,
5628 int ltype,
5629 const char *rvalue,
5630 void *data,
5631 void *userdata) {
5632
5633 ExecOutput t, *eo = data;
f3dc6af2 5634 bool obsolete = false;
a07a7324
FS
5635
5636 assert(filename);
5637 assert(lvalue);
5638 assert(rvalue);
5639 assert(data);
5640
f3dc6af2
LP
5641 if (streq(rvalue, "syslog")) {
5642 t = EXEC_OUTPUT_JOURNAL;
5643 obsolete = true;
5644 } else if (streq(rvalue, "syslog+console")) {
5645 t = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
5646 obsolete = true;
5647 } else {
5648 t = exec_output_from_string(rvalue);
5649 if (t < 0) {
323dda78 5650 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse output type, ignoring: %s", rvalue);
f3dc6af2
LP
5651 return 0;
5652 }
a07a7324 5653
f3dc6af2 5654 if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) {
323dda78 5655 log_syntax(unit, LOG_WARNING, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue);
f3dc6af2
LP
5656 return 0;
5657 }
a07a7324
FS
5658 }
5659
f3dc6af2
LP
5660 if (obsolete)
5661 log_syntax(unit, LOG_NOTICE, filename, line, 0,
5662 "Standard output type %s is obsolete, automatically updating to %s. Please update your configuration.",
5663 rvalue, exec_output_to_string(t));
5664
a07a7324
FS
5665 *eo = t;
5666 return 0;
5667}
5668
5669int config_parse_crash_chvt(
5670 const char* unit,
5671 const char *filename,
5672 unsigned line,
5673 const char *section,
5674 unsigned section_line,
5675 const char *lvalue,
5676 int ltype,
5677 const char *rvalue,
5678 void *data,
5679 void *userdata) {
5680
5681 int r;
5682
5683 assert(filename);
5684 assert(lvalue);
5685 assert(rvalue);
5686 assert(data);
5687
5688 r = parse_crash_chvt(rvalue, data);
323dda78
YW
5689 if (r < 0)
5690 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
a07a7324
FS
5691
5692 return 0;
5693}
eb34a981
LP
5694
5695int config_parse_swap_priority(
5696 const char *unit,
5697 const char *filename,
5698 unsigned line,
5699 const char *section,
5700 unsigned section_line,
5701 const char *lvalue,
5702 int ltype,
5703 const char *rvalue,
5704 void *data,
5705 void *userdata) {
5706
5707 Swap *s = userdata;
5708 int r, priority;
5709
5710 assert(s);
5711 assert(filename);
5712 assert(lvalue);
5713 assert(rvalue);
5714 assert(data);
5715
5716 if (isempty(rvalue)) {
5717 s->parameters_fragment.priority = -1;
5718 s->parameters_fragment.priority_set = false;
5719 return 0;
5720 }
5721
5722 r = safe_atoi(rvalue, &priority);
5723 if (r < 0) {
323dda78 5724 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid swap priority '%s', ignoring.", rvalue);
eb34a981
LP
5725 return 0;
5726 }
5727
5728 if (priority < -1) {
323dda78 5729 log_syntax(unit, LOG_WARNING, filename, line, 0, "Sorry, swap priorities smaller than -1 may only be assigned by the kernel itself, ignoring: %s", rvalue);
eb34a981
LP
5730 return 0;
5731 }
5732
5733 if (priority > 32767) {
323dda78 5734 log_syntax(unit, LOG_WARNING, filename, line, 0, "Swap priority out of range, ignoring: %s", rvalue);
eb34a981
LP
5735 return 0;
5736 }
5737
5738 s->parameters_fragment.priority = priority;
5739 s->parameters_fragment.priority_set = true;
5740 return 0;
5741}