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