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