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