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