]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/execute.c
core: split out cgroup specific state fields from Unit → CGroupRuntime
[thirdparty/systemd.git] / src / core / execute.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
a7334b09 2
034c6ed7
LP
3#include <errno.h>
4#include <fcntl.h>
8dd4c05b 5#include <poll.h>
ac8db36c 6#include <sys/file.h>
f3e43635 7#include <sys/mman.h>
8dd4c05b 8#include <sys/personality.h>
94f04347 9#include <sys/prctl.h>
d2ffa389 10#include <sys/shm.h>
d2ffa389 11#include <sys/types.h>
8dd4c05b
LP
12#include <sys/un.h>
13#include <unistd.h>
023a4f67 14#include <utmpx.h>
5cb5a6ff 15
9c0c6701
DDM
16#include <linux/fs.h> /* Must be included after <sys/mount.h> */
17
24882e06 18#include "sd-messages.h"
8dd4c05b
LP
19
20#include "af-list.h"
b5efdb8a 21#include "alloc-util.h"
8dd4c05b 22#include "async.h"
8dd4c05b 23#include "cap-list.h"
430f0182 24#include "capability-util.h"
fdb3deca 25#include "cgroup-setup.h"
28db6fbf 26#include "constants.h"
da681e1b 27#include "cpu-set-util.h"
af189d7b 28#include "dev-setup.h"
686d13b9 29#include "env-file.h"
4d1a6904 30#include "env-util.h"
17df7223 31#include "errno-list.h"
8a62620e 32#include "escape.h"
43962c30 33#include "exec-credential.h"
3ffd4af2 34#include "execute.h"
bb5232b6 35#include "execute-serialize.h"
8dd4c05b 36#include "exit-status.h"
3ffd4af2 37#include "fd-util.h"
154eb43f 38#include "fileio.h"
f97b34a6 39#include "format-util.h"
7d50b32a 40#include "glob-util.h"
0389f4fa 41#include "hexdecoct.h"
032b3afb 42#include "ioprio-util.h"
9c0c6701 43#include "lock-util.h"
8dd4c05b
LP
44#include "log.h"
45#include "macro.h"
e8a565cb 46#include "manager.h"
2a341bb9 47#include "manager-dump.h"
0a970718 48#include "memory-util.h"
f5947a5e 49#include "missing_fs.h"
7a114ed4 50#include "missing_prctl.h"
35cd0ba5 51#include "mkdir-label.h"
8dd4c05b 52#include "namespace.h"
6bedfcbb 53#include "parse-util.h"
8dd4c05b 54#include "path-util.h"
0b452006 55#include "process-util.h"
78f22b97 56#include "rlimit-util.h"
8dd4c05b 57#include "rm-rf.h"
3ffd4af2 58#include "seccomp-util.h"
07d46372 59#include "securebits-util.h"
8dd4c05b 60#include "selinux-util.h"
bb5232b6 61#include "serialize.h"
a2ab603c 62#include "sort-util.h"
fd63e712 63#include "special.h"
949befd3 64#include "stat-util.h"
8b43440b 65#include "string-table.h"
07630cea 66#include "string-util.h"
8dd4c05b 67#include "strv.h"
7ccbd1ae 68#include "syslog-util.h"
8dd4c05b 69#include "terminal-util.h"
bb0c0d6f 70#include "tmpfile-util.h"
566b7d23 71#include "umask-util.h"
2d3b784d 72#include "unit-serialize.h"
b1d4f8e1 73#include "user-util.h"
8dd4c05b 74#include "utmp-wtmp.h"
5cb5a6ff 75
43efbc39
LB
76static bool is_terminal_input(ExecInput i) {
77 return IN_SET(i,
78 EXEC_INPUT_TTY,
79 EXEC_INPUT_TTY_FORCE,
80 EXEC_INPUT_TTY_FAIL);
81}
82
83static bool is_terminal_output(ExecOutput o) {
84 return IN_SET(o,
85 EXEC_OUTPUT_TTY,
86 EXEC_OUTPUT_KMSG_AND_CONSOLE,
87 EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
88}
89
75689fb2 90const char *exec_context_tty_path(const ExecContext *context) {
80876c20
LP
91 assert(context);
92
1e22b5cd
LP
93 if (context->stdio_as_fds)
94 return NULL;
95
80876c20
LP
96 if (context->tty_path)
97 return context->tty_path;
98
99 return "/dev/console";
100}
101
d2b9e755
LP
102static void exec_context_determine_tty_size(
103 const ExecContext *context,
104 const char *tty_path,
105 unsigned *ret_rows,
106 unsigned *ret_cols) {
107
4d62ee55 108 unsigned rows, cols;
4d62ee55
DDM
109
110 assert(context);
111 assert(ret_rows);
112 assert(ret_cols);
113
d2b9e755
LP
114 if (!tty_path)
115 tty_path = exec_context_tty_path(context);
116
4d62ee55
DDM
117 rows = context->tty_rows;
118 cols = context->tty_cols;
119
d2b9e755
LP
120 if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
121 (void) proc_cmdline_tty_size(
122 tty_path,
123 rows == UINT_MAX ? &rows : NULL,
124 cols == UINT_MAX ? &cols : NULL);
4d62ee55
DDM
125
126 *ret_rows = rows;
127 *ret_cols = cols;
4d62ee55
DDM
128}
129
d2b9e755
LP
130int exec_context_apply_tty_size(
131 const ExecContext *context,
132 int tty_fd,
133 const char *tty_path) {
134
135 unsigned rows, cols;
136
137 exec_context_determine_tty_size(context, tty_path, &rows, &cols);
138
139 return terminal_set_size_fd(tty_fd, tty_path, rows, cols);
140 }
141
75689fb2 142void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p) {
af189d7b 143 _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
af189d7b 144 int fd;
1e22b5cd 145
adabcbab
LP
146 assert(context);
147
148 const char *path = exec_context_tty_path(context);
149
dd9c8da8 150 if (p && p->stdin_fd >= 0 && isatty_safe(p->stdin_fd))
af189d7b 151 fd = p->stdin_fd;
43efbc39
LB
152 else if (path && (context->tty_path || is_terminal_input(context->std_input) ||
153 is_terminal_output(context->std_output) || is_terminal_output(context->std_error))) {
af189d7b 154 fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
75689fb2 155 if (fd < 0)
adabcbab 156 return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path);
75689fb2
LB
157 } else
158 return; /* nothing to do */
b83d5050 159
af189d7b 160 /* Take a synchronization lock for the duration of the setup that we do here.
adabcbab
LP
161 * systemd-vconsole-setup.service also takes the lock to avoid being interrupted. We open a new fd
162 * that will be closed automatically, and operate on it for convenience. */
af189d7b 163 lock_fd = lock_dev_console();
f121efd3
LP
164 if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
165 log_debug_errno(lock_fd, "No privileges to lock /dev/console, proceeding without: %m");
f244e7a7
LP
166 else if (ERRNO_IS_NEG_DEVICE_ABSENT(lock_fd))
167 log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without locking it: %m");
f121efd3
LP
168 else if (lock_fd < 0)
169 return (void) log_debug_errno(lock_fd, "Failed to lock /dev/console: %m");
af189d7b 170
75689fb2
LB
171 if (context->tty_vhangup)
172 (void) terminal_vhangup_fd(fd);
49590d67 173
75689fb2 174 if (context->tty_reset)
29ed1f02 175 (void) reset_terminal_fd(fd, /* switch_to_text= */ true);
49590d67 176
c34eaeb5 177 (void) exec_context_apply_tty_size(context, fd, path);
e66cf1a3 178
75689fb2
LB
179 if (context->tty_vt_disallocate && path)
180 (void) vt_disallocate(path);
181}
e66cf1a3 182
75689fb2
LB
183bool exec_needs_network_namespace(const ExecContext *context) {
184 assert(context);
37ac2744 185
75689fb2
LB
186 return context->private_network || context->network_namespace_path;
187}
3b8bddde 188
75689fb2
LB
189static bool exec_needs_ephemeral(const ExecContext *context) {
190 return (context->root_image || context->root_directory) && context->root_ephemeral;
191}
755d4b67 192
75689fb2
LB
193bool exec_needs_ipc_namespace(const ExecContext *context) {
194 assert(context);
fa97f630 195
75689fb2
LB
196 return context->private_ipc || context->ipc_namespace_path;
197}
165a31c0 198
75689fb2
LB
199bool exec_needs_mount_namespace(
200 const ExecContext *context,
201 const ExecParameters *params,
202 const ExecRuntime *runtime) {
755d4b67 203
75689fb2 204 assert(context);
d35fbf6b 205
75689fb2
LB
206 if (context->root_image)
207 return true;
5cd9cd35 208
75689fb2
LB
209 if (!strv_isempty(context->read_write_paths) ||
210 !strv_isempty(context->read_only_paths) ||
211 !strv_isempty(context->inaccessible_paths) ||
212 !strv_isempty(context->exec_paths) ||
213 !strv_isempty(context->no_exec_paths))
214 return true;
5cd9cd35 215
75689fb2
LB
216 if (context->n_bind_mounts > 0)
217 return true;
5b6319dc 218
75689fb2
LB
219 if (context->n_temporary_filesystems > 0)
220 return true;
d35fbf6b 221
75689fb2
LB
222 if (context->n_mount_images > 0)
223 return true;
04aa0cb9 224
75689fb2
LB
225 if (context->n_extension_images > 0)
226 return true;
f4170c67 227
75689fb2
LB
228 if (!strv_isempty(context->extension_directories))
229 return true;
f4170c67 230
75689fb2
LB
231 if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
232 return true;
f69567cb 233
75689fb2
LB
234 if (context->private_tmp && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
235 return true;
add00535 236
75689fb2
LB
237 if (context->private_devices ||
238 context->private_mounts > 0 ||
239 (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
240 context->protect_system != PROTECT_SYSTEM_NO ||
241 context->protect_home != PROTECT_HOME_NO ||
242 context->protect_kernel_tunables ||
243 context->protect_kernel_modules ||
244 context->protect_kernel_logs ||
245 context->protect_control_groups ||
246 context->protect_proc != PROTECT_PROC_DEFAULT ||
247 context->proc_subset != PROC_SUBSET_ALL ||
248 exec_needs_ipc_namespace(context))
249 return true;
502d704e 250
75689fb2
LB
251 if (context->root_directory) {
252 if (exec_context_get_effective_mount_apivfs(context))
253 return true;
59eeb84b 254
75689fb2
LB
255 for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
256 if (params && !params->prefix[t])
257 continue;
84703040 258
75689fb2
LB
259 if (context->directories[t].n_items > 0)
260 return true;
fc64760d 261 }
75689fb2 262 }
fc64760d 263
75689fb2
LB
264 if (context->dynamic_user &&
265 (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
266 context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
267 context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
268 return true;
469830d1 269
75689fb2
LB
270 if (context->log_namespace)
271 return true;
ba128bb8 272
75689fb2
LB
273 return false;
274}
78e864e5 275
75689fb2
LB
276bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
277 assert(context);
9df2cdd8 278
75689fb2
LB
279 if (!context->dynamic_user)
280 return false;
b1994387 281
75689fb2
LB
282 if (type == EXEC_DIRECTORY_CONFIGURATION)
283 return false;
b1994387 284
75689fb2
LB
285 if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
286 return false;
034c6ed7 287
75689fb2
LB
288 return true;
289}
00819cc1 290
75689fb2
LB
291int exec_params_get_cgroup_path(
292 const ExecParameters *params,
293 const CGroupContext *c,
294 char **ret) {
00819cc1 295
75689fb2
LB
296 const char *subgroup = NULL;
297 char *p;
00819cc1 298
75689fb2
LB
299 assert(params);
300 assert(ret);
f331434d 301
75689fb2
LB
302 if (!params->cgroup_path)
303 return -EINVAL;
f331434d 304
75689fb2
LB
305 /* If we are called for a unit where cgroup delegation is on, and the payload created its own populated
306 * subcgroup (which we expect it to do, after all it asked for delegation), then we cannot place the control
307 * processes started after the main unit's process in the unit's main cgroup because it is now an inner one,
308 * and inner cgroups may not contain processes. Hence, if delegation is on, and this is a control process,
309 * let's use ".control" as subcgroup instead. Note that we do so only for ExecStartPost=, ExecReload=,
310 * ExecStop=, ExecStopPost=, i.e. for the commands where the main process is already forked. For ExecStartPre=
311 * this is not necessary, the cgroup is still empty. We distinguish these cases with the EXEC_CONTROL_CGROUP
312 * flag, which is only passed for the former statements, not for the latter. */
034c6ed7 313
75689fb2
LB
314 if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
315 if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
316 subgroup = ".control";
317 else
318 subgroup = c->delegate_subgroup;
319 }
dd305ec9 320
75689fb2
LB
321 if (subgroup)
322 p = path_join(params->cgroup_path, subgroup);
323 else
324 p = strdup(params->cgroup_path);
325 if (!p)
326 return -ENOMEM;
5686391b 327
75689fb2
LB
328 *ret = p;
329 return !!subgroup;
330}
5686391b 331
75689fb2
LB
332bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
333 assert(c);
5686391b 334
75689fb2
LB
335 return c->cpu_affinity_from_numa;
336}
5686391b 337
75689fb2
LB
338static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
339 assert(unit);
340 assert(msg);
341 assert(executable);
5686391b 342
75689fb2
LB
343 if (!DEBUG_LOGGING)
344 return;
5686391b 345
75689fb2 346 _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
12145637 347
75689fb2
LB
348 log_unit_struct(unit, LOG_DEBUG,
349 "EXECUTABLE=%s", executable,
350 LOG_UNIT_MESSAGE(unit, "%s: %s", msg, strnull(cmdline)),
351 LOG_UNIT_INVOCATION_ID(unit));
d35fbf6b 352}
81a2b7ce 353
75689fb2
LB
354static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
355
f2341e0a
LP
356int exec_spawn(Unit *unit,
357 ExecCommand *command,
d35fbf6b 358 const ExecContext *context,
154eb43f 359 ExecParameters *params,
28135da3 360 ExecRuntime *runtime,
6bb00842 361 const CGroupContext *cgroup_context,
556d2bc4 362 PidRef *ret) {
8351ceae 363
bb5232b6
LB
364 char serialization_fd_number[DECIMAL_STR_MAX(int) + 1];
365 _cleanup_free_ char *subcgroup_path = NULL, *log_level = NULL, *executor_path = NULL;
556d2bc4 366 _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
bb5232b6
LB
367 _cleanup_fdset_free_ FDSet *fdset = NULL;
368 _cleanup_fclose_ FILE *f = NULL;
154eb43f 369 int r;
8351ceae 370
f2341e0a 371 assert(unit);
154eb43f 372 assert(unit->manager);
bb5232b6 373 assert(unit->manager->executor_fd >= 0);
d35fbf6b
DM
374 assert(command);
375 assert(context);
d35fbf6b 376 assert(params);
25b583d7 377 assert(params->fds || (params->n_socket_fds + params->n_storage_fds <= 0));
b646fc32 378 assert(!params->files_env); /* We fill this field, ensure it comes NULL-initialized to us */
c9033540 379 assert(ret);
4298d0b5 380
4b2af439
DDM
381 LOG_CONTEXT_PUSH_UNIT(unit);
382
154eb43f 383 r = exec_context_load_environment(unit, context, &params->files_env);
ff0af2a1 384 if (r < 0)
f2341e0a 385 return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
034c6ed7 386
3ff67ec4
ZJS
387 /* We won't know the real executable path until we create the mount namespace in the child, but we
388 want to log from the parent, so we use the possibly inaccurate path here. */
75689fb2 389 log_command_line(unit, "About to execute", command->path, command->argv);
12145637 390
78f93209 391 if (params->cgroup_path) {
75689fb2 392 r = exec_params_get_cgroup_path(params, cgroup_context, &subcgroup_path);
78f93209
LP
393 if (r < 0)
394 return log_unit_error_errno(unit, r, "Failed to acquire subcgroup path: %m");
18c1e481
LP
395 if (r > 0) {
396 /* If there's a subcgroup, then let's create it here now (the main cgroup was already
397 * realized by the unit logic) */
398
78f93209
LP
399 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path);
400 if (r < 0)
a8b993dc 401 return log_unit_error_errno(unit, r, "Failed to create subcgroup '%s': %m", subcgroup_path);
78f93209
LP
402 }
403 }
404
bb5232b6
LB
405 /* In order to avoid copy-on-write traps and OOM-kills when pid1's memory.current is above the
406 * child's memory.max, serialize all the state needed to start the unit, and pass it to the
407 * systemd-executor binary. clone() with CLONE_VM + CLONE_VFORK will pause the parent until the exec
408 * and ensure all memory is shared. The child immediately execs the new binary so the delay should
2e106312
LB
409 * be minimal. If glibc 2.39 is available pidfd_spawn() is used in order to get a race-free pid fd
410 * and to clone directly into the target cgroup (if we booted with cgroupv2). */
d35fbf6b 411
bb5232b6
LB
412 r = open_serialization_file("sd-executor-state", &f);
413 if (r < 0)
414 return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
ff0af2a1 415
bb5232b6
LB
416 fdset = fdset_new();
417 if (!fdset)
418 return log_oom();
12145637 419
bb5232b6
LB
420 r = exec_serialize_invocation(f, fdset, context, command, params, runtime, cgroup_context);
421 if (r < 0)
422 return log_unit_error_errno(unit, r, "Failed to serialize parameters: %m");
e1714f02 423
86cbbc6d 424 if (fseeko(f, 0, SEEK_SET) < 0)
bb5232b6 425 return log_unit_error_errno(unit, errno, "Failed to reseek on serialization stream: %m");
4c2630eb 426
bb5232b6
LB
427 r = fd_cloexec(fileno(f), false);
428 if (r < 0)
429 return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m");
430
431 r = fdset_cloexec(fdset, false);
432 if (r < 0)
433 return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m");
434
36013380
YW
435 /* If LogLevelMax= is specified, then let's use the specified log level at the beginning of the
436 * executor process. To achieve that the specified log level is passed as an argument, rather than
437 * the one for the manager process. */
438 r = log_level_to_string_alloc(context->log_level_max >= 0 ? context->log_level_max : log_get_max_level(), &log_level);
bb5232b6
LB
439 if (r < 0)
440 return log_unit_error_errno(unit, r, "Failed to convert log level to string: %m");
441
442 r = fd_get_path(unit->manager->executor_fd, &executor_path);
443 if (r < 0)
444 return log_unit_error_errno(unit, r, "Failed to get executor path from fd: %m");
445
446 xsprintf(serialization_fd_number, "%i", fileno(f));
447
448 /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
4520681d
MY
449 r = posix_spawn_wrapper(
450 FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
bb5232b6
LB
451 STRV_MAKE(executor_path,
452 "--deserialize", serialization_fd_number,
453 "--log-level", log_level,
454 "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))),
455 environ,
2e106312 456 cg_unified() > 0 ? subcgroup_path : NULL,
556d2bc4 457 &pidref);
bb5232b6
LB
458 if (r < 0)
459 return log_unit_error_errno(unit, r, "Failed to spawn executor: %m");
78f93209
LP
460 /* We add the new process to the cgroup both in the child (so that we can be sure that no user code is ever
461 * executed outside of the cgroup) and in the parent (so that we can be sure that when we kill the cgroup the
462 * process will be killed too). */
c9033540 463 if (r == 0 && subcgroup_path)
556d2bc4 464 (void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path, pidref.pid);
c9033540
MY
465 /* r > 0: Already in the right cgroup thanks to CLONE_INTO_CGROUP */
466
467 log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
468 command->path, pidref.pid, r > 0 ? "via" : "without");
2da3263a 469
556d2bc4 470 exec_status_start(&command->exec_status, pidref.pid);
9fb86720 471
556d2bc4 472 *ret = TAKE_PIDREF(pidref);
5cb5a6ff
LP
473 return 0;
474}
475
034c6ed7
LP
476void exec_context_init(ExecContext *c) {
477 assert(c);
478
154eb43f
LB
479 /* When initializing a bool member to 'true', make sure to serialize in execute-serialize.c using
480 * serialize_bool() instead of serialize_bool_elide(). */
481
02131627
LB
482 *c = (ExecContext) {
483 .umask = 0022,
484 .ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO,
485 .cpu_sched_policy = SCHED_OTHER,
486 .syslog_priority = LOG_DAEMON|LOG_INFO,
487 .syslog_level_prefix = true,
488 .ignore_sigpipe = true,
489 .timer_slack_nsec = NSEC_INFINITY,
490 .personality = PERSONALITY_INVALID,
491 .timeout_clean_usec = USEC_INFINITY,
492 .capability_bounding_set = CAP_MASK_UNSET,
493 .restrict_namespaces = NAMESPACE_FLAGS_INITIAL,
494 .log_level_max = -1,
005bfaf1 495#if HAVE_SECCOMP
02131627 496 .syscall_errno = SECCOMP_ERROR_NUMBER_KILL,
005bfaf1 497#endif
02131627
LB
498 .tty_rows = UINT_MAX,
499 .tty_cols = UINT_MAX,
500 .private_mounts = -1,
501 .memory_ksm = -1,
854eca4a 502 .set_login_environment = -1,
02131627
LB
503 };
504
59026bcc
MY
505 FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
506 d->mode = 0755;
02131627 507
b070c7c0 508 numa_policy_reset(&c->numa_policy);
02131627
LB
509
510 assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
034c6ed7
LP
511}
512
613b411c 513void exec_context_done(ExecContext *c) {
5cb5a6ff
LP
514 assert(c);
515
6796073e
LP
516 c->environment = strv_free(c->environment);
517 c->environment_files = strv_free(c->environment_files);
b4c14404 518 c->pass_environment = strv_free(c->pass_environment);
00819cc1 519 c->unset_environment = strv_free(c->unset_environment);
8c7be95e 520
31ce987c 521 rlimit_free_all(c->rlimit);
034c6ed7 522
5b10116e 523 for (size_t l = 0; l < 3; l++) {
52c239d7 524 c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
2038c3f5
LP
525 c->stdio_file[l] = mfree(c->stdio_file[l]);
526 }
52c239d7 527
a1e58e8e
LP
528 c->working_directory = mfree(c->working_directory);
529 c->root_directory = mfree(c->root_directory);
915e6d16 530 c->root_image = mfree(c->root_image);
18d73705 531 c->root_image_options = mount_options_free_all(c->root_image_options);
0389f4fa
LB
532 c->root_hash = mfree(c->root_hash);
533 c->root_hash_size = 0;
534 c->root_hash_path = mfree(c->root_hash_path);
d4d55b0d
LB
535 c->root_hash_sig = mfree(c->root_hash_sig);
536 c->root_hash_sig_size = 0;
537 c->root_hash_sig_path = mfree(c->root_hash_sig_path);
0389f4fa 538 c->root_verity = mfree(c->root_verity);
93f59701 539 c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
a07b9926 540 c->extension_directories = strv_free(c->extension_directories);
a1e58e8e
LP
541 c->tty_path = mfree(c->tty_path);
542 c->syslog_identifier = mfree(c->syslog_identifier);
543 c->user = mfree(c->user);
544 c->group = mfree(c->group);
034c6ed7 545
6796073e 546 c->supplementary_groups = strv_free(c->supplementary_groups);
94f04347 547
a1e58e8e 548 c->pam_name = mfree(c->pam_name);
5b6319dc 549
2a624c36
AP
550 c->read_only_paths = strv_free(c->read_only_paths);
551 c->read_write_paths = strv_free(c->read_write_paths);
552 c->inaccessible_paths = strv_free(c->inaccessible_paths);
ddc155b2
TM
553 c->exec_paths = strv_free(c->exec_paths);
554 c->no_exec_paths = strv_free(c->no_exec_paths);
8c35c10d 555 c->exec_search_path = strv_free(c->exec_search_path);
82c121a4 556
d2d6c096 557 bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
8e06d57c
YW
558 c->bind_mounts = NULL;
559 c->n_bind_mounts = 0;
2abd4e38
YW
560 temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
561 c->temporary_filesystems = NULL;
562 c->n_temporary_filesystems = 0;
b3d13314 563 c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
d2d6c096 564
0985c7c4 565 cpu_set_reset(&c->cpu_set);
b070c7c0 566 numa_policy_reset(&c->numa_policy);
86a3475b 567
a1e58e8e
LP
568 c->utmp_id = mfree(c->utmp_id);
569 c->selinux_context = mfree(c->selinux_context);
570 c->apparmor_profile = mfree(c->apparmor_profile);
5b8e1b77 571 c->smack_process_label = mfree(c->smack_process_label);
eef65bf3 572
9b412709 573 c->restrict_filesystems = set_free_free(c->restrict_filesystems);
b1994387 574
8cfa775f 575 c->syscall_filter = hashmap_free(c->syscall_filter);
525d3cc7
LP
576 c->syscall_archs = set_free(c->syscall_archs);
577 c->address_families = set_free(c->address_families);
e66cf1a3 578
fc932ed4
MY
579 FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
580 exec_directory_done(d);
d3070fbd
LP
581
582 c->log_level_max = -1;
583
584 exec_context_free_log_extra_fields(c);
9b412709
FS
585 c->log_filter_allowed_patterns = set_free_free(c->log_filter_allowed_patterns);
586 c->log_filter_denied_patterns = set_free_free(c->log_filter_denied_patterns);
08f3be7a 587
5ac1530e
ZJS
588 c->log_ratelimit_interval_usec = 0;
589 c->log_ratelimit_burst = 0;
90fc172e 590
08f3be7a
LP
591 c->stdin_data = mfree(c->stdin_data);
592 c->stdin_data_size = 0;
a8d08f39
LP
593
594 c->network_namespace_path = mfree(c->network_namespace_path);
71d1e583 595 c->ipc_namespace_path = mfree(c->ipc_namespace_path);
91dd5f7c
LP
596
597 c->log_namespace = mfree(c->log_namespace);
bb0c0d6f 598
43144be4 599 c->load_credentials = hashmap_free(c->load_credentials);
bb0c0d6f 600 c->set_credentials = hashmap_free(c->set_credentials);
9b412709 601 c->import_credentials = set_free_free(c->import_credentials);
84be0c71
LP
602
603 c->root_image_policy = image_policy_free(c->root_image_policy);
604 c->mount_image_policy = image_policy_free(c->mount_image_policy);
605 c->extension_image_policy = image_policy_free(c->extension_image_policy);
e66cf1a3
LP
606}
607
34cf6c43 608int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
e66cf1a3
LP
609 assert(c);
610
611 if (!runtime_prefix)
612 return 0;
613
f7df8adb 614 FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
c2b2df60 615 _cleanup_free_ char *p = NULL;
e66cf1a3 616
494d0247 617 if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
f7df8adb 618 p = path_join(runtime_prefix, "private", i->path);
494d0247 619 else
f7df8adb 620 p = path_join(runtime_prefix, i->path);
e66cf1a3
LP
621 if (!p)
622 return -ENOMEM;
623
7bc4bf4a
LP
624 /* We execute this synchronously, since we need to be sure this is gone when we start the
625 * service next. */
c6878637 626 (void) rm_rf(p, REMOVE_ROOT);
211a3d87 627
f7df8adb 628 STRV_FOREACH(symlink, i->symlinks) {
211a3d87
LB
629 _cleanup_free_ char *symlink_abs = NULL;
630
631 if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
632 symlink_abs = path_join(runtime_prefix, "private", *symlink);
633 else
634 symlink_abs = path_join(runtime_prefix, *symlink);
635 if (!symlink_abs)
636 return -ENOMEM;
637
638 (void) unlink(symlink_abs);
639 }
e66cf1a3
LP
640 }
641
642 return 0;
5cb5a6ff
LP
643}
644
b9f976fb
MK
645int exec_context_destroy_mount_ns_dir(Unit *u) {
646 _cleanup_free_ char *p = NULL;
647
648 if (!u || !MANAGER_IS_SYSTEM(u->manager))
649 return 0;
650
651 p = path_join("/run/systemd/propagate/", u->id);
652 if (!p)
653 return -ENOMEM;
654
655 /* This is only filled transiently (see mount_in_namespace()), should be empty or even non-existent*/
656 if (rmdir(p) < 0 && errno != ENOENT)
657 log_unit_debug_errno(u, errno, "Unable to remove propagation dir '%s', ignoring: %m", p);
658
659 return 0;
660}
661
bb5232b6 662void exec_command_done(ExecCommand *c) {
43d0fcbd
LP
663 assert(c);
664
a1e58e8e 665 c->path = mfree(c->path);
6796073e 666 c->argv = strv_free(c->argv);
43d0fcbd
LP
667}
668
da6053d0 669void exec_command_done_array(ExecCommand *c, size_t n) {
f7df8adb
MY
670 FOREACH_ARRAY(i, c, n)
671 exec_command_done(i);
43d0fcbd
LP
672}
673
f1acf85a 674ExecCommand* exec_command_free_list(ExecCommand *c) {
5cb5a6ff
LP
675 ExecCommand *i;
676
52e3671b 677 while ((i = LIST_POP(command, c))) {
43d0fcbd 678 exec_command_done(i);
5cb5a6ff
LP
679 free(i);
680 }
f1acf85a
ZJS
681
682 return NULL;
5cb5a6ff
LP
683}
684
da6053d0 685void exec_command_free_array(ExecCommand **c, size_t n) {
f7df8adb
MY
686 FOREACH_ARRAY(i, c, n)
687 *i = exec_command_free_list(*i);
034c6ed7
LP
688}
689
6a1d4d9f 690void exec_command_reset_status_array(ExecCommand *c, size_t n) {
f7df8adb
MY
691 FOREACH_ARRAY(i, c, n)
692 exec_status_reset(&i->exec_status);
6a1d4d9f
LP
693}
694
695void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
f7df8adb
MY
696 FOREACH_ARRAY(i, c, n)
697 LIST_FOREACH(command, z, *i)
6a1d4d9f 698 exec_status_reset(&z->exec_status);
6a1d4d9f
LP
699}
700
039f0e70 701typedef struct InvalidEnvInfo {
34cf6c43 702 const Unit *unit;
039f0e70
LP
703 const char *path;
704} InvalidEnvInfo;
705
706static void invalid_env(const char *p, void *userdata) {
707 InvalidEnvInfo *info = userdata;
708
f2341e0a 709 log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
039f0e70
LP
710}
711
52c239d7
LB
712const char* exec_context_fdname(const ExecContext *c, int fd_index) {
713 assert(c);
714
715 switch (fd_index) {
5073ff6b 716
52c239d7
LB
717 case STDIN_FILENO:
718 if (c->std_input != EXEC_INPUT_NAMED_FD)
719 return NULL;
5073ff6b 720
52c239d7 721 return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
5073ff6b 722
52c239d7
LB
723 case STDOUT_FILENO:
724 if (c->std_output != EXEC_OUTPUT_NAMED_FD)
725 return NULL;
5073ff6b 726
52c239d7 727 return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
5073ff6b 728
52c239d7
LB
729 case STDERR_FILENO:
730 if (c->std_error != EXEC_OUTPUT_NAMED_FD)
731 return NULL;
5073ff6b 732
52c239d7 733 return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
5073ff6b 734
52c239d7
LB
735 default:
736 return NULL;
737 }
738}
739
398a5009
ZJS
740static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
741 _cleanup_strv_free_ char **v = NULL;
398a5009 742 int r;
8c7be95e
LP
743
744 assert(c);
398a5009 745 assert(ret);
8c7be95e
LP
746
747 STRV_FOREACH(i, c->environment_files) {
7fd1b19b 748 _cleanup_globfree_ glob_t pglob = {};
398a5009
ZJS
749 bool ignore = false;
750 char *fn = *i;
8c7be95e
LP
751
752 if (fn[0] == '-') {
753 ignore = true;
313cefa1 754 fn++;
8c7be95e
LP
755 }
756
757 if (!path_is_absolute(fn)) {
8c7be95e
LP
758 if (ignore)
759 continue;
8c7be95e
LP
760 return -EINVAL;
761 }
762
2bef10ab 763 /* Filename supports globbing, take all matching files */
398a5009
ZJS
764 r = safe_glob(fn, 0, &pglob);
765 if (r < 0) {
2bef10ab
PL
766 if (ignore)
767 continue;
398a5009 768 return r;
2bef10ab 769 }
8c7be95e 770
d8c92e8b
ZJS
771 /* When we don't match anything, -ENOENT should be returned */
772 assert(pglob.gl_pathc > 0);
773
f7df8adb 774 FOREACH_ARRAY(path, pglob.gl_pathv, pglob.gl_pathc) {
398a5009
ZJS
775 _cleanup_strv_free_ char **p = NULL;
776
f7df8adb 777 r = load_env_file(NULL, *path, &p);
398a5009 778 if (r < 0) {
2bef10ab
PL
779 if (ignore)
780 continue;
398a5009 781 return r;
e9c1ea9d 782 }
398a5009 783
ebc05a09 784 /* Log invalid environment variables with filename */
039f0e70
LP
785 if (p) {
786 InvalidEnvInfo info = {
f2341e0a 787 .unit = unit,
f7df8adb 788 .path = *path,
039f0e70
LP
789 };
790
791 p = strv_env_clean_with_callback(p, invalid_env, &info);
792 }
8c7be95e 793
398a5009
ZJS
794 if (!v)
795 v = TAKE_PTR(p);
2bef10ab 796 else {
398a5009 797 char **m = strv_env_merge(v, p);
c84a9488 798 if (!m)
2bef10ab 799 return -ENOMEM;
2bef10ab 800
398a5009 801 strv_free_and_replace(v, m);
2bef10ab 802 }
8c7be95e
LP
803 }
804 }
805
398a5009 806 *ret = TAKE_PTR(v);
8c7be95e
LP
807
808 return 0;
809}
810
6ac8fdc9 811static bool tty_may_match_dev_console(const char *tty) {
7b912648 812 _cleanup_free_ char *resolved = NULL;
6ac8fdc9 813
1e22b5cd
LP
814 if (!tty)
815 return true;
816
a119ec7c 817 tty = skip_dev_prefix(tty);
6ac8fdc9
MS
818
819 /* trivial identity? */
820 if (streq(tty, "console"))
821 return true;
822
7b912648
LP
823 if (resolve_dev_console(&resolved) < 0)
824 return true; /* if we could not resolve, assume it may */
6ac8fdc9
MS
825
826 /* "tty0" means the active VC, so it may be the same sometimes */
955f1c85 827 return path_equal(resolved, tty) || (streq(resolved, "tty0") && tty_is_vc(tty));
6ac8fdc9
MS
828}
829
6c0ae739
LP
830static bool exec_context_may_touch_tty(const ExecContext *ec) {
831 assert(ec);
1e22b5cd 832
6c0ae739 833 return ec->tty_reset ||
1e22b5cd
LP
834 ec->tty_vhangup ||
835 ec->tty_vt_disallocate ||
6ac8fdc9
MS
836 is_terminal_input(ec->std_input) ||
837 is_terminal_output(ec->std_output) ||
6c0ae739
LP
838 is_terminal_output(ec->std_error);
839}
840
841bool exec_context_may_touch_console(const ExecContext *ec) {
842
843 return exec_context_may_touch_tty(ec) &&
1e22b5cd 844 tty_may_match_dev_console(exec_context_tty_path(ec));
6ac8fdc9
MS
845}
846
15ae422b 847static void strv_fprintf(FILE *f, char **l) {
15ae422b
LP
848 assert(f);
849
850 STRV_FOREACH(g, l)
851 fprintf(f, " %s", *g);
852}
853
ddc155b2
TM
854static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
855 assert(f);
856 assert(prefix);
857 assert(name);
858
859 if (!strv_isempty(strv)) {
a7bd1656 860 fprintf(f, "%s%s:", prefix, name);
ddc155b2
TM
861 strv_fprintf(f, strv);
862 fputs("\n", f);
863 }
864}
865
97f53fec
LB
866void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
867 assert(p);
868 assert(f);
869
870 prefix = strempty(prefix);
871
872 fprintf(f,
873 "%sRuntimeScope: %s\n"
874 "%sExecFlags: %u\n"
875 "%sSELinuxContextNetwork: %s\n"
876 "%sCgroupSupportedMask: %u\n"
877 "%sCgroupPath: %s\n"
878 "%sCrededentialsDirectory: %s\n"
879 "%sEncryptedCredentialsDirectory: %s\n"
880 "%sConfirmSpawn: %s\n"
881 "%sShallConfirmSpawn: %s\n"
882 "%sWatchdogUSec: " USEC_FMT "\n"
883 "%sNotifySocket: %s\n"
884 "%sFallbackSmackProcessLabel: %s\n",
885 prefix, runtime_scope_to_string(p->runtime_scope),
886 prefix, p->flags,
887 prefix, yes_no(p->selinux_context_net),
888 prefix, p->cgroup_supported,
889 prefix, p->cgroup_path,
890 prefix, strempty(p->received_credentials_directory),
891 prefix, strempty(p->received_encrypted_credentials_directory),
892 prefix, strempty(p->confirm_spawn),
893 prefix, yes_no(p->shall_confirm_spawn),
894 prefix, p->watchdog_usec,
895 prefix, strempty(p->notify_socket),
896 prefix, strempty(p->fallback_smack_process_label));
897
898 strv_dump(f, prefix, "FdNames", p->fd_names);
899 strv_dump(f, prefix, "Environment", p->environment);
900 strv_dump(f, prefix, "Prefix", p->prefix);
901
902 LIST_FOREACH(open_files, file, p->open_files)
903 fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
904
905 strv_dump(f, prefix, "FilesEnv", p->files_env);
906}
907
34cf6c43 908void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
add00535 909 int r;
9eba9da4 910
5cb5a6ff
LP
911 assert(c);
912 assert(f);
913
4ad49000 914 prefix = strempty(prefix);
5cb5a6ff
LP
915
916 fprintf(f,
94f04347
LP
917 "%sUMask: %04o\n"
918 "%sWorkingDirectory: %s\n"
451a074f 919 "%sRootDirectory: %s\n"
9c0c6701 920 "%sRootEphemeral: %s\n"
15ae422b 921 "%sNonBlocking: %s\n"
64747e2d 922 "%sPrivateTmp: %s\n"
7f112f50 923 "%sPrivateDevices: %s\n"
59eeb84b 924 "%sProtectKernelTunables: %s\n"
e66a2f65 925 "%sProtectKernelModules: %s\n"
84703040 926 "%sProtectKernelLogs: %s\n"
fc64760d 927 "%sProtectClock: %s\n"
59eeb84b 928 "%sProtectControlGroups: %s\n"
d251207d
LP
929 "%sPrivateNetwork: %s\n"
930 "%sPrivateUsers: %s\n"
1b8689f9
LP
931 "%sProtectHome: %s\n"
932 "%sProtectSystem: %s\n"
5d997827 933 "%sMountAPIVFS: %s\n"
f3e43635 934 "%sIgnoreSIGPIPE: %s\n"
f4170c67 935 "%sMemoryDenyWriteExecute: %s\n"
b1edf445 936 "%sRestrictRealtime: %s\n"
f69567cb 937 "%sRestrictSUIDSGID: %s\n"
aecd5ac6 938 "%sKeyringMode: %s\n"
4e399953
LP
939 "%sProtectHostname: %s\n"
940 "%sProtectProc: %s\n"
941 "%sProcSubset: %s\n",
5cb5a6ff 942 prefix, c->umask,
14eb3285
LP
943 prefix, empty_to_root(c->working_directory),
944 prefix, empty_to_root(c->root_directory),
9c0c6701 945 prefix, yes_no(c->root_ephemeral),
15ae422b 946 prefix, yes_no(c->non_blocking),
64747e2d 947 prefix, yes_no(c->private_tmp),
7f112f50 948 prefix, yes_no(c->private_devices),
59eeb84b 949 prefix, yes_no(c->protect_kernel_tunables),
e66a2f65 950 prefix, yes_no(c->protect_kernel_modules),
84703040 951 prefix, yes_no(c->protect_kernel_logs),
fc64760d 952 prefix, yes_no(c->protect_clock),
59eeb84b 953 prefix, yes_no(c->protect_control_groups),
d251207d
LP
954 prefix, yes_no(c->private_network),
955 prefix, yes_no(c->private_users),
1b8689f9
LP
956 prefix, protect_home_to_string(c->protect_home),
957 prefix, protect_system_to_string(c->protect_system),
5e98086d 958 prefix, yes_no(exec_context_get_effective_mount_apivfs(c)),
f3e43635 959 prefix, yes_no(c->ignore_sigpipe),
f4170c67 960 prefix, yes_no(c->memory_deny_write_execute),
b1edf445 961 prefix, yes_no(c->restrict_realtime),
f69567cb 962 prefix, yes_no(c->restrict_suid_sgid),
aecd5ac6 963 prefix, exec_keyring_mode_to_string(c->keyring_mode),
4e399953
LP
964 prefix, yes_no(c->protect_hostname),
965 prefix, protect_proc_to_string(c->protect_proc),
966 prefix, proc_subset_to_string(c->proc_subset));
fb33a393 967
06120a15
MY
968 if (c->set_login_environment >= 0)
969 fprintf(f, "%sSetLoginEnvironment: %s\n", prefix, yes_no(c->set_login_environment > 0));
970
915e6d16
LP
971 if (c->root_image)
972 fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
973
18d73705 974 if (c->root_image_options) {
18d73705
LB
975 fprintf(f, "%sRootImageOptions:", prefix);
976 LIST_FOREACH(mount_options, o, c->root_image_options)
977 if (!isempty(o->options))
9ece6444
LB
978 fprintf(f, " %s:%s",
979 partition_designator_to_string(o->partition_designator),
980 o->options);
18d73705
LB
981 fprintf(f, "\n");
982 }
983
0389f4fa
LB
984 if (c->root_hash) {
985 _cleanup_free_ char *encoded = NULL;
986 encoded = hexmem(c->root_hash, c->root_hash_size);
987 if (encoded)
988 fprintf(f, "%sRootHash: %s\n", prefix, encoded);
989 }
990
991 if (c->root_hash_path)
992 fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
993
d4d55b0d
LB
994 if (c->root_hash_sig) {
995 _cleanup_free_ char *encoded = NULL;
996 ssize_t len;
997 len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded);
998 if (len)
999 fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
1000 }
1001
1002 if (c->root_hash_sig_path)
1003 fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
1004
0389f4fa
LB
1005 if (c->root_verity)
1006 fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
1007
8c7be95e
LP
1008 STRV_FOREACH(e, c->environment)
1009 fprintf(f, "%sEnvironment: %s\n", prefix, *e);
1010
1011 STRV_FOREACH(e, c->environment_files)
1012 fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
94f04347 1013
b4c14404
FB
1014 STRV_FOREACH(e, c->pass_environment)
1015 fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
1016
00819cc1
LP
1017 STRV_FOREACH(e, c->unset_environment)
1018 fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
1019
53f47dfc
YW
1020 fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
1021
5b10116e 1022 for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
3536f49e
YW
1023 fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode);
1024
211a3d87
LB
1025 for (size_t i = 0; i < c->directories[dt].n_items; i++) {
1026 fprintf(f, "%s%s: %s\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].items[i].path);
1027
1028 STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
1029 fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
1030 }
3536f49e 1031 }
c2bbd90b 1032
5291f26d 1033 fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC));
12213aed 1034
06120a15
MY
1035 if (c->memory_ksm >= 0)
1036 fprintf(f, "%sMemoryKSM: %s\n", prefix, yes_no(c->memory_ksm > 0));
1037
fb33a393 1038 if (c->nice_set)
5291f26d 1039 fprintf(f, "%sNice: %i\n", prefix, c->nice);
fb33a393 1040
dd6c17b1 1041 if (c->oom_score_adjust_set)
5291f26d 1042 fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
9eba9da4 1043
ad21e542 1044 if (c->coredump_filter_set)
5291f26d 1045 fprintf(f, "%sCoredumpFilter: 0x%"PRIx64"\n", prefix, c->coredump_filter);
ad21e542 1046
5b10116e 1047 for (unsigned i = 0; i < RLIM_NLIMITS; i++)
3c11da9d 1048 if (c->rlimit[i]) {
4c3a2b84 1049 fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
3c11da9d 1050 prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
4c3a2b84 1051 fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
3c11da9d
EV
1052 prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
1053 }
94f04347 1054
f8b69d1d 1055 if (c->ioprio_set) {
1756a011 1056 _cleanup_free_ char *class_str = NULL;
f8b69d1d 1057
5bead76e 1058 r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
837df140
YW
1059 if (r >= 0)
1060 fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
1061
5bead76e 1062 fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
f8b69d1d 1063 }
94f04347 1064
f8b69d1d 1065 if (c->cpu_sched_set) {
1756a011 1066 _cleanup_free_ char *policy_str = NULL;
f8b69d1d 1067
837df140
YW
1068 r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
1069 if (r >= 0)
1070 fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
1071
94f04347 1072 fprintf(f,
38b48754
LP
1073 "%sCPUSchedulingPriority: %i\n"
1074 "%sCPUSchedulingResetOnFork: %s\n",
38b48754
LP
1075 prefix, c->cpu_sched_priority,
1076 prefix, yes_no(c->cpu_sched_reset_on_fork));
b929bf04 1077 }
94f04347 1078
0985c7c4 1079 if (c->cpu_set.set) {
e7fca352
MS
1080 _cleanup_free_ char *affinity = NULL;
1081
1082 affinity = cpu_set_to_range_string(&c->cpu_set);
1083 fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
94f04347
LP
1084 }
1085
b070c7c0
MS
1086 if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
1087 _cleanup_free_ char *nodes = NULL;
1088
1089 nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1090 fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1091 fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1092 }
1093
3a43da28 1094 if (c->timer_slack_nsec != NSEC_INFINITY)
ccd06097 1095 fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
94f04347
LP
1096
1097 fprintf(f,
80876c20
LP
1098 "%sStandardInput: %s\n"
1099 "%sStandardOutput: %s\n"
1100 "%sStandardError: %s\n",
1101 prefix, exec_input_to_string(c->std_input),
1102 prefix, exec_output_to_string(c->std_output),
1103 prefix, exec_output_to_string(c->std_error));
1104
befc4a80
LP
1105 if (c->std_input == EXEC_INPUT_NAMED_FD)
1106 fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
1107 if (c->std_output == EXEC_OUTPUT_NAMED_FD)
1108 fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
1109 if (c->std_error == EXEC_OUTPUT_NAMED_FD)
1110 fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
1111
1112 if (c->std_input == EXEC_INPUT_FILE)
1113 fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
1114 if (c->std_output == EXEC_OUTPUT_FILE)
1115 fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
566b7d23
ZD
1116 if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
1117 fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
8d7dab1f
LW
1118 if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE)
1119 fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
befc4a80
LP
1120 if (c->std_error == EXEC_OUTPUT_FILE)
1121 fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
566b7d23
ZD
1122 if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
1123 fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
8d7dab1f
LW
1124 if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
1125 fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
befc4a80 1126
80876c20
LP
1127 if (c->tty_path)
1128 fprintf(f,
6ea832a2
LP
1129 "%sTTYPath: %s\n"
1130 "%sTTYReset: %s\n"
1131 "%sTTYVHangup: %s\n"
51462135
DDM
1132 "%sTTYVTDisallocate: %s\n"
1133 "%sTTYRows: %u\n"
1134 "%sTTYColumns: %u\n",
6ea832a2
LP
1135 prefix, c->tty_path,
1136 prefix, yes_no(c->tty_reset),
1137 prefix, yes_no(c->tty_vhangup),
51462135
DDM
1138 prefix, yes_no(c->tty_vt_disallocate),
1139 prefix, c->tty_rows,
1140 prefix, c->tty_cols);
94f04347 1141
9f6444eb 1142 if (IN_SET(c->std_output,
9f6444eb
LP
1143 EXEC_OUTPUT_KMSG,
1144 EXEC_OUTPUT_JOURNAL,
9f6444eb
LP
1145 EXEC_OUTPUT_KMSG_AND_CONSOLE,
1146 EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1147 IN_SET(c->std_error,
9f6444eb
LP
1148 EXEC_OUTPUT_KMSG,
1149 EXEC_OUTPUT_JOURNAL,
9f6444eb
LP
1150 EXEC_OUTPUT_KMSG_AND_CONSOLE,
1151 EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
f8b69d1d 1152
5ce70e5b 1153 _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
f8b69d1d 1154
837df140
YW
1155 r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
1156 if (r >= 0)
1157 fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
f8b69d1d 1158
837df140
YW
1159 r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
1160 if (r >= 0)
1161 fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
f8b69d1d 1162 }
94f04347 1163
d3070fbd
LP
1164 if (c->log_level_max >= 0) {
1165 _cleanup_free_ char *t = NULL;
1166
1167 (void) log_level_to_string_alloc(c->log_level_max, &t);
1168
1169 fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1170 }
1171
5291f26d 1172 if (c->log_ratelimit_interval_usec > 0)
90fc172e
AZ
1173 fprintf(f,
1174 "%sLogRateLimitIntervalSec: %s\n",
5291f26d 1175 prefix, FORMAT_TIMESPAN(c->log_ratelimit_interval_usec, USEC_PER_SEC));
90fc172e 1176
5ac1530e
ZJS
1177 if (c->log_ratelimit_burst > 0)
1178 fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit_burst);
90fc172e 1179
523ea123
QD
1180 if (!set_isempty(c->log_filter_allowed_patterns) || !set_isempty(c->log_filter_denied_patterns)) {
1181 fprintf(f, "%sLogFilterPatterns:", prefix);
1182
1183 char *pattern;
1184 SET_FOREACH(pattern, c->log_filter_allowed_patterns)
1185 fprintf(f, " %s", pattern);
1186 SET_FOREACH(pattern, c->log_filter_denied_patterns)
1187 fprintf(f, " ~%s", pattern);
1188 fputc('\n', f);
1189 }
1190
f7df8adb 1191 FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
5b10116e 1192 fprintf(f, "%sLogExtraFields: ", prefix);
f7df8adb 1193 fwrite(field->iov_base, 1, field->iov_len, f);
5b10116e 1194 fputc('\n', f);
d3070fbd
LP
1195 }
1196
91dd5f7c
LP
1197 if (c->log_namespace)
1198 fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
1199
07d46372
YW
1200 if (c->secure_bits) {
1201 _cleanup_free_ char *str = NULL;
1202
1203 r = secure_bits_to_string_alloc(c->secure_bits, &str);
1204 if (r >= 0)
1205 fprintf(f, "%sSecure Bits: %s\n", prefix, str);
1206 }
94f04347 1207
3fd5190b 1208 if (c->capability_bounding_set != CAP_MASK_UNSET) {
dd1f5bd0 1209 _cleanup_free_ char *str = NULL;
94f04347 1210
8142d735 1211 r = capability_set_to_string(c->capability_bounding_set, &str);
dd1f5bd0
YW
1212 if (r >= 0)
1213 fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
755d4b67
IP
1214 }
1215
1216 if (c->capability_ambient_set != 0) {
dd1f5bd0 1217 _cleanup_free_ char *str = NULL;
755d4b67 1218
8142d735 1219 r = capability_set_to_string(c->capability_ambient_set, &str);
dd1f5bd0
YW
1220 if (r >= 0)
1221 fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
94f04347
LP
1222 }
1223
1224 if (c->user)
f2d3769a 1225 fprintf(f, "%sUser: %s\n", prefix, c->user);
94f04347 1226 if (c->group)
f2d3769a 1227 fprintf(f, "%sGroup: %s\n", prefix, c->group);
94f04347 1228
29206d46
LP
1229 fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
1230
ddc155b2 1231 strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
94f04347 1232
5b6319dc 1233 if (c->pam_name)
f2d3769a 1234 fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
5b6319dc 1235
ddc155b2
TM
1236 strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
1237 strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
1238 strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
1239 strv_dump(f, prefix, "ExecPaths", c->exec_paths);
1240 strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
8c35c10d 1241 strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
2e22afe9 1242
f7df8adb 1243 FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
5b10116e 1244 fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
f7df8adb
MY
1245 mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
1246 mount->ignore_enoent ? "-": "",
1247 mount->source,
1248 mount->destination,
1249 mount->recursive ? "rbind" : "norbind");
2abd4e38 1250
f7df8adb 1251 FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
5b10116e 1252 fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
f7df8adb
MY
1253 tmpfs->path,
1254 isempty(tmpfs->options) ? "" : ":",
1255 strempty(tmpfs->options));
2abd4e38 1256
169c1bda
LP
1257 if (c->utmp_id)
1258 fprintf(f,
1259 "%sUtmpIdentifier: %s\n",
1260 prefix, c->utmp_id);
7b52a628
MS
1261
1262 if (c->selinux_context)
1263 fprintf(f,
5f8640fb
LP
1264 "%sSELinuxContext: %s%s\n",
1265 prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
17df7223 1266
80c21aea
WC
1267 if (c->apparmor_profile)
1268 fprintf(f,
1269 "%sAppArmorProfile: %s%s\n",
1270 prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
1271
1272 if (c->smack_process_label)
1273 fprintf(f,
1274 "%sSmackProcessLabel: %s%s\n",
1275 prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
1276
050f7277 1277 if (c->personality != PERSONALITY_INVALID)
ac45f971
LP
1278 fprintf(f,
1279 "%sPersonality: %s\n",
1280 prefix, strna(personality_to_string(c->personality)));
1281
78e864e5
TM
1282 fprintf(f,
1283 "%sLockPersonality: %s\n",
1284 prefix, yes_no(c->lock_personality));
1285
17df7223 1286 if (c->syscall_filter) {
17df7223 1287 fprintf(f,
57183d11 1288 "%sSystemCallFilter: ",
17df7223
LP
1289 prefix);
1290
6b000af4 1291 if (!c->syscall_allow_list)
17df7223
LP
1292 fputc('~', f);
1293
349cc4a5 1294#if HAVE_SECCOMP
d5a99b7c
JJ
1295 void *id, *val;
1296 bool first = true;
90e74a66 1297 HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
17df7223 1298 _cleanup_free_ char *name = NULL;
8cfa775f
YW
1299 const char *errno_name = NULL;
1300 int num = PTR_TO_INT(val);
17df7223
LP
1301
1302 if (first)
1303 first = false;
1304 else
1305 fputc(' ', f);
1306
57183d11 1307 name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
17df7223 1308 fputs(strna(name), f);
8cfa775f
YW
1309
1310 if (num >= 0) {
005bfaf1 1311 errno_name = seccomp_errno_or_action_to_string(num);
8cfa775f
YW
1312 if (errno_name)
1313 fprintf(f, ":%s", errno_name);
1314 else
1315 fprintf(f, ":%d", num);
1316 }
17df7223 1317 }
351a19b1 1318#endif
17df7223
LP
1319
1320 fputc('\n', f);
1321 }
1322
57183d11 1323 if (c->syscall_archs) {
57183d11
LP
1324 fprintf(f,
1325 "%sSystemCallArchitectures:",
1326 prefix);
1327
349cc4a5 1328#if HAVE_SECCOMP
d5a99b7c 1329 void *id;
90e74a66 1330 SET_FOREACH(id, c->syscall_archs)
57183d11
LP
1331 fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
1332#endif
1333 fputc('\n', f);
1334 }
1335
add00535
LP
1336 if (exec_context_restrict_namespaces_set(c)) {
1337 _cleanup_free_ char *s = NULL;
1338
86c2a9f1 1339 r = namespace_flags_to_string(c->restrict_namespaces, &s);
add00535
LP
1340 if (r >= 0)
1341 fprintf(f, "%sRestrictNamespaces: %s\n",
dd0395b5 1342 prefix, strna(s));
add00535
LP
1343 }
1344
b1994387 1345#if HAVE_LIBBPF
8fe84dc8
YW
1346 if (exec_context_restrict_filesystems_set(c)) {
1347 char *fs;
1348 SET_FOREACH(fs, c->restrict_filesystems)
1349 fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
1350 }
b1994387
ILG
1351#endif
1352
a8d08f39
LP
1353 if (c->network_namespace_path)
1354 fprintf(f,
1355 "%sNetworkNamespacePath: %s\n",
1356 prefix, c->network_namespace_path);
1357
3df90f24 1358 if (c->syscall_errno > 0) {
3df90f24
YW
1359 fprintf(f, "%sSystemCallErrorNumber: ", prefix);
1360
005bfaf1 1361#if HAVE_SECCOMP
d5a99b7c 1362 const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
3df90f24 1363 if (errno_name)
005bfaf1 1364 fputs(errno_name, f);
3df90f24 1365 else
005bfaf1
TM
1366 fprintf(f, "%d", c->syscall_errno);
1367#endif
1368 fputc('\n', f);
3df90f24 1369 }
b3d13314 1370
f7df8adb 1371 FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
79e20ceb 1372 fprintf(f, "%sMountImages: %s%s:%s", prefix,
f7df8adb
MY
1373 mount->ignore_enoent ? "-": "",
1374 mount->source,
1375 mount->destination);
1376 LIST_FOREACH(mount_options, o, mount->mount_options)
79e20ceb 1377 fprintf(f, ":%s:%s",
427353f6 1378 partition_designator_to_string(o->partition_designator),
79e20ceb 1379 strempty(o->options));
427353f6
LB
1380 fprintf(f, "\n");
1381 }
93f59701 1382
f7df8adb 1383 FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
93f59701 1384 fprintf(f, "%sExtensionImages: %s%s", prefix,
f7df8adb
MY
1385 mount->ignore_enoent ? "-": "",
1386 mount->source);
1387 LIST_FOREACH(mount_options, o, mount->mount_options)
93f59701
LB
1388 fprintf(f, ":%s:%s",
1389 partition_designator_to_string(o->partition_designator),
1390 strempty(o->options));
1391 fprintf(f, "\n");
1392 }
a07b9926
LB
1393
1394 strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
5cb5a6ff
LP
1395}
1396
34cf6c43 1397bool exec_context_maintains_privileges(const ExecContext *c) {
a931ad47
LP
1398 assert(c);
1399
61233823 1400 /* Returns true if the process forked off would run under
a931ad47
LP
1401 * an unchanged UID or as root. */
1402
1403 if (!c->user)
1404 return true;
1405
1406 if (streq(c->user, "root") || streq(c->user, "0"))
1407 return true;
1408
1409 return false;
1410}
1411
34cf6c43 1412int exec_context_get_effective_ioprio(const ExecContext *c) {
7f452159
LP
1413 int p;
1414
1415 assert(c);
1416
1417 if (c->ioprio_set)
1418 return c->ioprio;
1419
1420 p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
1421 if (p < 0)
0692548c 1422 return IOPRIO_DEFAULT_CLASS_AND_PRIO;
7f452159 1423
8b330d7d 1424 return ioprio_normalize(p);
7f452159
LP
1425}
1426
5e98086d
ZJS
1427bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
1428 assert(c);
1429
61198784 1430 /* Explicit setting wins */
5e98086d
ZJS
1431 if (c->mount_apivfs_set)
1432 return c->mount_apivfs;
1433
61198784 1434 /* Default to "yes" if root directory or image are specified */
74e12520 1435 if (exec_context_with_rootfs(c))
61198784
ZJS
1436 return true;
1437
5e98086d
ZJS
1438 return false;
1439}
1440
d3070fbd 1441void exec_context_free_log_extra_fields(ExecContext *c) {
d3070fbd
LP
1442 assert(c);
1443
f7df8adb
MY
1444 FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
1445 free(field->iov_base);
1446
d3070fbd
LP
1447 c->log_extra_fields = mfree(c->log_extra_fields);
1448 c->n_log_extra_fields = 0;
1449}
1450
6f765baf 1451void exec_context_revert_tty(ExecContext *c) {
254d1313 1452 _cleanup_close_ int fd = -EBADF;
0ba976e8
LP
1453 const char *path;
1454 struct stat st;
6f765baf
LP
1455 int r;
1456
1457 assert(c);
1458
1459 /* First, reset the TTY (possibly kicking everybody else from the TTY) */
f121efd3 1460 exec_context_tty_reset(c, /* parameters= */ NULL);
6f765baf
LP
1461
1462 /* And then undo what chown_terminal() did earlier. Note that we only do this if we have a path
1463 * configured. If the TTY was passed to us as file descriptor we assume the TTY is opened and managed
1464 * by whoever passed it to us and thus knows better when and how to chmod()/chown() it back. */
0ba976e8
LP
1465 if (!exec_context_may_touch_tty(c))
1466 return;
6f765baf 1467
0ba976e8
LP
1468 path = exec_context_tty_path(c);
1469 if (!path)
1470 return;
6f765baf 1471
026a8b02 1472 fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
0ba976e8
LP
1473 if (fd < 0)
1474 return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
1475 "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1476 path);
1477
1478 if (fstat(fd, &st) < 0)
1479 return (void) log_warning_errno(errno, "Failed to stat TTY '%s', ignoring: %m", path);
1480
1481 /* Let's add a superficial check that we only do this for stuff that looks like a TTY. We only check
1482 * if things are a character device, since a proper check either means we'd have to open the TTY and
1483 * use isatty(), but we'd rather not do that since opening TTYs comes with all kinds of side-effects
1484 * and is slow. Or we'd have to hardcode dev_t major information, which we'd rather avoid. Why bother
1485 * with this at all? → https://github.com/systemd/systemd/issues/19213 */
1486 if (!S_ISCHR(st.st_mode))
1487 return log_warning("Configured TTY '%s' is not actually a character device, ignoring.", path);
1488
1489 r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
1490 if (r < 0)
026a8b02 1491 log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s to " UID_FMT ":" GID_FMT ", ignoring: %m", path, (uid_t) 0, (gid_t) TTY_GID);
6f765baf
LP
1492}
1493
4c2f5842
LP
1494int exec_context_get_clean_directories(
1495 ExecContext *c,
1496 char **prefix,
1497 ExecCleanMask mask,
1498 char ***ret) {
1499
1500 _cleanup_strv_free_ char **l = NULL;
4c2f5842
LP
1501 int r;
1502
1503 assert(c);
1504 assert(prefix);
1505 assert(ret);
1506
5b10116e 1507 for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
4c2f5842
LP
1508 if (!FLAGS_SET(mask, 1U << t))
1509 continue;
1510
1511 if (!prefix[t])
1512 continue;
1513
f7df8adb 1514 FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
4c2f5842
LP
1515 char *j;
1516
f7df8adb 1517 j = path_join(prefix[t], i->path);
4c2f5842
LP
1518 if (!j)
1519 return -ENOMEM;
1520
1521 r = strv_consume(&l, j);
1522 if (r < 0)
1523 return r;
7f622a19
YW
1524
1525 /* Also remove private directories unconditionally. */
1526 if (t != EXEC_DIRECTORY_CONFIGURATION) {
f7df8adb 1527 j = path_join(prefix[t], "private", i->path);
211a3d87
LB
1528 if (!j)
1529 return -ENOMEM;
1530
1531 r = strv_consume(&l, j);
1532 if (r < 0)
1533 return r;
1534 }
1535
f7df8adb 1536 STRV_FOREACH(symlink, i->symlinks) {
211a3d87 1537 j = path_join(prefix[t], *symlink);
7f622a19
YW
1538 if (!j)
1539 return -ENOMEM;
1540
1541 r = strv_consume(&l, j);
1542 if (r < 0)
1543 return r;
1544 }
4c2f5842
LP
1545 }
1546 }
1547
1548 *ret = TAKE_PTR(l);
1549 return 0;
1550}
1551
1552int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1553 ExecCleanMask mask = 0;
1554
1555 assert(c);
1556 assert(ret);
1557
1558 for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
211a3d87 1559 if (c->directories[t].n_items > 0)
4c2f5842
LP
1560 mask |= 1U << t;
1561
1562 *ret = mask;
1563 return 0;
1564}
1565
ef44aa83
DDM
1566int exec_context_get_oom_score_adjust(const ExecContext *c) {
1567 int n = 0, r;
1568
1569 assert(c);
1570
1571 if (c->oom_score_adjust_set)
1572 return c->oom_score_adjust;
1573
1574 r = get_oom_score_adjust(&n);
1575 if (r < 0)
1576 log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
1577
1578 return n;
1579}
1580
1581uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1582 _cleanup_free_ char *t = NULL;
1583 uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1584 int r;
1585
1586 assert(c);
1587
1588 if (c->coredump_filter_set)
1589 return c->coredump_filter;
1590
1591 r = read_one_line_file("/proc/self/coredump_filter", &t);
1592 if (r < 0)
1593 log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
1594 else {
1595 r = safe_atoux64(t, &n);
1596 if (r < 0)
1597 log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
1598 }
1599
1600 return n;
1601}
1602
1603int exec_context_get_nice(const ExecContext *c) {
1604 int n;
1605
1606 assert(c);
1607
1608 if (c->nice_set)
1609 return c->nice;
1610
1611 errno = 0;
1612 n = getpriority(PRIO_PROCESS, 0);
1613 if (errno > 0) {
1614 log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
1615 n = 0;
1616 }
1617
1618 return n;
1619}
1620
1621int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1622 int n;
1623
1624 assert(c);
1625
1626 if (c->cpu_sched_set)
1627 return c->cpu_sched_policy;
1628
1629 n = sched_getscheduler(0);
1630 if (n < 0)
1631 log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
1632
1633 return n < 0 ? SCHED_OTHER : n;
1634}
1635
1636int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1637 struct sched_param p = {};
1638 int r;
1639
1640 assert(c);
1641
1642 if (c->cpu_sched_set)
1643 return c->cpu_sched_priority;
1644
1645 r = sched_getparam(0, &p);
1646 if (r < 0)
1647 log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
1648
1649 return r >= 0 ? p.sched_priority : 0;
1650}
1651
1652uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1653 int r;
1654
1655 assert(c);
1656
1657 if (c->timer_slack_nsec != NSEC_INFINITY)
1658 return c->timer_slack_nsec;
1659
1660 r = prctl(PR_GET_TIMERSLACK);
1661 if (r < 0)
1662 log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
1663
1664 return (uint64_t) MAX(r, 0);
1665}
1666
d1a5be82
LP
1667bool exec_context_get_set_login_environment(const ExecContext *c) {
1668 assert(c);
1669
1670 if (c->set_login_environment >= 0)
1671 return c->set_login_environment;
1672
1673 return c->user || c->dynamic_user || c->pam_name;
1674}
1675
ef44aa83
DDM
1676char** exec_context_get_syscall_filter(const ExecContext *c) {
1677 _cleanup_strv_free_ char **l = NULL;
1678
1679 assert(c);
1680
1681#if HAVE_SECCOMP
1682 void *id, *val;
1683 HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
1684 _cleanup_free_ char *name = NULL;
1685 const char *e = NULL;
1686 char *s;
1687 int num = PTR_TO_INT(val);
1688
1689 if (c->syscall_allow_list && num >= 0)
1690 /* syscall with num >= 0 in allow-list is denied. */
1691 continue;
1692
1693 name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
1694 if (!name)
1695 continue;
1696
1697 if (num >= 0) {
1698 e = seccomp_errno_or_action_to_string(num);
1699 if (e) {
1700 s = strjoin(name, ":", e);
1701 if (!s)
1702 return NULL;
1703 } else {
1704 if (asprintf(&s, "%s:%d", name, num) < 0)
1705 return NULL;
1706 }
1707 } else
1708 s = TAKE_PTR(name);
1709
1710 if (strv_consume(&l, s) < 0)
1711 return NULL;
1712 }
1713
1714 strv_sort(l);
1715#endif
1716
1717 return l ? TAKE_PTR(l) : strv_new(NULL);
1718}
1719
1720char** exec_context_get_syscall_archs(const ExecContext *c) {
1721 _cleanup_strv_free_ char **l = NULL;
1722
1723 assert(c);
1724
1725#if HAVE_SECCOMP
1726 void *id;
1727 SET_FOREACH(id, c->syscall_archs) {
1728 const char *name;
1729
1730 name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
1731 if (!name)
1732 continue;
1733
1734 if (strv_extend(&l, name) < 0)
1735 return NULL;
1736 }
1737
1738 strv_sort(l);
1739#endif
1740
1741 return l ? TAKE_PTR(l) : strv_new(NULL);
1742}
1743
1744char** exec_context_get_syscall_log(const ExecContext *c) {
1745 _cleanup_strv_free_ char **l = NULL;
1746
1747 assert(c);
1748
1749#if HAVE_SECCOMP
1750 void *id, *val;
1751 HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1752 char *name = NULL;
1753
1754 name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
1755 if (!name)
1756 continue;
1757
1758 if (strv_consume(&l, name) < 0)
1759 return NULL;
1760 }
1761
1762 strv_sort(l);
1763#endif
1764
1765 return l ? TAKE_PTR(l) : strv_new(NULL);
1766}
1767
1768char** exec_context_get_address_families(const ExecContext *c) {
1769 _cleanup_strv_free_ char **l = NULL;
1770 void *af;
1771
1772 assert(c);
1773
1774 SET_FOREACH(af, c->address_families) {
1775 const char *name;
1776
1777 name = af_to_name(PTR_TO_INT(af));
1778 if (!name)
1779 continue;
1780
1781 if (strv_extend(&l, name) < 0)
1782 return NULL;
1783 }
1784
1785 strv_sort(l);
1786
1787 return l ? TAKE_PTR(l) : strv_new(NULL);
1788}
1789
1790char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1791 _cleanup_strv_free_ char **l = NULL;
1792
1793 assert(c);
1794
1795#if HAVE_LIBBPF
1796 l = set_get_strv(c->restrict_filesystems);
1797 if (!l)
1798 return NULL;
1799
1800 strv_sort(l);
1801#endif
1802
1803 return l ? TAKE_PTR(l) : strv_new(NULL);
1804}
1805
b58b4116 1806void exec_status_start(ExecStatus *s, pid_t pid) {
034c6ed7 1807 assert(s);
5cb5a6ff 1808
2ed26ed0
LP
1809 *s = (ExecStatus) {
1810 .pid = pid,
1811 };
1812
fa5a0251 1813 dual_timestamp_now(&s->start_timestamp);
b58b4116
LP
1814}
1815
34cf6c43 1816void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
b58b4116
LP
1817 assert(s);
1818
d46b79bb 1819 if (s->pid != pid)
2ed26ed0
LP
1820 *s = (ExecStatus) {
1821 .pid = pid,
1822 };
b58b4116 1823
fa5a0251 1824 dual_timestamp_now(&s->exit_timestamp);
9fb86720 1825
034c6ed7
LP
1826 s->code = code;
1827 s->status = status;
169c1bda 1828
6f765baf
LP
1829 if (context && context->utmp_id)
1830 (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
9fb86720
LP
1831}
1832
6a1d4d9f
LP
1833void exec_status_reset(ExecStatus *s) {
1834 assert(s);
1835
1836 *s = (ExecStatus) {};
1837}
1838
34cf6c43 1839void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
9fb86720
LP
1840 assert(s);
1841 assert(f);
1842
9fb86720
LP
1843 if (s->pid <= 0)
1844 return;
1845
4c940960
LP
1846 prefix = strempty(prefix);
1847
9fb86720 1848 fprintf(f,
ccd06097
ZJS
1849 "%sPID: "PID_FMT"\n",
1850 prefix, s->pid);
9fb86720 1851
af9d16e1 1852 if (dual_timestamp_is_set(&s->start_timestamp))
9fb86720
LP
1853 fprintf(f,
1854 "%sStart Timestamp: %s\n",
04f5c018 1855 prefix, FORMAT_TIMESTAMP(s->start_timestamp.realtime));
9fb86720 1856
af9d16e1 1857 if (dual_timestamp_is_set(&s->exit_timestamp))
9fb86720
LP
1858 fprintf(f,
1859 "%sExit Timestamp: %s\n"
1860 "%sExit Code: %s\n"
1861 "%sExit Status: %i\n",
04f5c018 1862 prefix, FORMAT_TIMESTAMP(s->exit_timestamp.realtime),
9fb86720
LP
1863 prefix, sigchld_code_to_string(s->code),
1864 prefix, s->status);
5cb5a6ff 1865}
44d8db9e 1866
34cf6c43 1867static void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
e1d75803 1868 _cleanup_free_ char *cmd = NULL;
4c940960 1869 const char *prefix2;
44d8db9e
LP
1870
1871 assert(c);
1872 assert(f);
1873
4c940960 1874 prefix = strempty(prefix);
63c372cb 1875 prefix2 = strjoina(prefix, "\t");
44d8db9e 1876
4ef15008 1877 cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
38553034 1878
44d8db9e
LP
1879 fprintf(f,
1880 "%sCommand Line: %s\n",
38553034 1881 prefix, strnull(cmd));
44d8db9e 1882
9fb86720 1883 exec_status_dump(&c->exec_status, f, prefix2);
44d8db9e
LP
1884}
1885
1886void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
1887 assert(f);
1888
4c940960 1889 prefix = strempty(prefix);
44d8db9e 1890
03677889
YW
1891 LIST_FOREACH(command, i, c)
1892 exec_command_dump(i, f, prefix);
44d8db9e 1893}
94f04347 1894
a6a80b4f
LP
1895void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
1896 ExecCommand *end;
1897
1898 assert(l);
1899 assert(e);
1900
1901 if (*l) {
35b8ca3a 1902 /* It's kind of important, that we keep the order here */
cc232fa0 1903 end = LIST_FIND_TAIL(command, *l);
71fda00f 1904 LIST_INSERT_AFTER(command, *l, end, e);
a6a80b4f 1905 } else
3ff67ec4 1906 *l = e;
a6a80b4f
LP
1907}
1908
26fd040d
LP
1909int exec_command_set(ExecCommand *c, const char *path, ...) {
1910 va_list ap;
1911 char **l, *p;
1912
1913 assert(c);
1914 assert(path);
1915
1916 va_start(ap, path);
1917 l = strv_new_ap(path, ap);
1918 va_end(ap);
1919
1920 if (!l)
1921 return -ENOMEM;
1922
250a918d
LP
1923 p = strdup(path);
1924 if (!p) {
26fd040d
LP
1925 strv_free(l);
1926 return -ENOMEM;
1927 }
1928
6897dfe8 1929 free_and_replace(c->path, p);
26fd040d 1930
130d3d22 1931 return strv_free_and_replace(c->argv, l);
26fd040d
LP
1932}
1933
86b23b07 1934int exec_command_append(ExecCommand *c, const char *path, ...) {
e63ff941 1935 _cleanup_strv_free_ char **l = NULL;
86b23b07 1936 va_list ap;
86b23b07
JS
1937 int r;
1938
1939 assert(c);
1940 assert(path);
1941
1942 va_start(ap, path);
1943 l = strv_new_ap(path, ap);
1944 va_end(ap);
1945
1946 if (!l)
1947 return -ENOMEM;
1948
e287086b 1949 r = strv_extend_strv(&c->argv, l, false);
e63ff941 1950 if (r < 0)
86b23b07 1951 return r;
86b23b07
JS
1952
1953 return 0;
1954}
1955
437f3e35
LP
1956static char *destroy_tree(char *path) {
1957 if (!path)
1958 return NULL;
9c0c6701 1959
437f3e35
LP
1960 if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
1961 log_debug("Spawning process to nuke '%s'", path);
9c0c6701 1962
437f3e35
LP
1963 (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
1964 }
9c0c6701 1965
437f3e35 1966 return mfree(path);
9c0c6701
DDM
1967}
1968
bb5232b6 1969void exec_shared_runtime_done(ExecSharedRuntime *rt) {
e03975b9 1970 assert(rt);
e8a565cb
YW
1971
1972 if (rt->manager)
e76506b7 1973 (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
e8a565cb 1974
e52a696a
DDM
1975 rt->id = mfree(rt->id);
1976 rt->tmp_dir = mfree(rt->tmp_dir);
1977 rt->var_tmp_dir = mfree(rt->var_tmp_dir);
1978 safe_close_pair(rt->netns_storage_socket);
1979 safe_close_pair(rt->ipcns_storage_socket);
bb5232b6
LB
1980}
1981
1982static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
e03975b9
MY
1983 if (!rt)
1984 return NULL;
bb5232b6 1985
e03975b9 1986 exec_shared_runtime_done(rt);
e52a696a
DDM
1987 return mfree(rt);
1988}
1989
1990DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
1991DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
1992
1993ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
e52a696a
DDM
1994 if (!rt)
1995 return NULL;
1996
1997 assert(rt->n_ref > 0);
1998 rt->n_ref--;
1999
2000 if (rt->n_ref > 0)
2001 return NULL;
56a13a49 2002
437f3e35
LP
2003 rt->tmp_dir = destroy_tree(rt->tmp_dir);
2004 rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
e8a565cb 2005
e52a696a 2006 return exec_shared_runtime_free(rt);
e8a565cb
YW
2007}
2008
e76506b7 2009static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
56a13a49 2010 _cleanup_free_ char *id_copy = NULL;
e76506b7 2011 ExecSharedRuntime *n;
613b411c 2012
8e8009dc 2013 assert(ret);
613b411c 2014
56a13a49
ZJS
2015 id_copy = strdup(id);
2016 if (!id_copy)
2017 return -ENOMEM;
2018
e76506b7 2019 n = new(ExecSharedRuntime, 1);
8e8009dc 2020 if (!n)
613b411c
LP
2021 return -ENOMEM;
2022
e76506b7 2023 *n = (ExecSharedRuntime) {
56a13a49 2024 .id = TAKE_PTR(id_copy),
71136404
LP
2025 .netns_storage_socket = EBADF_PAIR,
2026 .ipcns_storage_socket = EBADF_PAIR,
8e8009dc
LP
2027 };
2028
2029 *ret = n;
613b411c
LP
2030 return 0;
2031}
2032
e76506b7 2033static int exec_shared_runtime_add(
e8a565cb
YW
2034 Manager *m,
2035 const char *id,
56a13a49
ZJS
2036 char **tmp_dir,
2037 char **var_tmp_dir,
2038 int netns_storage_socket[2],
a70581ff 2039 int ipcns_storage_socket[2],
e76506b7 2040 ExecSharedRuntime **ret) {
e8a565cb 2041
e76506b7 2042 _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
613b411c
LP
2043 int r;
2044
e8a565cb 2045 assert(m);
613b411c
LP
2046 assert(id);
2047
a70581ff 2048 /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
56a13a49 2049
e76506b7 2050 r = exec_shared_runtime_allocate(&rt, id);
613b411c
LP
2051 if (r < 0)
2052 return r;
2053
e76506b7 2054 r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
56a13a49
ZJS
2055 if (r < 0)
2056 return r;
e8a565cb 2057
56a13a49
ZJS
2058 assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
2059 rt->tmp_dir = TAKE_PTR(*tmp_dir);
2060 rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
e8a565cb
YW
2061
2062 if (netns_storage_socket) {
56a13a49
ZJS
2063 rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
2064 rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
613b411c
LP
2065 }
2066
a70581ff
XR
2067 if (ipcns_storage_socket) {
2068 rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
2069 rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
2070 }
2071
e8a565cb
YW
2072 rt->manager = m;
2073
2074 if (ret)
2075 *ret = rt;
e76506b7 2076 /* do not remove created ExecSharedRuntime object when the operation succeeds. */
56a13a49 2077 TAKE_PTR(rt);
e8a565cb
YW
2078 return 0;
2079}
2080
e76506b7 2081static int exec_shared_runtime_make(
74aaf59b
LP
2082 Manager *m,
2083 const ExecContext *c,
2084 const char *id,
e76506b7 2085 ExecSharedRuntime **ret) {
74aaf59b 2086
56a13a49 2087 _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
71136404 2088 _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
e8a565cb
YW
2089 int r;
2090
2091 assert(m);
2092 assert(c);
2093 assert(id);
2094
e76506b7 2095 /* It is not necessary to create ExecSharedRuntime object. */
fde36d25 2096 if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && !c->private_tmp) {
74aaf59b 2097 *ret = NULL;
e8a565cb 2098 return 0;
74aaf59b 2099 }
e8a565cb 2100
efa2f3a1
TM
2101 if (c->private_tmp &&
2102 !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
2103 (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
2104 prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
e8a565cb 2105 r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
613b411c
LP
2106 if (r < 0)
2107 return r;
2108 }
2109
a7774a8c 2110 if (exec_needs_network_namespace(c))
e8a565cb
YW
2111 if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
2112 return -errno;
e8a565cb 2113
a7774a8c 2114 if (exec_needs_ipc_namespace(c))
a70581ff
XR
2115 if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2116 return -errno;
a70581ff 2117
e76506b7 2118 r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
e8a565cb
YW
2119 if (r < 0)
2120 return r;
2121
613b411c
LP
2122 return 1;
2123}
2124
e76506b7
DDM
2125int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
2126 ExecSharedRuntime *rt;
e8a565cb 2127 int r;
613b411c 2128
e8a565cb
YW
2129 assert(m);
2130 assert(id);
2131 assert(ret);
2132
e76506b7 2133 rt = hashmap_get(m->exec_shared_runtime_by_id, id);
e8a565cb 2134 if (rt)
e76506b7 2135 /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
e8a565cb
YW
2136 goto ref;
2137
74aaf59b
LP
2138 if (!create) {
2139 *ret = NULL;
e8a565cb 2140 return 0;
74aaf59b 2141 }
e8a565cb
YW
2142
2143 /* If not found, then create a new object. */
e76506b7 2144 r = exec_shared_runtime_make(m, c, id, &rt);
74aaf59b 2145 if (r < 0)
e8a565cb 2146 return r;
74aaf59b 2147 if (r == 0) {
e76506b7 2148 /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
74aaf59b
LP
2149 *ret = NULL;
2150 return 0;
2151 }
613b411c 2152
e8a565cb
YW
2153ref:
2154 /* increment reference counter. */
2155 rt->n_ref++;
2156 *ret = rt;
2157 return 1;
2158}
613b411c 2159
e76506b7
DDM
2160int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
2161 ExecSharedRuntime *rt;
e8a565cb
YW
2162
2163 assert(m);
613b411c
LP
2164 assert(f);
2165 assert(fds);
2166
e76506b7 2167 HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
e8a565cb 2168 fprintf(f, "exec-runtime=%s", rt->id);
613b411c 2169
e8a565cb
YW
2170 if (rt->tmp_dir)
2171 fprintf(f, " tmp-dir=%s", rt->tmp_dir);
613b411c 2172
e8a565cb
YW
2173 if (rt->var_tmp_dir)
2174 fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
613b411c 2175
e8a565cb
YW
2176 if (rt->netns_storage_socket[0] >= 0) {
2177 int copy;
613b411c 2178
e8a565cb
YW
2179 copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2180 if (copy < 0)
2181 return copy;
613b411c 2182
e8a565cb
YW
2183 fprintf(f, " netns-socket-0=%i", copy);
2184 }
613b411c 2185
e8a565cb
YW
2186 if (rt->netns_storage_socket[1] >= 0) {
2187 int copy;
613b411c 2188
e8a565cb
YW
2189 copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2190 if (copy < 0)
2191 return copy;
613b411c 2192
e8a565cb
YW
2193 fprintf(f, " netns-socket-1=%i", copy);
2194 }
2195
a70581ff
XR
2196 if (rt->ipcns_storage_socket[0] >= 0) {
2197 int copy;
2198
2199 copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
2200 if (copy < 0)
2201 return copy;
2202
2203 fprintf(f, " ipcns-socket-0=%i", copy);
2204 }
2205
2206 if (rt->ipcns_storage_socket[1] >= 0) {
2207 int copy;
2208
2209 copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
2210 if (copy < 0)
2211 return copy;
2212
2213 fprintf(f, " ipcns-socket-1=%i", copy);
2214 }
2215
e8a565cb 2216 fputc('\n', f);
613b411c
LP
2217 }
2218
2219 return 0;
2220}
2221
e76506b7
DDM
2222int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
2223 _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
154eb43f 2224 ExecSharedRuntime *rt = NULL;
613b411c
LP
2225 int r;
2226
e8a565cb
YW
2227 /* This is for the migration from old (v237 or earlier) deserialization text.
2228 * Due to the bug #7790, this may not work with the units that use JoinsNamespaceOf=.
e76506b7 2229 * Even if the ExecSharedRuntime object originally created by the other unit, we cannot judge
e8a565cb
YW
2230 * so or not from the serialized text, then we always creates a new object owned by this. */
2231
2232 assert(u);
613b411c
LP
2233 assert(key);
2234 assert(value);
2235
e76506b7 2236 /* Manager manages ExecSharedRuntime objects by the unit id.
e8a565cb
YW
2237 * So, we omit the serialized text when the unit does not have id (yet?)... */
2238 if (isempty(u->id)) {
2239 log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
2240 return 0;
2241 }
613b411c 2242
154eb43f
LB
2243 if (u->manager) {
2244 if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
2245 return log_oom();
e8a565cb 2246
154eb43f
LB
2247 rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
2248 }
e8a565cb 2249 if (!rt) {
e76506b7 2250 if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
f2341e0a 2251 return log_oom();
613b411c 2252
e8a565cb
YW
2253 rt = rt_create;
2254 }
2255
2256 if (streq(key, "tmp-dir")) {
cbc165d1
ZJS
2257 if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
2258 return -ENOMEM;
613b411c
LP
2259
2260 } else if (streq(key, "var-tmp-dir")) {
cbc165d1
ZJS
2261 if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
2262 return -ENOMEM;
613b411c
LP
2263
2264 } else if (streq(key, "netns-socket-0")) {
e8a565cb
YW
2265
2266 safe_close(rt->netns_storage_socket[0]);
dff9808a
LP
2267 rt->netns_storage_socket[0] = deserialize_fd(fds, value);
2268 if (rt->netns_storage_socket[0] < 0)
2269 return 0;
e8a565cb 2270
613b411c 2271 } else if (streq(key, "netns-socket-1")) {
e8a565cb
YW
2272
2273 safe_close(rt->netns_storage_socket[1]);
dff9808a
LP
2274 rt->netns_storage_socket[1] = deserialize_fd(fds, value);
2275 if (rt->netns_storage_socket[1] < 0)
2276 return 0;
613b411c
LP
2277 } else
2278 return 0;
2279
e76506b7 2280 /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
154eb43f 2281 if (rt_create && u->manager) {
e76506b7 2282 r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
e8a565cb 2283 if (r < 0) {
3fe91079 2284 log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
e8a565cb
YW
2285 return 0;
2286 }
613b411c 2287
e8a565cb 2288 rt_create->manager = u->manager;
613b411c 2289
e8a565cb 2290 /* Avoid cleanup */
56a13a49 2291 TAKE_PTR(rt_create);
e8a565cb 2292 }
98b47d54 2293
e8a565cb
YW
2294 return 1;
2295}
613b411c 2296
e76506b7 2297int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
56a13a49
ZJS
2298 _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
2299 char *id = NULL;
a70581ff 2300 int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
99534007 2301 const char *p, *v = ASSERT_PTR(value);
e8a565cb 2302 size_t n;
613b411c 2303
e8a565cb 2304 assert(m);
e8a565cb 2305 assert(fds);
98b47d54 2306
e8a565cb 2307 n = strcspn(v, " ");
2f82562b 2308 id = strndupa_safe(v, n);
e8a565cb
YW
2309 if (v[n] != ' ')
2310 goto finalize;
2311 p = v + n + 1;
2312
2313 v = startswith(p, "tmp-dir=");
2314 if (v) {
2315 n = strcspn(v, " ");
56a13a49
ZJS
2316 tmp_dir = strndup(v, n);
2317 if (!tmp_dir)
2318 return log_oom();
e8a565cb
YW
2319 if (v[n] != ' ')
2320 goto finalize;
2321 p = v + n + 1;
2322 }
2323
2324 v = startswith(p, "var-tmp-dir=");
2325 if (v) {
2326 n = strcspn(v, " ");
56a13a49
ZJS
2327 var_tmp_dir = strndup(v, n);
2328 if (!var_tmp_dir)
2329 return log_oom();
e8a565cb
YW
2330 if (v[n] != ' ')
2331 goto finalize;
2332 p = v + n + 1;
2333 }
2334
2335 v = startswith(p, "netns-socket-0=");
2336 if (v) {
2337 char *buf;
2338
2339 n = strcspn(v, " ");
2f82562b 2340 buf = strndupa_safe(v, n);
c413bb28 2341
dff9808a 2342 netns_fdpair[0] = deserialize_fd(fds, buf);
e652663a 2343 if (netns_fdpair[0] < 0)
dff9808a 2344 return netns_fdpair[0];
e8a565cb
YW
2345 if (v[n] != ' ')
2346 goto finalize;
2347 p = v + n + 1;
613b411c
LP
2348 }
2349
e8a565cb
YW
2350 v = startswith(p, "netns-socket-1=");
2351 if (v) {
2352 char *buf;
98b47d54 2353
e8a565cb 2354 n = strcspn(v, " ");
2f82562b 2355 buf = strndupa_safe(v, n);
a70581ff 2356
dff9808a 2357 netns_fdpair[1] = deserialize_fd(fds, buf);
e652663a 2358 if (netns_fdpair[1] < 0)
dff9808a 2359 return netns_fdpair[1];
a70581ff
XR
2360 if (v[n] != ' ')
2361 goto finalize;
2362 p = v + n + 1;
2363 }
2364
2365 v = startswith(p, "ipcns-socket-0=");
2366 if (v) {
2367 char *buf;
2368
2369 n = strcspn(v, " ");
2f82562b 2370 buf = strndupa_safe(v, n);
a70581ff 2371
dff9808a 2372 ipcns_fdpair[0] = deserialize_fd(fds, buf);
e652663a 2373 if (ipcns_fdpair[0] < 0)
dff9808a 2374 return ipcns_fdpair[0];
a70581ff
XR
2375 if (v[n] != ' ')
2376 goto finalize;
2377 p = v + n + 1;
2378 }
2379
2380 v = startswith(p, "ipcns-socket-1=");
2381 if (v) {
2382 char *buf;
2383
2384 n = strcspn(v, " ");
2f82562b 2385 buf = strndupa_safe(v, n);
a70581ff 2386
dff9808a 2387 ipcns_fdpair[1] = deserialize_fd(fds, buf);
e652663a 2388 if (ipcns_fdpair[1] < 0)
dff9808a 2389 return ipcns_fdpair[1];
e8a565cb 2390 }
98b47d54 2391
e8a565cb 2392finalize:
e76506b7 2393 r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
7d853ca6 2394 if (r < 0)
56a13a49
ZJS
2395 return log_debug_errno(r, "Failed to add exec-runtime: %m");
2396 return 0;
e8a565cb 2397}
613b411c 2398
e76506b7
DDM
2399void exec_shared_runtime_vacuum(Manager *m) {
2400 ExecSharedRuntime *rt;
e8a565cb
YW
2401
2402 assert(m);
2403
e76506b7 2404 /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
e8a565cb 2405
e76506b7 2406 HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
e8a565cb
YW
2407 if (rt->n_ref > 0)
2408 continue;
2409
e52a696a 2410 (void) exec_shared_runtime_free(rt);
e8a565cb 2411 }
613b411c
LP
2412}
2413
9c0c6701
DDM
2414int exec_runtime_make(
2415 const Unit *unit,
2416 const ExecContext *context,
2417 ExecSharedRuntime *shared,
2418 DynamicCreds *creds,
2419 ExecRuntime **ret) {
71136404 2420 _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
9c0c6701 2421 _cleanup_free_ char *ephemeral = NULL;
28135da3 2422 _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
9c0c6701 2423 int r;
28135da3 2424
9c0c6701
DDM
2425 assert(unit);
2426 assert(context);
28135da3
DDM
2427 assert(ret);
2428
9c0c6701 2429 if (!shared && !creds && !exec_needs_ephemeral(context)) {
28135da3
DDM
2430 *ret = NULL;
2431 return 0;
2432 }
2433
9c0c6701
DDM
2434 if (exec_needs_ephemeral(context)) {
2435 r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
2436 if (r < 0)
2437 return r;
2438
2439 r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
2440 if (r < 0)
2441 return r;
2442
2443 if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
2444 return -errno;
2445 }
2446
28135da3
DDM
2447 rt = new(ExecRuntime, 1);
2448 if (!rt)
2449 return -ENOMEM;
2450
2451 *rt = (ExecRuntime) {
2452 .shared = shared,
15220772 2453 .dynamic_creds = creds,
9c0c6701
DDM
2454 .ephemeral_copy = TAKE_PTR(ephemeral),
2455 .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
2456 .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
28135da3
DDM
2457 };
2458
2459 *ret = TAKE_PTR(rt);
2460 return 1;
2461}
2462
2463ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
2464 if (!rt)
2465 return NULL;
2466
2467 exec_shared_runtime_unref(rt->shared);
15220772 2468 dynamic_creds_unref(rt->dynamic_creds);
9c0c6701 2469
437f3e35 2470 rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
9c0c6701 2471
9c0c6701 2472 safe_close_pair(rt->ephemeral_storage_socket);
28135da3
DDM
2473 return mfree(rt);
2474}
2475
2476ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
2477 if (!rt)
2478 return NULL;
2479
2480 rt->shared = exec_shared_runtime_destroy(rt->shared);
15220772 2481 rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
28135da3
DDM
2482 return exec_runtime_free(rt);
2483}
2484
bb5232b6
LB
2485void exec_runtime_clear(ExecRuntime *rt) {
2486 if (!rt)
2487 return;
2488
2489 safe_close_pair(rt->ephemeral_storage_socket);
2490 rt->ephemeral_copy = mfree(rt->ephemeral_copy);
2491}
2492
fba173ff 2493void exec_params_shallow_clear(ExecParameters *p) {
b9c04eaf
YW
2494 if (!p)
2495 return;
2496
fba173ff
LB
2497 /* This is called on the PID1 side, as many of the struct's FDs are only borrowed, and actually
2498 * owned by the manager or other objects, and reused across multiple units. */
2499
c3f8a065
LP
2500 p->environment = strv_free(p->environment);
2501 p->fd_names = strv_free(p->fd_names);
154eb43f 2502 p->files_env = strv_free(p->files_env);
c3f8a065
LP
2503 p->fds = mfree(p->fds);
2504 p->exec_fd = safe_close(p->exec_fd);
b646fc32 2505 p->user_lookup_fd = -EBADF;
352ec23c 2506 p->bpf_restrict_fs_map_fd = -EBADF;
b646fc32
LB
2507 p->unit_id = mfree(p->unit_id);
2508 p->invocation_id = SD_ID128_NULL;
2509 p->invocation_id_string[0] = '\0';
bb5232b6
LB
2510 p->confirm_spawn = mfree(p->confirm_spawn);
2511}
2512
fba173ff 2513void exec_params_deep_clear(ExecParameters *p) {
bb5232b6
LB
2514 if (!p)
2515 return;
2516
fba173ff
LB
2517 /* This is called on the sd-executor side, where everything received is owned by the process and has
2518 * to be fully cleaned up to make sanitizers and analyzers happy, as opposed as the shallow clean
2519 * function above. */
2520
d3eb74f8 2521 close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds);
bb5232b6
LB
2522
2523 p->cgroup_path = mfree(p->cgroup_path);
2524
d31330c1 2525 if (p->prefix) {
fc932ed4 2526 free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
d31330c1
FS
2527 p->prefix = mfree(p->prefix);
2528 }
2529
bb5232b6
LB
2530 p->received_credentials_directory = mfree(p->received_credentials_directory);
2531 p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
2532
26f937e5
LP
2533 if (p->idle_pipe) {
2534 close_many_and_free(p->idle_pipe, 4);
2535 p->idle_pipe = NULL;
2536 }
bb5232b6
LB
2537
2538 p->stdin_fd = safe_close(p->stdin_fd);
2539 p->stdout_fd = safe_close(p->stdout_fd);
2540 p->stderr_fd = safe_close(p->stderr_fd);
2541
2542 p->notify_socket = mfree(p->notify_socket);
2543
2544 open_file_free_many(&p->open_files);
2545
2546 p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
2547
fba173ff 2548 exec_params_shallow_clear(p);
b9c04eaf
YW
2549}
2550
211a3d87
LB
2551void exec_directory_done(ExecDirectory *d) {
2552 if (!d)
2553 return;
2554
f7df8adb
MY
2555 FOREACH_ARRAY(i, d->items, d->n_items) {
2556 free(i->path);
2557 strv_free(i->symlinks);
211a3d87
LB
2558 }
2559
2560 d->items = mfree(d->items);
2561 d->n_items = 0;
2562 d->mode = 0755;
2563}
2564
564e5c98
YW
2565static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
2566 assert(d);
2567 assert(path);
2568
f7df8adb
MY
2569 FOREACH_ARRAY(i, d->items, d->n_items)
2570 if (path_equal(i->path, path))
2571 return i;
564e5c98
YW
2572
2573 return NULL;
2574}
2575
2576int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink) {
211a3d87
LB
2577 _cleanup_strv_free_ char **s = NULL;
2578 _cleanup_free_ char *p = NULL;
564e5c98
YW
2579 ExecDirectoryItem *existing;
2580 int r;
211a3d87
LB
2581
2582 assert(d);
211a3d87
LB
2583 assert(path);
2584
564e5c98
YW
2585 existing = exec_directory_find(d, path);
2586 if (existing) {
2587 r = strv_extend(&existing->symlinks, symlink);
2588 if (r < 0)
2589 return r;
2590
2591 return 0; /* existing item is updated */
2592 }
2593
211a3d87
LB
2594 p = strdup(path);
2595 if (!p)
2596 return -ENOMEM;
2597
564e5c98
YW
2598 if (symlink) {
2599 s = strv_new(symlink);
211a3d87
LB
2600 if (!s)
2601 return -ENOMEM;
2602 }
2603
564e5c98 2604 if (!GREEDY_REALLOC(d->items, d->n_items + 1))
211a3d87
LB
2605 return -ENOMEM;
2606
564e5c98 2607 d->items[d->n_items++] = (ExecDirectoryItem) {
211a3d87
LB
2608 .path = TAKE_PTR(p),
2609 .symlinks = TAKE_PTR(s),
2610 };
2611
564e5c98 2612 return 1; /* new item is added */
211a3d87
LB
2613}
2614
a2ab603c
YW
2615static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
2616 assert(a);
2617 assert(b);
2618
2619 return path_compare(a->path, b->path);
2620}
2621
2622void exec_directory_sort(ExecDirectory *d) {
2623 assert(d);
2624
2625 /* Sort the exec directories to make always parent directories processed at first in
2626 * setup_exec_directory(), e.g., even if StateDirectory=foo/bar foo, we need to create foo at first,
2627 * then foo/bar. Also, set .only_create flag if one of the parent directories is contained in the
2628 * list. See also comments in setup_exec_directory() and issue #24783. */
2629
2630 if (d->n_items <= 1)
2631 return;
2632
2633 typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
2634
2635 for (size_t i = 1; i < d->n_items; i++)
2636 for (size_t j = 0; j < i; j++)
2637 if (path_startswith(d->items[i].path, d->items[j].path)) {
2638 d->items[i].only_create = true;
2639 break;
2640 }
211a3d87
LB
2641}
2642
4fb8f1e8
LP
2643ExecCleanMask exec_clean_mask_from_string(const char *s) {
2644 ExecDirectoryType t;
2645
2646 assert(s);
2647
2648 if (streq(s, "all"))
2649 return EXEC_CLEAN_ALL;
2650 if (streq(s, "fdstore"))
2651 return EXEC_CLEAN_FDSTORE;
2652
2653 t = exec_resource_type_from_string(s);
2654 if (t < 0)
2655 return (ExecCleanMask) t;
2656
2657 return 1U << t;
2658}
2659
80876c20
LP
2660static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
2661 [EXEC_INPUT_NULL] = "null",
2662 [EXEC_INPUT_TTY] = "tty",
2663 [EXEC_INPUT_TTY_FORCE] = "tty-force",
4f2d528d 2664 [EXEC_INPUT_TTY_FAIL] = "tty-fail",
52c239d7
LB
2665 [EXEC_INPUT_SOCKET] = "socket",
2666 [EXEC_INPUT_NAMED_FD] = "fd",
08f3be7a 2667 [EXEC_INPUT_DATA] = "data",
2038c3f5 2668 [EXEC_INPUT_FILE] = "file",
80876c20
LP
2669};
2670
8a0867d6
LP
2671DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
2672
94f04347 2673static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
80876c20 2674 [EXEC_OUTPUT_INHERIT] = "inherit",
94f04347 2675 [EXEC_OUTPUT_NULL] = "null",
80876c20 2676 [EXEC_OUTPUT_TTY] = "tty",
9a6bca7a 2677 [EXEC_OUTPUT_KMSG] = "kmsg",
28dbc1e8 2678 [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
706343f4
LP
2679 [EXEC_OUTPUT_JOURNAL] = "journal",
2680 [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
52c239d7
LB
2681 [EXEC_OUTPUT_SOCKET] = "socket",
2682 [EXEC_OUTPUT_NAMED_FD] = "fd",
2038c3f5 2683 [EXEC_OUTPUT_FILE] = "file",
566b7d23 2684 [EXEC_OUTPUT_FILE_APPEND] = "append",
8d7dab1f 2685 [EXEC_OUTPUT_FILE_TRUNCATE] = "truncate",
94f04347
LP
2686};
2687
2688DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
023a4f67
LP
2689
2690static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
2691 [EXEC_UTMP_INIT] = "init",
2692 [EXEC_UTMP_LOGIN] = "login",
2693 [EXEC_UTMP_USER] = "user",
2694};
2695
2696DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
53f47dfc
YW
2697
2698static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
2699 [EXEC_PRESERVE_NO] = "no",
2700 [EXEC_PRESERVE_YES] = "yes",
2701 [EXEC_PRESERVE_RESTART] = "restart",
2702};
2703
2704DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
3536f49e 2705
6b7b2ed9 2706/* This table maps ExecDirectoryType to the setting it is configured with in the unit */
72fd1768 2707static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3536f49e
YW
2708 [EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectory",
2709 [EXEC_DIRECTORY_STATE] = "StateDirectory",
2710 [EXEC_DIRECTORY_CACHE] = "CacheDirectory",
2711 [EXEC_DIRECTORY_LOGS] = "LogsDirectory",
2712 [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectory",
2713};
2714
2715DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
b1edf445 2716
211a3d87
LB
2717/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
2718static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2719 [EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectorySymlink",
2720 [EXEC_DIRECTORY_STATE] = "StateDirectorySymlink",
2721 [EXEC_DIRECTORY_CACHE] = "CacheDirectorySymlink",
2722 [EXEC_DIRECTORY_LOGS] = "LogsDirectorySymlink",
2723 [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
2724};
2725
2726DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
2727
ef44aa83
DDM
2728static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2729 [EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectoryMode",
2730 [EXEC_DIRECTORY_STATE] = "StateDirectoryMode",
2731 [EXEC_DIRECTORY_CACHE] = "CacheDirectoryMode",
2732 [EXEC_DIRECTORY_LOGS] = "LogsDirectoryMode",
2733 [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
2734};
2735
2736DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
2737
6b7b2ed9
LP
2738/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
2739 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
2740 * directories, specifically .timer units with their timestamp touch file. */
2741static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2742 [EXEC_DIRECTORY_RUNTIME] = "runtime",
2743 [EXEC_DIRECTORY_STATE] = "state",
2744 [EXEC_DIRECTORY_CACHE] = "cache",
2745 [EXEC_DIRECTORY_LOGS] = "logs",
2746 [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
2747};
2748
2749DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
2750
b1edf445
LP
2751static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
2752 [EXEC_KEYRING_INHERIT] = "inherit",
2753 [EXEC_KEYRING_PRIVATE] = "private",
2754 [EXEC_KEYRING_SHARED] = "shared",
2755};
2756
2757DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);