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