• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 13578340107

27 Feb 2025 09:12PM UTC coverage: 71.822% (+0.002%) from 71.82%
13578340107

push

github

web-flow
Issue OSC ANSI sequence whenever we change "context" of a TTY, i.e. acquire privs, enter container or VM or similar (#35224)

This is mostly a strawman to get a discussion going regarding how to
communicate to terminal emulators such as ptyxis about run0 (and nspawn,
and vmspawn, and moe) and what it does.

It's hierarchical and I think still relatively simple.

/cc @chergert

316 of 400 new or added lines in 18 files covered. (79.0%)

975 existing lines in 59 files now uncovered.

294628 of 410221 relevant lines covered (71.82%)

715614.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.59
/src/core/execute.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <fcntl.h>
5
#include <poll.h>
6
#include <sys/file.h>
7
#include <sys/mman.h>
8
#include <sys/personality.h>
9
#include <sys/prctl.h>
10
#include <sys/shm.h>
11
#include <sys/types.h>
12
#include <sys/un.h>
13
#include <unistd.h>
14
#include <utmpx.h>
15

16
#include <linux/fs.h> /* Must be included after <sys/mount.h> */
17

18
#include "sd-messages.h"
19

20
#include "af-list.h"
21
#include "alloc-util.h"
22
#include "async.h"
23
#include "bitfield.h"
24
#include "cap-list.h"
25
#include "capability-util.h"
26
#include "cgroup-setup.h"
27
#include "constants.h"
28
#include "cpu-set-util.h"
29
#include "env-file.h"
30
#include "env-util.h"
31
#include "errno-list.h"
32
#include "escape.h"
33
#include "exec-credential.h"
34
#include "execute.h"
35
#include "execute-serialize.h"
36
#include "exit-status.h"
37
#include "fd-util.h"
38
#include "fileio.h"
39
#include "format-util.h"
40
#include "glob-util.h"
41
#include "hexdecoct.h"
42
#include "io-util.h"
43
#include "ioprio-util.h"
44
#include "lock-util.h"
45
#include "log.h"
46
#include "macro.h"
47
#include "manager.h"
48
#include "manager-dump.h"
49
#include "memory-util.h"
50
#include "missing_fs.h"
51
#include "missing_prctl.h"
52
#include "mkdir-label.h"
53
#include "namespace.h"
54
#include "osc-context.h"
55
#include "parse-util.h"
56
#include "path-util.h"
57
#include "process-util.h"
58
#include "rlimit-util.h"
59
#include "rm-rf.h"
60
#include "seccomp-util.h"
61
#include "securebits-util.h"
62
#include "selinux-util.h"
63
#include "serialize.h"
64
#include "sort-util.h"
65
#include "special.h"
66
#include "stat-util.h"
67
#include "string-table.h"
68
#include "string-util.h"
69
#include "strv.h"
70
#include "syslog-util.h"
71
#include "terminal-util.h"
72
#include "tmpfile-util.h"
73
#include "umask-util.h"
74
#include "unit-serialize.h"
75
#include "user-util.h"
76
#include "utmp-wtmp.h"
77

78
static bool is_terminal_input(ExecInput i) {
43,359✔
79
        return IN_SET(i,
43,359✔
80
                      EXEC_INPUT_TTY,
81
                      EXEC_INPUT_TTY_FORCE,
82
                      EXEC_INPUT_TTY_FAIL);
83
}
84

85
static bool is_terminal_output(ExecOutput o) {
85,661✔
86
        return IN_SET(o,
85,661✔
87
                      EXEC_OUTPUT_TTY,
88
                      EXEC_OUTPUT_KMSG_AND_CONSOLE,
89
                      EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
90
}
91

92
const char* exec_context_tty_path(const ExecContext *context) {
17,686✔
93
        assert(context);
17,686✔
94

95
        if (context->stdio_as_fds)
17,686✔
96
                return NULL;
97

98
        if (context->tty_path)
17,103✔
99
                return context->tty_path;
692✔
100

101
        return "/dev/console";
102
}
103

104
int exec_context_apply_tty_size(
1,776✔
105
                const ExecContext *context,
106
                int input_fd,
107
                int output_fd,
108
                const char *tty_path) {
109

110
        unsigned rows, cols;
1,776✔
111
        int r;
1,776✔
112

113
        assert(context);
1,776✔
114
        assert(input_fd >= 0);
1,776✔
115
        assert(output_fd >= 0);
1,776✔
116

117
        if (!isatty_safe(output_fd))
1,776✔
118
                return 0;
1,776✔
119

120
        if (!tty_path)
1,091✔
121
                tty_path = exec_context_tty_path(context);
422✔
122

123
        /* Preferably use explicitly configured data */
124
        rows = context->tty_rows;
1,091✔
125
        cols = context->tty_cols;
1,091✔
126

127
        /* Fill in data from kernel command line if anything is unspecified */
128
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
1,091✔
129
                (void) proc_cmdline_tty_size(
1,059✔
130
                                tty_path,
131
                                rows == UINT_MAX ? &rows : NULL,
132
                                cols == UINT_MAX ? &cols : NULL);
133

134
        /* If we got nothing so far and we are talking to a physical device, then let's query dimensions from
135
         * the ANSI terminal driver. Note that we will not bother with this in case terminal reset via ansi
136
         * sequences is not enabled, as the DSR logic relies on ANSI sequences after all, and if we shall not
137
         * use those during initialization we need to skip it. */
138
        if (rows == UINT_MAX && cols == UINT_MAX &&
1,308✔
139
            exec_context_shall_ansi_seq_reset(context) &&
406✔
140
            isatty_safe(input_fd)) {
189✔
141
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
189✔
142
                if (r < 0)
189✔
143
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
189✔
144
        }
145

146
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
1,091✔
147
}
148

149
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p, sd_id128_t invocation_id) {
16,027✔
150
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
32,054✔
151
        int fd, r;
16,027✔
152

153
        assert(context);
16,027✔
154

155
        /* Note that this is potentially a "destructive" reset of a TTY device. It's about getting rid of the
156
         * remains of previous uses of the TTY. It's *not* about getting things set up for coming uses. We'll
157
         * potentially invalidate the TTY here through hangups or VT disallocations, and hence do not keep a
158
         * continuous fd open. */
159

160
        const char *path = exec_context_tty_path(context);
16,027✔
161

162
        if (p && p->stdout_fd >= 0 && isatty_safe(p->stdout_fd))
16,027✔
163
                fd = p->stdout_fd;
16✔
164
        else if (path && (context->tty_path || is_terminal_input(context->std_input) ||
16,011✔
165
                        is_terminal_output(context->std_output) || is_terminal_output(context->std_error))) {
15,112✔
166
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
669✔
167
                if (fd < 0)
669✔
168
                        return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path);
×
169
        } else
170
                return;   /* nothing to do */
171

172
        /* Take a synchronization lock for the duration of the setup that we do here.
173
         * systemd-vconsole-setup.service also takes the lock to avoid being interrupted. We open a new fd
174
         * that will be closed automatically, and operate on it for convenience. */
175
        lock_fd = lock_dev_console();
685✔
176
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
685✔
177
                log_debug_errno(lock_fd, "No privileges to lock /dev/console, proceeding without lock: %m");
×
178
        else if (ERRNO_IS_NEG_DEVICE_ABSENT(lock_fd))
685✔
179
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
180
        else if (lock_fd < 0)
685✔
181
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
182

183
        if (context->tty_reset)
685✔
184
                (void) terminal_reset_defensive(
179✔
185
                                fd,
186
                                TERMINAL_RESET_SWITCH_TO_TEXT |
187
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
176✔
188

189
        r = exec_context_apply_tty_size(context, fd, fd, path);
685✔
190
        if (r < 0)
685✔
191
                log_debug_errno(r, "Failed to configure TTY dimensions, ignoring: %m");
×
192

193
        if (!sd_id128_is_null(invocation_id)) {
1,321✔
194
                sd_id128_t context_id;
49✔
195

196
                r = osc_context_id_from_invocation_id(invocation_id, &context_id);
49✔
197
                if (r < 0)
49✔
198
                        log_debug_errno(r, "Failed to derive context ID from invocation ID, ignoring: %m");
49✔
199
                else {
200
                        _cleanup_free_ char *seq = NULL;
49✔
201

202
                        r = osc_context_close(context_id, &seq);
49✔
203
                        if (r < 0)
49✔
NEW
204
                                log_debug_errno(r, "Failed to acquire OSC close sequence, ignoring: %m");
×
205
                        else
206
                                (void) loop_write(fd, seq, SIZE_MAX);
49✔
207
                }
208
        }
209

210
        if (context->tty_vhangup)
685✔
211
                (void) terminal_vhangup_fd(fd);
171✔
212

213
        /* We don't need the fd anymore now, and it potentially points to a hungup TTY anyway, let's close it
214
         * hence. */
215
        _fd = safe_close(_fd);
685✔
216

217
        if (context->tty_vt_disallocate && path)
685✔
218
                (void) vt_disallocate(path);
94✔
219
}
220

221
bool exec_needs_network_namespace(const ExecContext *context) {
36,161✔
222
        assert(context);
36,161✔
223

224
        return context->private_network || context->network_namespace_path;
36,161✔
225
}
226

227
static bool exec_needs_ephemeral(const ExecContext *context) {
7,718✔
228
        return (context->root_image || context->root_directory) && context->root_ephemeral;
7,718✔
229
}
230

231
bool exec_needs_ipc_namespace(const ExecContext *context) {
32,364✔
232
        assert(context);
32,364✔
233

234
        return context->private_ipc || context->ipc_namespace_path;
32,364✔
235
}
236

237
static bool can_apply_cgroup_namespace(const ExecContext *context, const ExecParameters *params) {
38✔
238
        return cg_all_unified() > 0 && ns_type_supported(NAMESPACE_CGROUP);
38✔
239
}
240

241
static bool needs_cgroup_namespace(ProtectControlGroups i) {
65,272✔
242
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
65,272✔
243
}
244

245
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context, const ExecParameters *params) {
41,007✔
246
        assert(context);
41,007✔
247

248
        /* If cgroup namespace is configured via ProtectControlGroups=private or strict but we can't actually
249
         * use cgroup namespace, either from not having unified hierarchy or kernel support, we ignore the
250
         * setting and do not unshare the namespace. ProtectControlGroups=private and strict get downgraded
251
         * to no and yes respectively. This ensures that strict always gets a read-only mount of /sys/fs/cgroup.
252
         *
253
         * TODO: Remove fallback once cgroupv1 support is removed in v258. */
254
        if (needs_cgroup_namespace(context->protect_control_groups) && !can_apply_cgroup_namespace(context, params)) {
41,007✔
255
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_PRIVATE)
×
256
                        return PROTECT_CONTROL_GROUPS_NO;
257
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_STRICT)
×
258
                        return PROTECT_CONTROL_GROUPS_YES;
259
        }
260
        return context->protect_control_groups;
41,007✔
261
}
262

263
bool exec_needs_cgroup_namespace(const ExecContext *context, const ExecParameters *params) {
24,265✔
264
        assert(context);
24,265✔
265

266
        return needs_cgroup_namespace(exec_get_protect_control_groups(context, params));
24,265✔
267
}
268

269
bool exec_needs_cgroup_mount(const ExecContext *context, const ExecParameters *params) {
11,638✔
270
        assert(context);
11,638✔
271

272
        return exec_get_protect_control_groups(context, params) != PROTECT_CONTROL_GROUPS_NO;
11,638✔
273
}
274

275
bool exec_is_cgroup_mount_read_only(const ExecContext *context, const ExecParameters *params) {
2,552✔
276
        assert(context);
2,552✔
277

278
        return IN_SET(exec_get_protect_control_groups(context, params), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
2,552✔
279
}
280

281
bool exec_needs_pid_namespace(const ExecContext *context) {
54,067✔
282
        assert(context);
54,067✔
283

284
        return context->private_pids != PRIVATE_PIDS_NO && ns_type_supported(NAMESPACE_PID);
54,067✔
285
}
286

287
bool exec_needs_mount_namespace(
14,384✔
288
                const ExecContext *context,
289
                const ExecParameters *params,
290
                const ExecRuntime *runtime) {
291

292
        assert(context);
14,384✔
293

294
        if (context->root_image)
14,384✔
295
                return true;
296

297
        if (!strv_isempty(context->read_write_paths) ||
14,366✔
298
            !strv_isempty(context->read_only_paths) ||
13,340✔
299
            !strv_isempty(context->inaccessible_paths) ||
13,335✔
300
            !strv_isempty(context->exec_paths) ||
13,332✔
301
            !strv_isempty(context->no_exec_paths))
13,332✔
302
                return true;
303

304
        if (context->n_bind_mounts > 0)
13,332✔
305
                return true;
306

307
        if (context->n_temporary_filesystems > 0)
13,292✔
308
                return true;
309

310
        if (context->n_mount_images > 0)
13,239✔
311
                return true;
312

313
        if (context->n_extension_images > 0)
13,231✔
314
                return true;
315

316
        if (!strv_isempty(context->extension_directories))
13,227✔
317
                return true;
318

319
        if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
13,225✔
320
                return true;
321

322
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
13,225✔
323
                return true;
324

325
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
12,611✔
326
                return true;
327

328
        if (context->private_devices ||
12,394✔
329
            context->private_mounts > 0 ||
12,030✔
330
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
11,853✔
331
            context->protect_system != PROTECT_SYSTEM_NO ||
11,852✔
332
            context->protect_home != PROTECT_HOME_NO ||
333
            context->protect_kernel_tunables ||
334
            context->protect_kernel_modules ||
11,852✔
335
            context->protect_kernel_logs ||
11,020✔
336
            exec_needs_cgroup_mount(context, params) ||
11,020✔
337
            context->protect_proc != PROTECT_PROC_DEFAULT ||
11,014✔
338
            context->proc_subset != PROC_SUBSET_ALL ||
10,971✔
339
            exec_needs_ipc_namespace(context) ||
21,942✔
340
            exec_needs_pid_namespace(context))
10,971✔
341
                return true;
1,452✔
342

343
        if (context->root_directory) {
10,942✔
344
                if (exec_context_get_effective_mount_apivfs(context))
2✔
345
                        return true;
346

347
                for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
348
                        if (params && !params->prefix[t])
×
349
                                continue;
×
350

351
                        if (context->directories[t].n_items > 0)
×
352
                                return true;
353
                }
354
        }
355

356
        if (context->dynamic_user &&
10,940✔
357
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
358
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
359
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
360
                return true;
361

362
        if (exec_context_get_effective_bind_log_sockets(context))
10,940✔
363
                return true;
364

365
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
65,504✔
366
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
56,702✔
367
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
2,136✔
368
                                return true;
369

370
        return false;
371
}
372

373
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
5,010✔
374
        assert(context);
5,010✔
375
        assert(params);
5,010✔
376

377
        if (!params->notify_socket)
5,010✔
378
                return NULL;
379

380
        if (!needs_sandboxing)
4,379✔
381
                return NULL;
382

383
        if (!context->root_directory && !context->root_image)
4,379✔
384
                return NULL;
385

386
        if (!exec_context_get_effective_mount_apivfs(context))
×
387
                return NULL;
388

389
        if (!FLAGS_SET(params->flags, EXEC_APPLY_CHROOT))
×
390
                return NULL;
×
391

392
        return "/run/host/notify";
393
}
394

395
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
12,952✔
396
        assert(context);
12,952✔
397

398
        if (!context->dynamic_user)
12,952✔
399
                return false;
400

401
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
98✔
402
                return false;
403

404
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
92✔
405
                return false;
14✔
406

407
        return true;
408
}
409

410
int exec_params_get_cgroup_path(
17,993✔
411
                const ExecParameters *params,
412
                const CGroupContext *c,
413
                char **ret) {
414

415
        const char *subgroup = NULL;
17,993✔
416
        char *p;
17,993✔
417

418
        assert(params);
17,993✔
419
        assert(ret);
17,993✔
420

421
        if (!params->cgroup_path)
17,993✔
422
                return -EINVAL;
423

424
        /* If we are called for a unit where cgroup delegation is on, and the payload created its own populated
425
         * subcgroup (which we expect it to do, after all it asked for delegation), then we cannot place the control
426
         * processes started after the main unit's process in the unit's main cgroup because it is now an inner one,
427
         * and inner cgroups may not contain processes. Hence, if delegation is on, and this is a control process,
428
         * let's use ".control" as subcgroup instead. Note that we do so only for ExecStartPost=, ExecReload=,
429
         * ExecStop=, ExecStopPost=, i.e. for the commands where the main process is already forked. For ExecStartPre=
430
         * this is not necessary, the cgroup is still empty. We distinguish these cases with the EXEC_CONTROL_CGROUP
431
         * flag, which is only passed for the former statements, not for the latter. */
432

433
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
17,993✔
434
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
741✔
435
                        subgroup = ".control";
436
                else
437
                        subgroup = c->delegate_subgroup;
741✔
438
        }
439

440
        if (subgroup)
741✔
441
                p = path_join(params->cgroup_path, subgroup);
741✔
442
        else
443
                p = strdup(params->cgroup_path);
17,252✔
444
        if (!p)
17,993✔
445
                return -ENOMEM;
446

447
        *ret = p;
17,993✔
448
        return !!subgroup;
17,993✔
449
}
450

451
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
1,061✔
452
        assert(c);
1,061✔
453

454
        return c->cpu_affinity_from_numa;
1,061✔
455
}
456

457
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
3,823✔
458
        assert(unit);
3,823✔
459
        assert(msg);
3,823✔
460
        assert(executable);
3,823✔
461

462
        if (!DEBUG_LOGGING)
3,823✔
463
                return;
3,823✔
464

465
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
7,646✔
466

467
        log_unit_struct(unit, LOG_DEBUG,
3,823✔
468
                        "EXECUTABLE=%s", executable,
469
                        LOG_UNIT_MESSAGE(unit, "%s: %s", msg, strnull(cmdline)),
470
                        LOG_UNIT_INVOCATION_ID(unit));
471
}
472

473
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
474

475
int exec_spawn(
3,823✔
476
                Unit *unit,
477
                ExecCommand *command,
478
                const ExecContext *context,
479
                ExecParameters *params,
480
                ExecRuntime *runtime,
481
                const CGroupContext *cgroup_context,
482
                PidRef *ret) {
483

484
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL, *executor_path = NULL;
3,823✔
485
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
486
        _cleanup_fclose_ FILE *f = NULL;
3,823✔
487
        int r;
3,823✔
488

489
        assert(unit);
3,823✔
490
        assert(unit->manager);
3,823✔
491
        assert(unit->manager->executor_fd >= 0);
3,823✔
492
        assert(command);
3,823✔
493
        assert(context);
3,823✔
494
        assert(params);
3,823✔
495
        assert(!params->fds || FLAGS_SET(params->flags, EXEC_PASS_FDS));
3,823✔
496
        assert(params->fds || (params->n_socket_fds + params->n_storage_fds + params->n_extra_fds == 0));
3,823✔
497
        assert(!params->files_env); /* We fill this field, ensure it comes NULL-initialized to us */
3,823✔
498
        assert(ret);
3,823✔
499

500
        LOG_CONTEXT_PUSH_UNIT(unit);
7,646✔
501

502
        r = exec_context_load_environment(unit, context, &params->files_env);
3,823✔
503
        if (r < 0)
3,823✔
504
                return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
×
505

506
        /* We won't know the real executable path until we create the mount namespace in the child, but we
507
           want to log from the parent, so we use the possibly inaccurate path here. */
508
        log_command_line(unit, "About to execute", command->path, command->argv);
3,823✔
509

510
        if (params->cgroup_path) {
3,823✔
511
                r = exec_params_get_cgroup_path(params, cgroup_context, &subcgroup_path);
3,823✔
512
                if (r < 0)
3,823✔
513
                        return log_unit_error_errno(unit, r, "Failed to acquire subcgroup path: %m");
×
514
                if (r > 0) {
3,823✔
515
                        /* If there's a subcgroup, then let's create it here now (the main cgroup was already
516
                         * realized by the unit logic) */
517

518
                        r = cg_create(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path);
101✔
519
                        if (r < 0)
101✔
520
                                return log_unit_error_errno(unit, r, "Failed to create subcgroup '%s': %m", subcgroup_path);
×
521
                }
522
        }
523

524
        /* In order to avoid copy-on-write traps and OOM-kills when pid1's memory.current is above the
525
         * child's memory.max, serialize all the state needed to start the unit, and pass it to the
526
         * systemd-executor binary. clone() with CLONE_VM + CLONE_VFORK will pause the parent until the exec
527
         * and ensure all memory is shared. The child immediately execs the new binary so the delay should
528
         * be minimal. If glibc 2.39 is available pidfd_spawn() is used in order to get a race-free pid fd
529
         * and to clone directly into the target cgroup (if we booted with cgroupv2). */
530

531
        r = open_serialization_file("sd-executor-state", &f);
3,823✔
532
        if (r < 0)
3,823✔
533
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
534

535
        fdset = fdset_new();
3,823✔
536
        if (!fdset)
3,823✔
537
                return log_oom();
×
538

539
        r = exec_serialize_invocation(f, fdset, context, command, params, runtime, cgroup_context);
3,823✔
540
        if (r < 0)
3,823✔
541
                return log_unit_error_errno(unit, r, "Failed to serialize parameters: %m");
×
542

543
        r = finish_serialization_file(f);
3,823✔
544
        if (r < 0)
3,823✔
545
                return log_unit_error_errno(unit, r, "Failed to finish serialization stream: %m");
×
546

547
        r = fd_cloexec(fileno(f), false);
3,823✔
548
        if (r < 0)
3,823✔
549
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m");
×
550

551
        r = fdset_cloexec(fdset, false);
3,823✔
552
        if (r < 0)
3,823✔
553
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m");
×
554

555
        /* If LogLevelMax= is specified, then let's use the specified log level at the beginning of the
556
         * executor process. To achieve that the specified log level is passed as an argument, rather than
557
         * the one for the manager process. */
558
        r = log_max_levels_to_string(context->log_level_max >= 0 ? context->log_level_max : log_get_max_level(), &max_log_levels);
3,823✔
559
        if (r < 0)
3,823✔
560
                return log_unit_error_errno(unit, r, "Failed to convert max log levels to string: %m");
×
561

562
        r = fd_get_path(unit->manager->executor_fd, &executor_path);
3,823✔
563
        if (r < 0)
3,823✔
564
                return log_unit_error_errno(unit, r, "Failed to get executor path from fd: %m");
×
565

566
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
3,823✔
567
        xsprintf(serialization_fd_number, "%i", fileno(f));
3,823✔
568

569
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
3,823✔
570
        dual_timestamp start_timestamp;
3,823✔
571

572
        /* Restore the original ambient capability set the manager was started with to pass it to
573
         * sd-executor. */
574
        r = capability_ambient_set_apply(unit->manager->saved_ambient_set, /* also_inherit= */ false);
3,823✔
575
        if (r < 0)
3,823✔
576
                return log_unit_error_errno(unit, r, "Failed to apply the starting ambient set: %m");
×
577

578
        /* Record the start timestamp before we fork so that it is guaranteed to be earlier than the
579
         * handoff timestamp. */
580
        dual_timestamp_now(&start_timestamp);
3,823✔
581

582
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
583
        r = posix_spawn_wrapper(
7,646✔
584
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
3,823✔
585
                        STRV_MAKE(executor_path,
3,823✔
586
                                  "--deserialize", serialization_fd_number,
587
                                  "--log-level", max_log_levels,
588
                                  "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))),
589
                        environ,
590
                        cg_unified() > 0 ? subcgroup_path : NULL,
591
                        &pidref);
592

593
        /* Drop the ambient set again, so no processes other than sd-executore spawned from the manager inherit it. */
594
        (void) capability_ambient_set_apply(0, /* also_inherit= */ false);
3,823✔
595

596
        if (r == -EUCLEAN && subcgroup_path)
3,823✔
597
                return log_unit_error_errno(unit, r,
×
598
                                            "Failed to spawn process into cgroup '%s', because the cgroup "
599
                                            "or one of its parents or siblings is in the threaded mode.",
600
                                            subcgroup_path);
601
        if (r < 0)
3,823✔
602
                return log_unit_error_errno(unit, r, "Failed to spawn executor: %m");
×
603
        /* We add the new process to the cgroup both in the child (so that we can be sure that no user code is ever
604
         * executed outside of the cgroup) and in the parent (so that we can be sure that when we kill the cgroup the
605
         * process will be killed too). */
606
        if (r == 0 && subcgroup_path)
3,823✔
607
                (void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path, pidref.pid);
×
608
        /* r > 0: Already in the right cgroup thanks to CLONE_INTO_CGROUP */
609

610
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
7,646✔
611
                       command->path, pidref.pid, r > 0 ? "via" : "without");
612

613
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
3,823✔
614

615
        *ret = TAKE_PIDREF(pidref);
3,823✔
616
        return 0;
3,823✔
617
}
618

619
void exec_context_init(ExecContext *c) {
63,509✔
620
        assert(c);
63,509✔
621

622
        /* When initializing a bool member to 'true', make sure to serialize in execute-serialize.c using
623
         * serialize_bool() instead of serialize_bool_elide(). */
624

625
        *c = (ExecContext) {
63,509✔
626
                .umask = 0022,
627
                .ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO,
63,509✔
628
                .cpu_sched_policy = SCHED_OTHER,
629
                .syslog_priority = LOG_DAEMON|LOG_INFO,
630
                .syslog_level_prefix = true,
631
                .ignore_sigpipe = true,
632
                .timer_slack_nsec = NSEC_INFINITY,
633
                .personality = PERSONALITY_INVALID,
634
                .timeout_clean_usec = USEC_INFINITY,
635
                .capability_bounding_set = CAP_MASK_UNSET,
636
                .restrict_namespaces = NAMESPACE_FLAGS_INITIAL,
637
                .log_level_max = -1,
638
#if HAVE_SECCOMP
639
                .syscall_errno = SECCOMP_ERROR_NUMBER_KILL,
640
#endif
641
                .tty_rows = UINT_MAX,
642
                .tty_cols = UINT_MAX,
643
                .private_mounts = -1,
644
                .mount_apivfs = -1,
645
                .bind_log_sockets = -1,
646
                .memory_ksm = -1,
647
                .set_login_environment = -1,
648
        };
649

650
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
381,054✔
651
                d->mode = 0755;
317,545✔
652

653
        numa_policy_reset(&c->numa_policy);
63,509✔
654

655
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
63,509✔
656
}
63,509✔
657

658
void exec_context_done(ExecContext *c) {
50,020✔
659
        assert(c);
50,020✔
660

661
        c->environment = strv_free(c->environment);
50,020✔
662
        c->environment_files = strv_free(c->environment_files);
50,020✔
663
        c->pass_environment = strv_free(c->pass_environment);
50,020✔
664
        c->unset_environment = strv_free(c->unset_environment);
50,020✔
665

666
        rlimit_free_all(c->rlimit);
50,020✔
667

668
        for (size_t l = 0; l < 3; l++) {
200,080✔
669
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
150,060✔
670
                c->stdio_file[l] = mfree(c->stdio_file[l]);
150,060✔
671
        }
672

673
        c->working_directory = mfree(c->working_directory);
50,020✔
674
        c->root_directory = mfree(c->root_directory);
50,020✔
675
        c->root_image = mfree(c->root_image);
50,020✔
676
        c->root_image_options = mount_options_free_all(c->root_image_options);
50,020✔
677
        c->root_hash = mfree(c->root_hash);
50,020✔
678
        c->root_hash_size = 0;
50,020✔
679
        c->root_hash_path = mfree(c->root_hash_path);
50,020✔
680
        c->root_hash_sig = mfree(c->root_hash_sig);
50,020✔
681
        c->root_hash_sig_size = 0;
50,020✔
682
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
50,020✔
683
        c->root_verity = mfree(c->root_verity);
50,020✔
684
        c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
50,020✔
685
        c->extension_directories = strv_free(c->extension_directories);
50,020✔
686
        c->tty_path = mfree(c->tty_path);
50,020✔
687
        c->syslog_identifier = mfree(c->syslog_identifier);
50,020✔
688
        c->user = mfree(c->user);
50,020✔
689
        c->group = mfree(c->group);
50,020✔
690

691
        c->supplementary_groups = strv_free(c->supplementary_groups);
50,020✔
692

693
        c->pam_name = mfree(c->pam_name);
50,020✔
694

695
        c->read_only_paths = strv_free(c->read_only_paths);
50,020✔
696
        c->read_write_paths = strv_free(c->read_write_paths);
50,020✔
697
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
50,020✔
698
        c->exec_paths = strv_free(c->exec_paths);
50,020✔
699
        c->no_exec_paths = strv_free(c->no_exec_paths);
50,020✔
700
        c->exec_search_path = strv_free(c->exec_search_path);
50,020✔
701

702
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
50,020✔
703
        c->bind_mounts = NULL;
50,020✔
704
        c->n_bind_mounts = 0;
50,020✔
705
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
50,020✔
706
        c->temporary_filesystems = NULL;
50,020✔
707
        c->n_temporary_filesystems = 0;
50,020✔
708
        c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
50,020✔
709

710
        cpu_set_reset(&c->cpu_set);
50,020✔
711
        numa_policy_reset(&c->numa_policy);
50,020✔
712

713
        c->utmp_id = mfree(c->utmp_id);
50,020✔
714
        c->selinux_context = mfree(c->selinux_context);
50,020✔
715
        c->apparmor_profile = mfree(c->apparmor_profile);
50,020✔
716
        c->smack_process_label = mfree(c->smack_process_label);
50,020✔
717

718
        c->restrict_filesystems = set_free_free(c->restrict_filesystems);
50,020✔
719

720
        c->syscall_filter = hashmap_free(c->syscall_filter);
50,020✔
721
        c->syscall_archs = set_free(c->syscall_archs);
50,020✔
722
        c->syscall_log = hashmap_free(c->syscall_log);
50,020✔
723
        c->address_families = set_free(c->address_families);
50,020✔
724

725
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
300,120✔
726
                exec_directory_done(d);
250,100✔
727

728
        c->log_level_max = -1;
50,020✔
729

730
        exec_context_free_log_extra_fields(c);
50,020✔
731
        c->log_filter_allowed_patterns = set_free_free(c->log_filter_allowed_patterns);
50,020✔
732
        c->log_filter_denied_patterns = set_free_free(c->log_filter_denied_patterns);
50,020✔
733

734
        c->log_ratelimit = (RateLimit) {};
50,020✔
735

736
        c->stdin_data = mfree(c->stdin_data);
50,020✔
737
        c->stdin_data_size = 0;
50,020✔
738

739
        c->network_namespace_path = mfree(c->network_namespace_path);
50,020✔
740
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
50,020✔
741

742
        c->log_namespace = mfree(c->log_namespace);
50,020✔
743

744
        c->load_credentials = hashmap_free(c->load_credentials);
50,020✔
745
        c->set_credentials = hashmap_free(c->set_credentials);
50,020✔
746
        c->import_credentials = ordered_set_free(c->import_credentials);
50,020✔
747

748
        c->root_image_policy = image_policy_free(c->root_image_policy);
50,020✔
749
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
50,020✔
750
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
50,020✔
751

752
        c->private_hostname = mfree(c->private_hostname);
50,020✔
753
}
50,020✔
754

755
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
5,769✔
756
        assert(c);
5,769✔
757

758
        if (!runtime_prefix)
5,769✔
759
                return 0;
760

761
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
5,833✔
762
                _cleanup_free_ char *p = NULL;
64✔
763

764
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
64✔
765
                        p = path_join(runtime_prefix, "private", i->path);
×
766
                else
767
                        p = path_join(runtime_prefix, i->path);
64✔
768
                if (!p)
64✔
769
                        return -ENOMEM;
770

771
                /* We execute this synchronously, since we need to be sure this is gone when we start the
772
                 * service next. */
773
                (void) rm_rf(p, REMOVE_ROOT);
64✔
774

775
                STRV_FOREACH(symlink, i->symlinks) {
64✔
776
                        _cleanup_free_ char *symlink_abs = NULL;
×
777

778
                        if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
×
779
                                symlink_abs = path_join(runtime_prefix, "private", *symlink);
×
780
                        else
781
                                symlink_abs = path_join(runtime_prefix, *symlink);
×
782
                        if (!symlink_abs)
×
783
                                return -ENOMEM;
×
784

785
                        (void) unlink(symlink_abs);
×
786
                }
787
        }
788

789
        return 0;
790
}
791

792
int exec_context_destroy_mount_ns_dir(Unit *u) {
11,542✔
793
        _cleanup_free_ char *p = NULL;
11,542✔
794

795
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
11,542✔
796
                return 0;
797

798
        p = path_join("/run/systemd/propagate/", u->id);
5,038✔
799
        if (!p)
5,038✔
800
                return -ENOMEM;
801

802
        /* This is only filled transiently (see mount_in_namespace()), should be empty or even non-existent. */
803
        if (rmdir(p) < 0 && errno != ENOENT)
5,038✔
804
                log_unit_debug_errno(u, errno, "Unable to remove propagation dir '%s', ignoring: %m", p);
×
805

806
        return 0;
807
}
808

809
void exec_command_done(ExecCommand *c) {
101,748✔
810
        assert(c);
101,748✔
811

812
        c->path = mfree(c->path);
101,748✔
813
        c->argv = strv_free(c->argv);
101,748✔
814
}
101,748✔
815

816
void exec_command_done_array(ExecCommand *c, size_t n) {
26,938✔
817
        FOREACH_ARRAY(i, c, n)
107,751✔
818
                exec_command_done(i);
80,813✔
819
}
26,938✔
820

821
ExecCommand* exec_command_free(ExecCommand *c) {
20,907✔
822
        if (!c)
20,907✔
823
                return NULL;
824

825
        exec_command_done(c);
20,907✔
826
        return mfree(c);
20,907✔
827
}
828

829
ExecCommand* exec_command_free_list(ExecCommand *c) {
149,961✔
830
        ExecCommand *i;
149,961✔
831

832
        while ((i = LIST_POP(command, c)))
170,868✔
833
                exec_command_free(i);
20,907✔
834

835
        return NULL;
149,961✔
836
}
837

838
void exec_command_free_array(ExecCommand **c, size_t n) {
23,054✔
839
        FOREACH_ARRAY(i, c, n)
173,000✔
840
                *i = exec_command_free_list(*i);
149,946✔
841
}
23,054✔
842

843
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
5,480✔
844
        FOREACH_ARRAY(i, c, n)
21,919✔
845
                exec_status_reset(&i->exec_status);
16,439✔
846
}
5,480✔
847

848
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
6,003✔
849
        FOREACH_ARRAY(i, c, n)
42,186✔
850
                LIST_FOREACH(command, z, *i)
39,631✔
851
                        exec_status_reset(&z->exec_status);
3,448✔
852
}
6,003✔
853

854
typedef struct InvalidEnvInfo {
855
        const Unit *unit;
856
        const char *path;
857
} InvalidEnvInfo;
858

859
static void invalid_env(const char *p, void *userdata) {
×
860
        InvalidEnvInfo *info = userdata;
×
861

862
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
863
}
×
864

865
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
43,737✔
866
        assert(c);
43,737✔
867

868
        switch (fd_index) {
43,737✔
869

870
        case STDIN_FILENO:
14,579✔
871
                if (c->std_input != EXEC_INPUT_NAMED_FD)
14,579✔
872
                        return NULL;
873

874
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
875

876
        case STDOUT_FILENO:
14,579✔
877
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
14,579✔
878
                        return NULL;
879

880
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
881

882
        case STDERR_FILENO:
14,579✔
883
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
14,579✔
884
                        return NULL;
885

886
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
887

888
        default:
889
                return NULL;
890
        }
891
}
892

893
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
3,823✔
894
        _cleanup_strv_free_ char **v = NULL;
3,823✔
895
        int r;
3,823✔
896

897
        assert(c);
3,823✔
898
        assert(ret);
3,823✔
899

900
        STRV_FOREACH(i, c->environment_files) {
3,825✔
901
                _cleanup_globfree_ glob_t pglob = {};
2✔
902
                bool ignore = false;
2✔
903
                char *fn = *i;
2✔
904

905
                if (fn[0] == '-') {
2✔
906
                        ignore = true;
1✔
907
                        fn++;
1✔
908
                }
909

910
                if (!path_is_absolute(fn)) {
2✔
911
                        if (ignore)
×
912
                                continue;
×
913
                        return -EINVAL;
914
                }
915

916
                /* Filename supports globbing, take all matching files */
917
                r = safe_glob(fn, 0, &pglob);
2✔
918
                if (r < 0) {
2✔
919
                        if (ignore)
1✔
920
                                continue;
1✔
921
                        return r;
922
                }
923

924
                /* When we don't match anything, -ENOENT should be returned */
925
                assert(pglob.gl_pathc > 0);
1✔
926

927
                FOREACH_ARRAY(path, pglob.gl_pathv, pglob.gl_pathc) {
2✔
928
                        _cleanup_strv_free_ char **p = NULL;
1✔
929

930
                        r = load_env_file(NULL, *path, &p);
1✔
931
                        if (r < 0) {
1✔
932
                                if (ignore)
×
933
                                        continue;
×
934
                                return r;
935
                        }
936

937
                        /* Log invalid environment variables with filename */
938
                        if (p) {
1✔
939
                                InvalidEnvInfo info = {
1✔
940
                                        .unit = unit,
941
                                        .path = *path,
1✔
942
                                };
943

944
                                p = strv_env_clean_with_callback(p, invalid_env, &info);
1✔
945
                        }
946

947
                        if (!v)
1✔
948
                                v = TAKE_PTR(p);
1✔
949
                        else {
950
                                char **m = strv_env_merge(v, p);
×
951
                                if (!m)
×
952
                                        return -ENOMEM;
×
953

954
                                strv_free_and_replace(v, m);
×
955
                        }
956
                }
957
        }
958

959
        *ret = TAKE_PTR(v);
3,823✔
960

961
        return 0;
3,823✔
962
}
963

964
static bool tty_may_match_dev_console(const char *tty) {
344✔
965
        _cleanup_free_ char *resolved = NULL;
344✔
966

967
        if (!tty)
344✔
968
                return true;
969

970
        tty = skip_dev_prefix(tty);
344✔
971

972
        /* trivial identity? */
973
        if (streq(tty, "console"))
344✔
974
                return true;
975

976
        if (resolve_dev_console(&resolved) < 0)
24✔
977
                return true; /* if we could not resolve, assume it may */
978

979
        /* "tty0" means the active VC, so it may be the same sometimes */
980
        return path_equal(resolved, tty) || (streq(resolved, "tty0") && tty_is_vc(tty));
24✔
981
}
982

983
static bool exec_context_may_touch_tty(const ExecContext *ec) {
28,170✔
984
        assert(ec);
28,170✔
985

986
        return ec->tty_reset ||
28,170✔
987
                ec->tty_vhangup ||
28,170✔
988
                ec->tty_vt_disallocate ||
28,038✔
989
                is_terminal_input(ec->std_input) ||
28,038✔
990
                is_terminal_output(ec->std_output) ||
56,118✔
991
                is_terminal_output(ec->std_error);
27,777✔
992
}
993

994
bool exec_context_may_touch_console(const ExecContext *ec) {
25,660✔
995

996
        return exec_context_may_touch_tty(ec) &&
26,004✔
997
               tty_may_match_dev_console(exec_context_tty_path(ec));
344✔
998
}
999

1000
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,484✔
1001
        assert(c);
1,484✔
1002

1003
        /* Determines whether ANSI sequences shall be used during any terminal initialisation:
1004
         *
1005
         * 1. If the reset logic is enabled at all, this is an immediate no.
1006
         *
1007
         * 2. If $TERM is set to anything other than "dumb", it's a yes.
1008
         */
1009

1010
        if (!c->tty_reset)
1,484✔
1011
                return false;
1012

1013
        return !streq_ptr(strv_env_get(c->environment, "TERM"), "dumb");
533✔
1014
}
1015

1016
static void strv_fprintf(FILE *f, char **l) {
×
1017
        assert(f);
×
1018

1019
        STRV_FOREACH(g, l)
×
1020
                fprintf(f, " %s", *g);
×
1021
}
×
1022

1023
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
1,792✔
1024
        assert(f);
1,792✔
1025
        assert(prefix);
1,792✔
1026
        assert(name);
1,792✔
1027

1028
        if (!strv_isempty(strv)) {
1,792✔
1029
                fprintf(f, "%s%s:", prefix, name);
×
1030
                strv_fprintf(f, strv);
×
1031
                fputs("\n", f);
×
1032
        }
1033
}
1,792✔
1034

1035
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1036
        assert(p);
×
1037
        assert(f);
×
1038

1039
        prefix = strempty(prefix);
×
1040

1041
        fprintf(f,
×
1042
                "%sRuntimeScope: %s\n"
1043
                "%sExecFlags: %u\n"
1044
                "%sSELinuxContextNetwork: %s\n"
1045
                "%sCgroupSupportedMask: %u\n"
1046
                "%sCgroupPath: %s\n"
1047
                "%sCrededentialsDirectory: %s\n"
1048
                "%sEncryptedCredentialsDirectory: %s\n"
1049
                "%sConfirmSpawn: %s\n"
1050
                "%sShallConfirmSpawn: %s\n"
1051
                "%sWatchdogUSec: " USEC_FMT "\n"
1052
                "%sNotifySocket: %s\n"
1053
                "%sDebugInvocation: %s\n"
1054
                "%sFallbackSmackProcessLabel: %s\n",
1055
                prefix, runtime_scope_to_string(p->runtime_scope),
×
1056
                prefix, p->flags,
×
1057
                prefix, yes_no(p->selinux_context_net),
×
1058
                prefix, p->cgroup_supported,
×
1059
                prefix, p->cgroup_path,
×
1060
                prefix, strempty(p->received_credentials_directory),
×
1061
                prefix, strempty(p->received_encrypted_credentials_directory),
×
1062
                prefix, strempty(p->confirm_spawn),
×
1063
                prefix, yes_no(p->shall_confirm_spawn),
×
1064
                prefix, p->watchdog_usec,
×
1065
                prefix, strempty(p->notify_socket),
×
1066
                prefix, yes_no(p->debug_invocation),
×
1067
                prefix, strempty(p->fallback_smack_process_label));
×
1068

1069
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1070
        strv_dump(f, prefix, "Environment", p->environment);
×
1071
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1072

1073
        LIST_FOREACH(open_files, file, p->open_files)
×
1074
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1075

1076
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1077
}
×
1078

1079
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
224✔
1080
        int r;
224✔
1081

1082
        assert(c);
224✔
1083
        assert(f);
224✔
1084

1085
        prefix = strempty(prefix);
224✔
1086

1087
        fprintf(f,
224✔
1088
                "%sUMask: %04o\n"
1089
                "%sWorkingDirectory: %s\n"
1090
                "%sRootDirectory: %s\n"
1091
                "%sRootEphemeral: %s\n"
1092
                "%sNonBlocking: %s\n"
1093
                "%sPrivateTmp: %s\n"
1094
                "%sPrivateDevices: %s\n"
1095
                "%sProtectKernelTunables: %s\n"
1096
                "%sProtectKernelModules: %s\n"
1097
                "%sProtectKernelLogs: %s\n"
1098
                "%sProtectClock: %s\n"
1099
                "%sProtectControlGroups: %s\n"
1100
                "%sPrivateNetwork: %s\n"
1101
                "%sPrivateUsers: %s\n"
1102
                "%sPrivatePIDs: %s\n"
1103
                "%sProtectHome: %s\n"
1104
                "%sProtectSystem: %s\n"
1105
                "%sMountAPIVFS: %s\n"
1106
                "%sBindLogSockets: %s\n"
1107
                "%sIgnoreSIGPIPE: %s\n"
1108
                "%sMemoryDenyWriteExecute: %s\n"
1109
                "%sRestrictRealtime: %s\n"
1110
                "%sRestrictSUIDSGID: %s\n"
1111
                "%sKeyringMode: %s\n"
1112
                "%sProtectHostname: %s%s%s\n"
1113
                "%sProtectProc: %s\n"
1114
                "%sProcSubset: %s\n",
1115
                prefix, c->umask,
224✔
1116
                prefix, empty_to_root(c->working_directory),
224✔
1117
                prefix, empty_to_root(c->root_directory),
224✔
1118
                prefix, yes_no(c->root_ephemeral),
224✔
1119
                prefix, yes_no(c->non_blocking),
224✔
1120
                prefix, private_tmp_to_string(c->private_tmp),
224✔
1121
                prefix, yes_no(c->private_devices),
224✔
1122
                prefix, yes_no(c->protect_kernel_tunables),
224✔
1123
                prefix, yes_no(c->protect_kernel_modules),
224✔
1124
                prefix, yes_no(c->protect_kernel_logs),
224✔
1125
                prefix, yes_no(c->protect_clock),
224✔
1126
                prefix, protect_control_groups_to_string(c->protect_control_groups),
224✔
1127
                prefix, yes_no(c->private_network),
224✔
1128
                prefix, private_users_to_string(c->private_users),
224✔
1129
                prefix, private_pids_to_string(c->private_pids),
224✔
1130
                prefix, protect_home_to_string(c->protect_home),
224✔
1131
                prefix, protect_system_to_string(c->protect_system),
224✔
1132
                prefix, yes_no(exec_context_get_effective_mount_apivfs(c)),
224✔
1133
                prefix, yes_no(exec_context_get_effective_bind_log_sockets(c)),
224✔
1134
                prefix, yes_no(c->ignore_sigpipe),
224✔
1135
                prefix, yes_no(c->memory_deny_write_execute),
224✔
1136
                prefix, yes_no(c->restrict_realtime),
224✔
1137
                prefix, yes_no(c->restrict_suid_sgid),
224✔
1138
                prefix, exec_keyring_mode_to_string(c->keyring_mode),
224✔
1139
                prefix, protect_hostname_to_string(c->protect_hostname), c->private_hostname ? ":" : "", strempty(c->private_hostname),
448✔
1140
                prefix, protect_proc_to_string(c->protect_proc),
224✔
1141
                prefix, proc_subset_to_string(c->proc_subset));
224✔
1142

1143
        if (c->set_login_environment >= 0)
224✔
1144
                fprintf(f, "%sSetLoginEnvironment: %s\n", prefix, yes_no(c->set_login_environment > 0));
2✔
1145

1146
        if (c->root_image)
224✔
1147
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1148

1149
        if (c->root_image_options) {
224✔
1150
                fprintf(f, "%sRootImageOptions:", prefix);
×
1151
                LIST_FOREACH(mount_options, o, c->root_image_options)
×
1152
                        if (!isempty(o->options))
×
1153
                                fprintf(f, " %s:%s",
×
1154
                                        partition_designator_to_string(o->partition_designator),
1155
                                        o->options);
1156
                fprintf(f, "\n");
×
1157
        }
1158

1159
        if (c->root_hash) {
224✔
1160
                _cleanup_free_ char *encoded = NULL;
×
1161
                encoded = hexmem(c->root_hash, c->root_hash_size);
×
1162
                if (encoded)
×
1163
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1164
        }
1165

1166
        if (c->root_hash_path)
224✔
1167
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1168

1169
        if (c->root_hash_sig) {
224✔
1170
                _cleanup_free_ char *encoded = NULL;
×
1171
                ssize_t len;
×
1172
                len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded);
×
1173
                if (len)
×
1174
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1175
        }
1176

1177
        if (c->root_hash_sig_path)
224✔
1178
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1179

1180
        if (c->root_verity)
224✔
1181
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1182

1183
        STRV_FOREACH(e, c->environment)
224✔
1184
                fprintf(f, "%sEnvironment: %s\n", prefix, *e);
×
1185

1186
        STRV_FOREACH(e, c->environment_files)
224✔
1187
                fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
×
1188

1189
        STRV_FOREACH(e, c->pass_environment)
234✔
1190
                fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
10✔
1191

1192
        STRV_FOREACH(e, c->unset_environment)
224✔
1193
                fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
×
1194

1195
        fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
224✔
1196

1197
        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
1,344✔
1198
                fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode);
1,120✔
1199

1200
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
1,130✔
1201
                        fprintf(f,
10✔
1202
                                "%s%s: %s%s\n",
1203
                                prefix,
1204
                                exec_directory_type_to_string(dt),
1205
                                c->directories[dt].items[i].path,
1206
                                FLAGS_SET(c->directories[dt].items[i].flags, EXEC_DIRECTORY_READ_ONLY) ? " (ro)" : "");
10✔
1207

1208
                        STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
10✔
1209
                                fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
×
1210
                }
1211
        }
1212

1213
        fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC));
224✔
1214

1215
        if (c->memory_ksm >= 0)
224✔
1216
                fprintf(f, "%sMemoryKSM: %s\n", prefix, yes_no(c->memory_ksm > 0));
2✔
1217

1218
        if (c->nice_set)
224✔
1219
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
×
1220

1221
        if (c->oom_score_adjust_set)
224✔
1222
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
10✔
1223

1224
        if (c->coredump_filter_set)
224✔
1225
                fprintf(f, "%sCoredumpFilter: 0x%"PRIx64"\n", prefix, c->coredump_filter);
×
1226

1227
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
3,808✔
1228
                if (c->rlimit[i]) {
3,584✔
1229
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
20✔
1230
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1231
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
20✔
1232
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
20✔
1233
                }
1234

1235
        if (c->ioprio_set) {
224✔
1236
                _cleanup_free_ char *class_str = NULL;
×
1237

1238
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
×
1239
                if (r >= 0)
×
1240
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
×
1241

1242
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
×
1243
        }
1244

1245
        if (c->cpu_sched_set) {
224✔
1246
                _cleanup_free_ char *policy_str = NULL;
×
1247

1248
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1249
                if (r >= 0)
×
1250
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1251

1252
                fprintf(f,
×
1253
                        "%sCPUSchedulingPriority: %i\n"
1254
                        "%sCPUSchedulingResetOnFork: %s\n",
1255
                        prefix, c->cpu_sched_priority,
×
1256
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1257
        }
1258

1259
        if (c->cpu_set.set) {
224✔
1260
                _cleanup_free_ char *affinity = NULL;
×
1261

1262
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1263
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1264
        }
1265

1266
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
224✔
1267
                _cleanup_free_ char *nodes = NULL;
1✔
1268

1269
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1270
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1271
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1272
        }
1273

1274
        if (c->timer_slack_nsec != NSEC_INFINITY)
224✔
1275
                fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
1✔
1276

1277
        fprintf(f,
224✔
1278
                "%sStandardInput: %s\n"
1279
                "%sStandardOutput: %s\n"
1280
                "%sStandardError: %s\n",
1281
                prefix, exec_input_to_string(c->std_input),
224✔
1282
                prefix, exec_output_to_string(c->std_output),
224✔
1283
                prefix, exec_output_to_string(c->std_error));
224✔
1284

1285
        if (c->std_input == EXEC_INPUT_NAMED_FD)
224✔
1286
                fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
×
1287
        if (c->std_output == EXEC_OUTPUT_NAMED_FD)
224✔
1288
                fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
×
1289
        if (c->std_error == EXEC_OUTPUT_NAMED_FD)
224✔
1290
                fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
×
1291

1292
        if (c->std_input == EXEC_INPUT_FILE)
224✔
1293
                fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
×
1294
        if (c->std_output == EXEC_OUTPUT_FILE)
224✔
1295
                fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1296
        if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
224✔
1297
                fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1298
        if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1299
                fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1300
        if (c->std_error == EXEC_OUTPUT_FILE)
224✔
1301
                fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1302
        if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
224✔
1303
                fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1304
        if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1305
                fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1306

1307
        if (c->tty_path)
224✔
1308
                fprintf(f,
×
1309
                        "%sTTYPath: %s\n"
1310
                        "%sTTYReset: %s\n"
1311
                        "%sTTYVHangup: %s\n"
1312
                        "%sTTYVTDisallocate: %s\n"
1313
                        "%sTTYRows: %u\n"
1314
                        "%sTTYColumns: %u\n",
1315
                        prefix, c->tty_path,
1316
                        prefix, yes_no(c->tty_reset),
×
1317
                        prefix, yes_no(c->tty_vhangup),
×
1318
                        prefix, yes_no(c->tty_vt_disallocate),
×
1319
                        prefix, c->tty_rows,
×
1320
                        prefix, c->tty_cols);
×
1321

1322
        if (IN_SET(c->std_output,
224✔
1323
                   EXEC_OUTPUT_KMSG,
1324
                   EXEC_OUTPUT_JOURNAL,
1325
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1326
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1327
            IN_SET(c->std_error,
17✔
1328
                   EXEC_OUTPUT_KMSG,
1329
                   EXEC_OUTPUT_JOURNAL,
1330
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1331
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1332

1333
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
207✔
1334

1335
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
207✔
1336
                if (r >= 0)
207✔
1337
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
207✔
1338

1339
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
207✔
1340
                if (r >= 0)
207✔
1341
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
207✔
1342
        }
1343

1344
        if (c->log_level_max >= 0) {
224✔
1345
                _cleanup_free_ char *t = NULL;
1✔
1346

1347
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1348

1349
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1350
        }
1351

1352
        if (c->log_ratelimit.interval > 0)
224✔
1353
                fprintf(f,
×
1354
                        "%sLogRateLimitIntervalSec: %s\n",
1355
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1356

1357
        if (c->log_ratelimit.burst > 0)
224✔
1358
                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit.burst);
×
1359

1360
        if (!set_isempty(c->log_filter_allowed_patterns) || !set_isempty(c->log_filter_denied_patterns)) {
224✔
1361
                fprintf(f, "%sLogFilterPatterns:", prefix);
×
1362

1363
                char *pattern;
×
1364
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1365
                        fprintf(f, " %s", pattern);
×
1366
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1367
                        fprintf(f, " ~%s", pattern);
×
1368
                fputc('\n', f);
×
1369
        }
1370

1371
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
228✔
1372
                fprintf(f, "%sLogExtraFields: ", prefix);
4✔
1373
                fwrite(field->iov_base, 1, field->iov_len, f);
4✔
1374
                fputc('\n', f);
4✔
1375
        }
1376

1377
        if (c->log_namespace)
224✔
1378
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1379

1380
        if (c->secure_bits) {
224✔
1381
                _cleanup_free_ char *str = NULL;
×
1382

1383
                r = secure_bits_to_string_alloc(c->secure_bits, &str);
×
1384
                if (r >= 0)
×
1385
                        fprintf(f, "%sSecure Bits: %s\n", prefix, str);
×
1386
        }
1387

1388
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
224✔
1389
                _cleanup_free_ char *str = NULL;
16✔
1390

1391
                r = capability_set_to_string(c->capability_bounding_set, &str);
16✔
1392
                if (r >= 0)
16✔
1393
                        fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
16✔
1394
        }
1395

1396
        if (c->capability_ambient_set != 0) {
224✔
1397
                _cleanup_free_ char *str = NULL;
×
1398

1399
                r = capability_set_to_string(c->capability_ambient_set, &str);
×
1400
                if (r >= 0)
×
1401
                        fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
×
1402
        }
1403

1404
        if (c->user)
224✔
1405
                fprintf(f, "%sUser: %s\n", prefix, c->user);
1✔
1406
        if (c->group)
224✔
1407
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1408

1409
        fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
447✔
1410

1411
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
224✔
1412

1413
        if (c->pam_name)
224✔
1414
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
×
1415

1416
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
224✔
1417
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
224✔
1418
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
224✔
1419
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
224✔
1420
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
224✔
1421
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
224✔
1422

1423
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
228✔
1424
                fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
4✔
1425
                        mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
4✔
1426
                        mount->ignore_enoent ? "-": "",
4✔
1427
                        mount->source,
1428
                        mount->destination,
1429
                        mount->recursive ? "rbind" : "norbind");
4✔
1430

1431
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
224✔
1432
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1433
                        tmpfs->path,
1434
                        isempty(tmpfs->options) ? "" : ":",
×
1435
                        strempty(tmpfs->options));
×
1436

1437
        if (c->utmp_id)
224✔
1438
                fprintf(f,
×
1439
                        "%sUtmpIdentifier: %s\n",
1440
                        prefix, c->utmp_id);
1441

1442
        if (c->selinux_context)
224✔
1443
                fprintf(f,
×
1444
                        "%sSELinuxContext: %s%s\n",
1445
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1446

1447
        if (c->apparmor_profile)
224✔
1448
                fprintf(f,
×
1449
                        "%sAppArmorProfile: %s%s\n",
1450
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1451

1452
        if (c->smack_process_label)
224✔
1453
                fprintf(f,
×
1454
                        "%sSmackProcessLabel: %s%s\n",
1455
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1456

1457
        if (c->personality != PERSONALITY_INVALID)
224✔
1458
                fprintf(f,
1✔
1459
                        "%sPersonality: %s\n",
1460
                        prefix, strna(personality_to_string(c->personality)));
1461

1462
        fprintf(f,
224✔
1463
                "%sLockPersonality: %s\n",
1464
                prefix, yes_no(c->lock_personality));
224✔
1465

1466
        if (c->syscall_filter) {
224✔
1467
                fprintf(f,
11✔
1468
                        "%sSystemCallFilter: ",
1469
                        prefix);
1470

1471
                if (!c->syscall_allow_list)
11✔
1472
                        fputc('~', f);
×
1473

1474
#if HAVE_SECCOMP
1475
                void *id, *val;
11✔
1476
                bool first = true;
11✔
1477
                HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
4,169✔
1478
                        _cleanup_free_ char *name = NULL;
4,158✔
1479
                        const char *errno_name = NULL;
4,158✔
1480
                        int num = PTR_TO_INT(val);
4,158✔
1481

1482
                        if (first)
4,158✔
1483
                                first = false;
1484
                        else
1485
                                fputc(' ', f);
4,147✔
1486

1487
                        name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
4,158✔
1488
                        fputs(strna(name), f);
4,158✔
1489

1490
                        if (num >= 0) {
4,158✔
1491
                                errno_name = seccomp_errno_or_action_to_string(num);
×
1492
                                if (errno_name)
×
1493
                                        fprintf(f, ":%s", errno_name);
×
1494
                                else
1495
                                        fprintf(f, ":%d", num);
×
1496
                        }
1497
                }
1498
#endif
1499

1500
                fputc('\n', f);
11✔
1501
        }
1502

1503
        if (c->syscall_archs) {
224✔
1504
                fprintf(f,
11✔
1505
                        "%sSystemCallArchitectures:",
1506
                        prefix);
1507

1508
#if HAVE_SECCOMP
1509
                void *id;
11✔
1510
                SET_FOREACH(id, c->syscall_archs)
22✔
1511
                        fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
11✔
1512
#endif
1513
                fputc('\n', f);
11✔
1514
        }
1515

1516
        if (exec_context_restrict_namespaces_set(c)) {
224✔
1517
                _cleanup_free_ char *s = NULL;
12✔
1518

1519
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
12✔
1520
                if (r >= 0)
12✔
1521
                        fprintf(f, "%sRestrictNamespaces: %s\n",
24✔
1522
                                prefix, strna(s));
1523
        }
1524

1525
#if HAVE_LIBBPF
1526
        if (exec_context_restrict_filesystems_set(c)) {
224✔
1527
                char *fs;
×
1528
                SET_FOREACH(fs, c->restrict_filesystems)
×
1529
                        fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
×
1530
        }
1531
#endif
1532

1533
        if (c->network_namespace_path)
224✔
1534
                fprintf(f,
×
1535
                        "%sNetworkNamespacePath: %s\n",
1536
                        prefix, c->network_namespace_path);
1537

1538
        if (c->syscall_errno > 0) {
224✔
1539
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
223✔
1540

1541
#if HAVE_SECCOMP
1542
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
223✔
1543
                if (errno_name)
223✔
1544
                        fputs(errno_name, f);
223✔
1545
                else
1546
                        fprintf(f, "%d", c->syscall_errno);
×
1547
#endif
1548
                fputc('\n', f);
223✔
1549
        }
1550

1551
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
224✔
1552
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1553
                        mount->ignore_enoent ? "-": "",
×
1554
                        mount->source,
1555
                        mount->destination);
1556
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1557
                        fprintf(f, ":%s:%s",
×
1558
                                partition_designator_to_string(o->partition_designator),
1559
                                strempty(o->options));
×
1560
                fprintf(f, "\n");
×
1561
        }
1562

1563
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
224✔
1564
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1565
                        mount->ignore_enoent ? "-": "",
×
1566
                        mount->source);
1567
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1568
                        fprintf(f, ":%s:%s",
×
1569
                                partition_designator_to_string(o->partition_designator),
1570
                                strempty(o->options));
×
1571
                fprintf(f, "\n");
×
1572
        }
1573

1574
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
224✔
1575
}
224✔
1576

1577
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1578
        assert(c);
×
1579

1580
        /* Returns true if the process forked off would run under
1581
         * an unchanged UID or as root. */
1582

1583
        if (!c->user)
×
1584
                return true;
1585

1586
        if (STR_IN_SET(c->user, "root", "0"))
×
1587
                return true;
×
1588

1589
        return false;
×
1590
}
1591

1592
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,122✔
1593
        int p;
2,122✔
1594

1595
        assert(c);
2,122✔
1596

1597
        if (c->ioprio_set)
2,122✔
1598
                return c->ioprio;
18✔
1599

1600
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,104✔
1601
        if (p < 0)
2,104✔
1602
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1603

1604
        return ioprio_normalize(p);
2,104✔
1605
}
1606

1607
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
21,162✔
1608
        assert(c);
21,162✔
1609

1610
        /* Explicit setting wins */
1611
        if (c->mount_apivfs >= 0)
21,162✔
1612
                return c->mount_apivfs > 0;
110✔
1613

1614
        /* Default to "yes" if root directory or image are specified */
1615
        if (exec_context_with_rootfs(c))
21,052✔
1616
                return true;
47✔
1617

1618
        return false;
1619
}
1620

1621
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
14,777✔
1622
        assert(c);
14,777✔
1623

1624
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1625
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1626
        if (c->log_namespace)
14,777✔
1627
                return true;
1628

1629
        if (c->bind_log_sockets >= 0)
14,773✔
1630
                return c->bind_log_sockets > 0;
2✔
1631

1632
        if (exec_context_get_effective_mount_apivfs(c))
14,771✔
1633
                return true;
1634

1635
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1636
        if (exec_context_with_rootfs(c) && c->private_devices)
14,712✔
1637
                return true;
×
1638

1639
        return false;
1640
}
1641

1642
void exec_context_free_log_extra_fields(ExecContext *c) {
50,022✔
1643
        assert(c);
50,022✔
1644

1645
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
50,027✔
1646
                free(field->iov_base);
5✔
1647

1648
        c->log_extra_fields = mfree(c->log_extra_fields);
50,022✔
1649
        c->n_log_extra_fields = 0;
50,022✔
1650
}
50,022✔
1651

1652
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
2,510✔
1653
        _cleanup_close_ int fd = -EBADF;
2,510✔
1654
        const char *path;
2,510✔
1655
        struct stat st;
2,510✔
1656
        int r;
2,510✔
1657

1658
        assert(c);
2,510✔
1659

1660
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1661
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
2,510✔
1662

1663
        /* And then undo what chown_terminal() did earlier. Note that we only do this if we have a path
1664
         * configured. If the TTY was passed to us as file descriptor we assume the TTY is opened and managed
1665
         * by whoever passed it to us and thus knows better when and how to chmod()/chown() it back. */
1666
        if (!exec_context_may_touch_tty(c))
2,510✔
1667
                return;
1668

1669
        path = exec_context_tty_path(c);
49✔
1670
        if (!path)
49✔
1671
                return;
1672

1673
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
49✔
1674
        if (fd < 0)
49✔
1675
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1676
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1677
                                             path);
1678

1679
        if (fstat(fd, &st) < 0)
49✔
1680
                return (void) log_warning_errno(errno, "Failed to stat TTY '%s', ignoring: %m", path);
×
1681

1682
        /* Let's add a superficial check that we only do this for stuff that looks like a TTY. We only check
1683
         * if things are a character device, since a proper check either means we'd have to open the TTY and
1684
         * use isatty(), but we'd rather not do that since opening TTYs comes with all kinds of side-effects
1685
         * and is slow. Or we'd have to hardcode dev_t major information, which we'd rather avoid. Why bother
1686
         * with this at all? → https://github.com/systemd/systemd/issues/19213 */
1687
        if (!S_ISCHR(st.st_mode))
49✔
1688
                return log_warning("Configured TTY '%s' is not actually a character device, ignoring.", path);
×
1689

1690
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
49✔
1691
        if (r < 0)
49✔
1692
                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);
49✔
1693
}
1694

1695
int exec_context_get_clean_directories(
×
1696
                ExecContext *c,
1697
                char **prefix,
1698
                ExecCleanMask mask,
1699
                char ***ret) {
1700

1701
        _cleanup_strv_free_ char **l = NULL;
×
1702
        int r;
×
1703

1704
        assert(c);
×
1705
        assert(prefix);
×
1706
        assert(ret);
×
1707

1708
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
1709
                if (!BIT_SET(mask, t))
×
1710
                        continue;
×
1711

1712
                if (!prefix[t])
×
1713
                        continue;
×
1714

1715
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
×
1716
                        char *j;
×
1717

1718
                        j = path_join(prefix[t], i->path);
×
1719
                        if (!j)
×
1720
                                return -ENOMEM;
1721

1722
                        r = strv_consume(&l, j);
×
1723
                        if (r < 0)
×
1724
                                return r;
1725

1726
                        /* Also remove private directories unconditionally. */
1727
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
×
1728
                                j = path_join(prefix[t], "private", i->path);
×
1729
                                if (!j)
×
1730
                                        return -ENOMEM;
1731

1732
                                r = strv_consume(&l, j);
×
1733
                                if (r < 0)
×
1734
                                        return r;
1735
                        }
1736

1737
                        STRV_FOREACH(symlink, i->symlinks) {
×
1738
                                j = path_join(prefix[t], *symlink);
×
1739
                                if (!j)
×
1740
                                        return -ENOMEM;
1741

1742
                                r = strv_consume(&l, j);
×
1743
                                if (r < 0)
×
1744
                                        return r;
1745
                        }
1746
                }
1747
        }
1748

1749
        *ret = TAKE_PTR(l);
×
1750
        return 0;
×
1751
}
1752

1753
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,041✔
1754
        ExecCleanMask mask = 0;
1,041✔
1755

1756
        assert(c);
1,041✔
1757
        assert(ret);
1,041✔
1758

1759
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
6,246✔
1760
                if (c->directories[t].n_items > 0)
5,205✔
1761
                        mask |= 1U << t;
241✔
1762

1763
        *ret = mask;
1,041✔
1764
        return 0;
1,041✔
1765
}
1766

1767
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,061✔
1768
        int n = 0, r;
1,061✔
1769

1770
        assert(c);
1,061✔
1771

1772
        if (c->oom_score_adjust_set)
1,061✔
1773
                return c->oom_score_adjust;
307✔
1774

1775
        r = get_oom_score_adjust(&n);
754✔
1776
        if (r < 0)
754✔
1777
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1778

1779
        return n;
754✔
1780
}
1781

1782
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1,061✔
1783
        _cleanup_free_ char *t = NULL;
1,061✔
1784
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1,061✔
1785
        int r;
1,061✔
1786

1787
        assert(c);
1,061✔
1788

1789
        if (c->coredump_filter_set)
1,061✔
1790
                return c->coredump_filter;
×
1791

1792
        r = read_one_line_file("/proc/self/coredump_filter", &t);
1,061✔
1793
        if (r < 0)
1,061✔
1794
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1795
        else {
1796
                r = safe_atoux64(t, &n);
1,061✔
1797
                if (r < 0)
1,061✔
1798
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1799
        }
1800

1801
        return n;
1,061✔
1802
}
1803

1804
int exec_context_get_nice(const ExecContext *c) {
1,061✔
1805
        int n;
1,061✔
1806

1807
        assert(c);
1,061✔
1808

1809
        if (c->nice_set)
1,061✔
1810
                return c->nice;
6✔
1811

1812
        errno = 0;
1,055✔
1813
        n = getpriority(PRIO_PROCESS, 0);
1,055✔
1814
        if (errno > 0) {
1,055✔
1815
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1816
                n = 0;
1817
        }
1818

1819
        return n;
1820
}
1821

1822
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,061✔
1823
        int n;
1,061✔
1824

1825
        assert(c);
1,061✔
1826

1827
        if (c->cpu_sched_set)
1,061✔
1828
                return c->cpu_sched_policy;
×
1829

1830
        n = sched_getscheduler(0);
1,061✔
1831
        if (n < 0)
1,061✔
1832
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1833

1834
        return n < 0 ? SCHED_OTHER : n;
1,061✔
1835
}
1836

1837
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,061✔
1838
        struct sched_param p = {};
1,061✔
1839
        int r;
1,061✔
1840

1841
        assert(c);
1,061✔
1842

1843
        if (c->cpu_sched_set)
1,061✔
1844
                return c->cpu_sched_priority;
×
1845

1846
        r = sched_getparam(0, &p);
1,061✔
1847
        if (r < 0)
1,061✔
1848
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1849

1850
        return r >= 0 ? p.sched_priority : 0;
1,061✔
1851
}
1852

1853
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,061✔
1854
        int r;
1,061✔
1855

1856
        assert(c);
1,061✔
1857

1858
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,061✔
1859
                return c->timer_slack_nsec;
1860

1861
        r = prctl(PR_GET_TIMERSLACK);
1,061✔
1862
        if (r < 0)
1,061✔
1863
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1864

1865
        return (uint64_t) MAX(r, 0);
1,061✔
1866
}
1867

1868
bool exec_context_get_set_login_environment(const ExecContext *c) {
12,169✔
1869
        assert(c);
12,169✔
1870

1871
        if (c->set_login_environment >= 0)
12,169✔
1872
                return c->set_login_environment;
×
1873

1874
        return c->user || c->dynamic_user || c->pam_name;
22,235✔
1875
}
1876

1877
char** exec_context_get_syscall_filter(const ExecContext *c) {
1,061✔
1878
        _cleanup_strv_free_ char **l = NULL;
1,061✔
1879

1880
        assert(c);
1,061✔
1881

1882
#if HAVE_SECCOMP
1883
        void *id, *val;
1,061✔
1884
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
18,577✔
1885
                _cleanup_free_ char *name = NULL;
17,516✔
1886
                const char *e = NULL;
17,516✔
1887
                char *s;
17,516✔
1888
                int num = PTR_TO_INT(val);
17,516✔
1889

1890
                if (c->syscall_allow_list && num >= 0)
17,516✔
1891
                        /* syscall with num >= 0 in allow-list is denied. */
1892
                        continue;
×
1893

1894
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
17,516✔
1895
                if (!name)
17,516✔
1896
                        continue;
×
1897

1898
                if (num >= 0) {
17,516✔
1899
                        e = seccomp_errno_or_action_to_string(num);
×
1900
                        if (e) {
×
1901
                                s = strjoin(name, ":", e);
×
1902
                                if (!s)
×
1903
                                        return NULL;
1904
                        } else {
1905
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1906
                                        return NULL;
1907
                        }
1908
                } else
1909
                        s = TAKE_PTR(name);
17,516✔
1910

1911
                if (strv_consume(&l, s) < 0)
17,516✔
1912
                        return NULL;
1913
        }
1914

1915
        strv_sort(l);
1,061✔
1916
#endif
1917

1918
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,061✔
1919
}
1920

1921
char** exec_context_get_syscall_archs(const ExecContext *c) {
1,061✔
1922
        _cleanup_strv_free_ char **l = NULL;
1,061✔
1923

1924
        assert(c);
1,061✔
1925

1926
#if HAVE_SECCOMP
1927
        void *id;
1,061✔
1928
        SET_FOREACH(id, c->syscall_archs) {
1,113✔
1929
                const char *name;
52✔
1930

1931
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
52✔
1932
                if (!name)
52✔
1933
                        continue;
×
1934

1935
                if (strv_extend(&l, name) < 0)
52✔
1936
                        return NULL;
×
1937
        }
1938

1939
        strv_sort(l);
1,061✔
1940
#endif
1941

1942
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,061✔
1943
}
1944

1945
char** exec_context_get_syscall_log(const ExecContext *c) {
1,061✔
1946
        _cleanup_strv_free_ char **l = NULL;
1,061✔
1947

1948
        assert(c);
1,061✔
1949

1950
#if HAVE_SECCOMP
1951
        void *id, *val;
1,061✔
1952
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,061✔
1953
                char *name = NULL;
×
1954

1955
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1956
                if (!name)
×
1957
                        continue;
×
1958

1959
                if (strv_consume(&l, name) < 0)
×
1960
                        return NULL;
×
1961
        }
1962

1963
        strv_sort(l);
1,061✔
1964
#endif
1965

1966
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,061✔
1967
}
1968

1969
char** exec_context_get_address_families(const ExecContext *c) {
1,061✔
1970
        _cleanup_strv_free_ char **l = NULL;
1,061✔
1971
        void *af;
1,061✔
1972

1973
        assert(c);
1,061✔
1974

1975
        SET_FOREACH(af, c->address_families) {
1,215✔
1976
                const char *name;
154✔
1977

1978
                name = af_to_name(PTR_TO_INT(af));
154✔
1979
                if (!name)
154✔
1980
                        continue;
×
1981

1982
                if (strv_extend(&l, name) < 0)
154✔
1983
                        return NULL;
×
1984
        }
1985

1986
        strv_sort(l);
1,061✔
1987

1988
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,061✔
1989
}
1990

1991
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1,061✔
1992
        _cleanup_strv_free_ char **l = NULL;
1,061✔
1993

1994
        assert(c);
1,061✔
1995

1996
#if HAVE_LIBBPF
1997
        l = set_get_strv(c->restrict_filesystems);
1,061✔
1998
        if (!l)
1,061✔
1999
                return NULL;
2000

2001
        strv_sort(l);
1,061✔
2002
#endif
2003

2004
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,061✔
2005
}
2006

2007
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
7,358✔
2008
        assert(s);
7,358✔
2009

2010
        *s = (ExecStatus) {
7,358✔
2011
                .pid = pid,
2012
        };
2013

2014
        if (ts)
7,358✔
2015
                s->start_timestamp = *ts;
7,358✔
2016
        else
2017
                dual_timestamp_now(&s->start_timestamp);
×
2018
}
7,358✔
2019

2020
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
3,560✔
2021
        assert(s);
3,560✔
2022

2023
        if (s->pid != pid)
3,560✔
2024
                *s = (ExecStatus) {
3✔
2025
                        .pid = pid,
2026
                };
2027

2028
        dual_timestamp_now(&s->exit_timestamp);
3,560✔
2029

2030
        s->code = code;
3,560✔
2031
        s->status = status;
3,560✔
2032

2033
        if (context && context->utmp_id)
3,560✔
2034
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2035
}
3,560✔
2036

2037
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
13,394✔
2038
        assert(s);
13,394✔
2039
        assert(ucred);
13,394✔
2040
        assert(ts);
13,394✔
2041

2042
        if (ucred->pid != s->pid)
13,394✔
2043
                *s = (ExecStatus) {
6✔
2044
                        .pid = ucred->pid,
2045
                };
2046

2047
        s->handoff_timestamp = *ts;
13,394✔
2048
}
13,394✔
2049

2050
void exec_status_reset(ExecStatus *s) {
22,971✔
2051
        assert(s);
22,971✔
2052

2053
        *s = (ExecStatus) {};
22,971✔
2054
}
22,971✔
2055

2056
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
94✔
2057
        assert(s);
94✔
2058
        assert(f);
94✔
2059

2060
        if (s->pid <= 0)
94✔
2061
                return;
2062

2063
        prefix = strempty(prefix);
10✔
2064

2065
        fprintf(f,
10✔
2066
                "%sPID: "PID_FMT"\n",
2067
                prefix, s->pid);
2068

2069
        if (dual_timestamp_is_set(&s->start_timestamp))
10✔
2070
                fprintf(f,
10✔
2071
                        "%sStart Timestamp: %s\n",
2072
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
10✔
2073

2074
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
10✔
2075
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
10✔
2076
                fprintf(f,
10✔
2077
                        "%sHandoff Timestamp: %s since start\n",
2078
                        prefix,
2079
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
20✔
2080
        else
2081
                fprintf(f,
×
2082
                        "%sHandoff Timestamp: %s\n",
2083
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
×
2084

2085
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2086

2087
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
×
2088
                        fprintf(f,
×
2089
                                "%sExit Timestamp: %s since handoff\n",
2090
                                prefix,
2091
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
×
2092
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2093
                        fprintf(f,
×
2094
                                "%sExit Timestamp: %s since start\n",
2095
                                prefix,
2096
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2097
                else
2098
                        fprintf(f,
×
2099
                                "%sExit Timestamp: %s\n",
2100
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2101

2102
                fprintf(f,
×
2103
                        "%sExit Code: %s\n"
2104
                        "%sExit Status: %i\n",
2105
                        prefix, sigchld_code_to_string(s->code),
×
2106
                        prefix, s->status);
×
2107
        }
2108
}
2109

2110
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
94✔
2111
        _cleanup_free_ char *cmd = NULL;
188✔
2112
        const char *prefix2;
94✔
2113

2114
        assert(c);
94✔
2115
        assert(f);
94✔
2116

2117
        prefix = strempty(prefix);
94✔
2118
        prefix2 = strjoina(prefix, "\t");
470✔
2119

2120
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
94✔
2121

2122
        fprintf(f,
94✔
2123
                "%sCommand Line: %s\n",
2124
                prefix, strnull(cmd));
2125

2126
        exec_status_dump(&c->exec_status, f, prefix2);
94✔
2127
}
94✔
2128

2129
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
91✔
2130
        assert(f);
91✔
2131

2132
        prefix = strempty(prefix);
91✔
2133

2134
        LIST_FOREACH(command, i, c)
185✔
2135
                exec_command_dump(i, f, prefix);
94✔
2136
}
91✔
2137

2138
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
20,907✔
2139
        ExecCommand *end;
20,907✔
2140

2141
        assert(l);
20,907✔
2142
        assert(e);
20,907✔
2143

2144
        if (*l) {
20,907✔
2145
                /* It's kind of important, that we keep the order here */
2146
                end = LIST_FIND_TAIL(command, *l);
369✔
2147
                LIST_INSERT_AFTER(command, *l, end, e);
132✔
2148
        } else
2149
                *l = e;
20,775✔
2150
}
20,907✔
2151

2152
int exec_command_set(ExecCommand *c, const char *path, ...) {
570✔
2153
        va_list ap;
570✔
2154
        char **l, *p;
570✔
2155

2156
        assert(c);
570✔
2157
        assert(path);
570✔
2158

2159
        va_start(ap, path);
570✔
2160
        l = strv_new_ap(path, ap);
570✔
2161
        va_end(ap);
570✔
2162

2163
        if (!l)
570✔
2164
                return -ENOMEM;
570✔
2165

2166
        p = strdup(path);
570✔
2167
        if (!p) {
570✔
2168
                strv_free(l);
×
2169
                return -ENOMEM;
×
2170
        }
2171

2172
        free_and_replace(c->path, p);
570✔
2173

2174
        return strv_free_and_replace(c->argv, l);
570✔
2175
}
2176

2177
int exec_command_append(ExecCommand *c, const char *path, ...) {
991✔
2178
        char **l;
991✔
2179
        va_list ap;
991✔
2180
        int r;
991✔
2181

2182
        assert(c);
991✔
2183
        assert(path);
991✔
2184

2185
        va_start(ap, path);
991✔
2186
        l = strv_new_ap(path, ap);
991✔
2187
        va_end(ap);
991✔
2188

2189
        if (!l)
991✔
2190
                return -ENOMEM;
991✔
2191

2192
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
991✔
2193
        if (r < 0)
991✔
2194
                return r;
×
2195

2196
        return 0;
2197
}
2198

2199
static char *destroy_tree(char *path) {
421✔
2200
        if (!path)
421✔
2201
                return NULL;
2202

2203
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
186✔
2204
                log_debug("Spawning process to nuke '%s'", path);
186✔
2205

2206
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
186✔
2207
        }
2208

2209
        return mfree(path);
186✔
2210
}
2211

2212
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
128,602✔
2213
        assert(rt);
128,602✔
2214

2215
        if (rt->manager)
128,602✔
2216
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
220✔
2217

2218
        rt->id = mfree(rt->id);
128,602✔
2219
        rt->tmp_dir = mfree(rt->tmp_dir);
128,602✔
2220
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
128,602✔
2221
        safe_close_pair(rt->netns_storage_socket);
128,602✔
2222
        safe_close_pair(rt->ipcns_storage_socket);
128,602✔
2223
}
128,602✔
2224

2225
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
128,574✔
2226
        if (!rt)
128,574✔
2227
                return NULL;
2228

2229
        exec_shared_runtime_done(rt);
128,574✔
2230
        return mfree(rt);
128,574✔
2231
}
2232

2233
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
221✔
2234
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
131,560✔
2235

2236
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
101✔
2237
        if (!rt)
101✔
2238
                return NULL;
2239

2240
        assert(rt->n_ref > 0);
100✔
2241
        rt->n_ref--;
100✔
2242

2243
        if (rt->n_ref > 0)
100✔
2244
                return NULL;
2245

2246
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
100✔
2247
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
100✔
2248

2249
        return exec_shared_runtime_free(rt);
100✔
2250
}
2251

2252
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
128,574✔
2253
        _cleanup_free_ char *id_copy = NULL;
257,148✔
2254
        ExecSharedRuntime *n;
128,574✔
2255

2256
        assert(ret);
128,574✔
2257

2258
        id_copy = strdup(id);
128,574✔
2259
        if (!id_copy)
128,574✔
2260
                return -ENOMEM;
2261

2262
        n = new(ExecSharedRuntime, 1);
128,574✔
2263
        if (!n)
128,574✔
2264
                return -ENOMEM;
2265

2266
        *n = (ExecSharedRuntime) {
128,574✔
2267
                .id = TAKE_PTR(id_copy),
128,574✔
2268
                .netns_storage_socket = EBADF_PAIR,
2269
                .ipcns_storage_socket = EBADF_PAIR,
2270
        };
2271

2272
        *ret = n;
128,574✔
2273
        return 0;
128,574✔
2274
}
2275

2276
static int exec_shared_runtime_add(
220✔
2277
                Manager *m,
2278
                const char *id,
2279
                char **tmp_dir,
2280
                char **var_tmp_dir,
2281
                int netns_storage_socket[2],
2282
                int ipcns_storage_socket[2],
2283
                ExecSharedRuntime **ret) {
2284

2285
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
220✔
2286
        int r;
220✔
2287

2288
        assert(m);
220✔
2289
        assert(id);
220✔
2290

2291
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2292

2293
        r = exec_shared_runtime_allocate(&rt, id);
220✔
2294
        if (r < 0)
220✔
2295
                return r;
2296

2297
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
220✔
2298
        if (r < 0)
220✔
2299
                return r;
2300

2301
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
220✔
2302
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
220✔
2303
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
220✔
2304

2305
        if (netns_storage_socket) {
220✔
2306
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
220✔
2307
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
220✔
2308
        }
2309

2310
        if (ipcns_storage_socket) {
220✔
2311
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
220✔
2312
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
220✔
2313
        }
2314

2315
        rt->manager = m;
220✔
2316

2317
        if (ret)
220✔
2318
                *ret = rt;
121✔
2319
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2320
        TAKE_PTR(rt);
220✔
2321
        return 0;
220✔
2322
}
2323

2324
static int exec_shared_runtime_make(
7,619✔
2325
                Manager *m,
2326
                const ExecContext *c,
2327
                const char *id,
2328
                ExecSharedRuntime **ret) {
2329

2330
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
7,619✔
2331
        _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
15,238✔
2332
        int r;
7,619✔
2333

2334
        assert(m);
7,619✔
2335
        assert(c);
7,619✔
2336
        assert(id);
7,619✔
2337

2338
        /* It is not necessary to create ExecSharedRuntime object. */
2339
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
7,619✔
2340
                *ret = NULL;
7,498✔
2341
                return 0;
7,498✔
2342
        }
2343

2344
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
235✔
2345
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
114✔
2346
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
2347
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2348
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
114✔
2349
                if (r < 0)
114✔
2350
                        return r;
2351
        }
2352

2353
        if (exec_needs_network_namespace(c))
121✔
2354
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
7✔
2355
                        return -errno;
×
2356

2357
        if (exec_needs_ipc_namespace(c))
121✔
2358
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
1✔
2359
                        return -errno;
×
2360

2361
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
121✔
2362
        if (r < 0)
121✔
2363
                return r;
×
2364

2365
        return 1;
2366
}
2367

2368
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
7,718✔
2369
        ExecSharedRuntime *rt;
7,718✔
2370
        int r;
7,718✔
2371

2372
        assert(m);
7,718✔
2373
        assert(id);
7,718✔
2374
        assert(ret);
7,718✔
2375

2376
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
7,718✔
2377
        if (rt)
7,718✔
2378
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2379
                goto ref;
99✔
2380

2381
        if (!create) {
7,619✔
2382
                *ret = NULL;
×
2383
                return 0;
×
2384
        }
2385

2386
        /* If not found, then create a new object. */
2387
        r = exec_shared_runtime_make(m, c, id, &rt);
7,619✔
2388
        if (r < 0)
7,619✔
2389
                return r;
2390
        if (r == 0) {
7,619✔
2391
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2392
                *ret = NULL;
7,498✔
2393
                return 0;
7,498✔
2394
        }
2395

2396
ref:
121✔
2397
        /* increment reference counter. */
2398
        rt->n_ref++;
220✔
2399
        *ret = rt;
220✔
2400
        return 1;
220✔
2401
}
2402

2403
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
123✔
2404
        ExecSharedRuntime *rt;
123✔
2405

2406
        assert(m);
123✔
2407
        assert(f);
123✔
2408
        assert(fds);
123✔
2409

2410
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
243✔
2411
                fprintf(f, "exec-runtime=%s", rt->id);
120✔
2412

2413
                if (rt->tmp_dir)
120✔
2414
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
120✔
2415

2416
                if (rt->var_tmp_dir)
120✔
2417
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
120✔
2418

2419
                if (rt->netns_storage_socket[0] >= 0) {
120✔
2420
                        int copy;
2✔
2421

2422
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2✔
2423
                        if (copy < 0)
2✔
2424
                                return copy;
×
2425

2426
                        fprintf(f, " netns-socket-0=%i", copy);
2✔
2427
                }
2428

2429
                if (rt->netns_storage_socket[1] >= 0) {
120✔
2430
                        int copy;
2✔
2431

2432
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2✔
2433
                        if (copy < 0)
2✔
2434
                                return copy;
2435

2436
                        fprintf(f, " netns-socket-1=%i", copy);
2✔
2437
                }
2438

2439
                if (rt->ipcns_storage_socket[0] >= 0) {
120✔
2440
                        int copy;
×
2441

2442
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2443
                        if (copy < 0)
×
2444
                                return copy;
2445

2446
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2447
                }
2448

2449
                if (rt->ipcns_storage_socket[1] >= 0) {
120✔
2450
                        int copy;
×
2451

2452
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2453
                        if (copy < 0)
×
2454
                                return copy;
2455

2456
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2457
                }
2458

2459
                fputc('\n', f);
120✔
2460
        }
2461

2462
        return 0;
123✔
2463
}
2464

2465
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
131,340✔
2466
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
131,340✔
2467
        ExecSharedRuntime *rt = NULL;
131,340✔
2468
        int r;
131,340✔
2469

2470
        /* This is for the migration from old (v237 or earlier) deserialization text.
2471
         * Due to the bug #7790, this may not work with the units that use JoinsNamespaceOf=.
2472
         * Even if the ExecSharedRuntime object originally created by the other unit, we cannot judge
2473
         * so or not from the serialized text, then we always creates a new object owned by this. */
2474

2475
        assert(u);
131,340✔
2476
        assert(key);
131,340✔
2477
        assert(value);
131,340✔
2478

2479
        /* Manager manages ExecSharedRuntime objects by the unit id.
2480
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2481
        if (isempty(u->id)) {
131,340✔
2482
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
2483
                return 0;
×
2484
        }
2485

2486
        if (u->manager) {
131,340✔
2487
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
131,340✔
2488
                        return log_oom();
×
2489

2490
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
131,340✔
2491
        }
2492
        if (!rt) {
131,340✔
2493
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
128,354✔
2494
                        return log_oom();
×
2495

2496
                rt = rt_create;
128,354✔
2497
        }
2498

2499
        if (streq(key, "tmp-dir")) {
131,340✔
2500
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2501
                        return -ENOMEM;
2502

2503
        } else if (streq(key, "var-tmp-dir")) {
131,340✔
2504
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2505
                        return -ENOMEM;
2506

2507
        } else if (streq(key, "netns-socket-0")) {
131,340✔
2508

2509
                safe_close(rt->netns_storage_socket[0]);
×
2510
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2511
                if (rt->netns_storage_socket[0] < 0)
×
2512
                        return 0;
2513

2514
        } else if (streq(key, "netns-socket-1")) {
131,340✔
2515

2516
                safe_close(rt->netns_storage_socket[1]);
×
2517
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2518
                if (rt->netns_storage_socket[1] < 0)
×
2519
                        return 0;
2520
        } else
2521
                return 0;
2522

2523
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
2524
        if (rt_create && u->manager) {
×
2525
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
2526
                if (r < 0) {
×
2527
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
2528
                        return 0;
×
2529
                }
2530

2531
                rt_create->manager = u->manager;
×
2532

2533
                /* Avoid cleanup */
2534
                TAKE_PTR(rt_create);
×
2535
        }
2536

2537
        return 1;
2538
}
2539

2540
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
99✔
2541
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
99✔
2542
        char *id = NULL;
99✔
2543
        int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
99✔
2544
        const char *p, *v = ASSERT_PTR(value);
99✔
2545
        size_t n;
99✔
2546

2547
        assert(m);
99✔
2548
        assert(fds);
99✔
2549

2550
        n = strcspn(v, " ");
99✔
2551
        id = strndupa_safe(v, n);
99✔
2552
        if (v[n] != ' ')
99✔
2553
                goto finalize;
×
2554
        p = v + n + 1;
99✔
2555

2556
        v = startswith(p, "tmp-dir=");
99✔
2557
        if (v) {
99✔
2558
                n = strcspn(v, " ");
99✔
2559
                tmp_dir = strndup(v, n);
99✔
2560
                if (!tmp_dir)
99✔
2561
                        return log_oom();
×
2562
                if (v[n] != ' ')
99✔
2563
                        goto finalize;
×
2564
                p = v + n + 1;
99✔
2565
        }
2566

2567
        v = startswith(p, "var-tmp-dir=");
99✔
2568
        if (v) {
99✔
2569
                n = strcspn(v, " ");
99✔
2570
                var_tmp_dir = strndup(v, n);
99✔
2571
                if (!var_tmp_dir)
99✔
2572
                        return log_oom();
×
2573
                if (v[n] != ' ')
99✔
2574
                        goto finalize;
98✔
2575
                p = v + n + 1;
1✔
2576
        }
2577

2578
        v = startswith(p, "netns-socket-0=");
1✔
2579
        if (v) {
1✔
2580
                char *buf;
1✔
2581

2582
                n = strcspn(v, " ");
1✔
2583
                buf = strndupa_safe(v, n);
1✔
2584

2585
                netns_fdpair[0] = deserialize_fd(fds, buf);
1✔
2586
                if (netns_fdpair[0] < 0)
1✔
2587
                        return netns_fdpair[0];
2588
                if (v[n] != ' ')
1✔
2589
                        goto finalize;
×
2590
                p = v + n + 1;
1✔
2591
        }
2592

2593
        v = startswith(p, "netns-socket-1=");
1✔
2594
        if (v) {
1✔
2595
                char *buf;
1✔
2596

2597
                n = strcspn(v, " ");
1✔
2598
                buf = strndupa_safe(v, n);
1✔
2599

2600
                netns_fdpair[1] = deserialize_fd(fds, buf);
1✔
2601
                if (netns_fdpair[1] < 0)
1✔
2602
                        return netns_fdpair[1];
2603
                if (v[n] != ' ')
1✔
2604
                        goto finalize;
1✔
2605
                p = v + n + 1;
×
2606
        }
2607

2608
        v = startswith(p, "ipcns-socket-0=");
×
2609
        if (v) {
×
2610
                char *buf;
×
2611

2612
                n = strcspn(v, " ");
×
2613
                buf = strndupa_safe(v, n);
×
2614

2615
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2616
                if (ipcns_fdpair[0] < 0)
×
2617
                        return ipcns_fdpair[0];
2618
                if (v[n] != ' ')
×
2619
                        goto finalize;
×
2620
                p = v + n + 1;
×
2621
        }
2622

2623
        v = startswith(p, "ipcns-socket-1=");
×
2624
        if (v) {
×
2625
                char *buf;
×
2626

2627
                n = strcspn(v, " ");
×
2628
                buf = strndupa_safe(v, n);
×
2629

2630
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2631
                if (ipcns_fdpair[1] < 0)
×
2632
                        return ipcns_fdpair[1];
2633
        }
2634

2635
finalize:
×
2636
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
99✔
2637
        if (r < 0)
99✔
2638
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2639
        return 0;
2640
}
2641

2642
void exec_shared_runtime_vacuum(Manager *m) {
1,282✔
2643
        ExecSharedRuntime *rt;
1,282✔
2644

2645
        assert(m);
1,282✔
2646

2647
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2648

2649
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,381✔
2650
                if (rt->n_ref > 0)
99✔
2651
                        continue;
99✔
2652

2653
                (void) exec_shared_runtime_free(rt);
×
2654
        }
2655
}
1,282✔
2656

2657
int exec_runtime_make(
7,718✔
2658
                const Unit *unit,
2659
                const ExecContext *context,
2660
                ExecSharedRuntime *shared,
2661
                DynamicCreds *creds,
2662
                ExecRuntime **ret) {
2663
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
7,718✔
2664
        _cleanup_free_ char *ephemeral = NULL;
7,718✔
2665
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
7,718✔
2666
        int r;
7,718✔
2667

2668
        assert(unit);
7,718✔
2669
        assert(context);
7,718✔
2670
        assert(ret);
7,718✔
2671

2672
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
7,718✔
2673
                *ret = NULL;
7,497✔
2674
                return 0;
7,497✔
2675
        }
2676

2677
        if (exec_needs_ephemeral(context)) {
221✔
2678
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2679
                if (r < 0)
×
2680
                        return r;
2681

2682
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2683
                if (r < 0)
×
2684
                        return r;
2685

2686
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2687
                        return -errno;
×
2688
        }
2689

2690
        rt = new(ExecRuntime, 1);
221✔
2691
        if (!rt)
221✔
2692
                return -ENOMEM;
2693

2694
        *rt = (ExecRuntime) {
221✔
2695
                .shared = shared,
2696
                .dynamic_creds = creds,
2697
                .ephemeral_copy = TAKE_PTR(ephemeral),
221✔
2698
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
221✔
2699
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
221✔
2700
        };
2701

2702
        *ret = TAKE_PTR(rt);
221✔
2703
        return 1;
221✔
2704
}
2705

2706
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
50,093✔
2707
        if (!rt)
50,093✔
2708
                return NULL;
2709

2710
        exec_shared_runtime_unref(rt->shared);
221✔
2711
        dynamic_creds_unref(rt->dynamic_creds);
221✔
2712

2713
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
221✔
2714

2715
        safe_close_pair(rt->ephemeral_storage_socket);
221✔
2716
        return mfree(rt);
221✔
2717
}
2718

2719
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
5,884✔
2720
        if (!rt)
5,884✔
2721
                return NULL;
2722

2723
        rt->shared = exec_shared_runtime_destroy(rt->shared);
101✔
2724
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
101✔
2725
        return exec_runtime_free(rt);
101✔
2726
}
2727

2728
void exec_runtime_clear(ExecRuntime *rt) {
28✔
2729
        if (!rt)
28✔
2730
                return;
2731

2732
        safe_close_pair(rt->ephemeral_storage_socket);
28✔
2733
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
28✔
2734
}
2735

2736
void exec_params_shallow_clear(ExecParameters *p) {
3,851✔
2737
        if (!p)
3,851✔
2738
                return;
2739

2740
        /* This is called on the PID1 side, as many of the struct's FDs are only borrowed, and actually
2741
         * owned by the manager or other objects, and reused across multiple units. */
2742

2743
        p->environment = strv_free(p->environment);
3,851✔
2744
        p->fd_names = strv_free(p->fd_names);
3,851✔
2745
        p->files_env = strv_free(p->files_env);
3,851✔
2746
        p->fds = mfree(p->fds);
3,851✔
2747
        p->exec_fd = safe_close(p->exec_fd);
3,851✔
2748
        p->user_lookup_fd = -EBADF;
3,851✔
2749
        p->bpf_restrict_fs_map_fd = -EBADF;
3,851✔
2750
        p->unit_id = mfree(p->unit_id);
3,851✔
2751
        p->invocation_id = SD_ID128_NULL;
3,851✔
2752
        p->invocation_id_string[0] = '\0';
3,851✔
2753
        p->confirm_spawn = mfree(p->confirm_spawn);
3,851✔
2754
}
2755

2756
void exec_params_deep_clear(ExecParameters *p) {
28✔
2757
        if (!p)
28✔
2758
                return;
2759

2760
        /* This is called on the sd-executor side, where everything received is owned by the process and has
2761
         * to be fully cleaned up to make sanitizers and analyzers happy, as opposed as the shallow clean
2762
         * function above. */
2763

2764
        close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
28✔
2765

2766
        p->cgroup_path = mfree(p->cgroup_path);
28✔
2767

2768
        if (p->prefix) {
28✔
2769
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
28✔
2770
                p->prefix = mfree(p->prefix);
28✔
2771
        }
2772

2773
        p->received_credentials_directory = mfree(p->received_credentials_directory);
28✔
2774
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
28✔
2775

2776
        if (p->idle_pipe) {
28✔
2777
                close_many_and_free(p->idle_pipe, 4);
×
2778
                p->idle_pipe = NULL;
×
2779
        }
2780

2781
        p->stdin_fd = safe_close(p->stdin_fd);
28✔
2782
        p->stdout_fd = safe_close(p->stdout_fd);
28✔
2783
        p->stderr_fd = safe_close(p->stderr_fd);
28✔
2784

2785
        p->notify_socket = mfree(p->notify_socket);
28✔
2786

2787
        open_file_free_many(&p->open_files);
28✔
2788

2789
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
28✔
2790

2791
        exec_params_shallow_clear(p);
28✔
2792
}
2793

2794
void exec_directory_done(ExecDirectory *d) {
250,100✔
2795
        if (!d)
250,100✔
2796
                return;
2797

2798
        FOREACH_ARRAY(i, d->items, d->n_items) {
252,679✔
2799
                free(i->path);
2,579✔
2800
                strv_free(i->symlinks);
2,579✔
2801
        }
2802

2803
        d->items = mfree(d->items);
250,100✔
2804
        d->n_items = 0;
250,100✔
2805
        d->mode = 0755;
250,100✔
2806
}
2807

2808
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
6,596✔
2809
        assert(d);
6,596✔
2810
        assert(path);
6,596✔
2811

2812
        FOREACH_ARRAY(i, d->items, d->n_items)
9,420✔
2813
                if (path_equal(i->path, path))
2,839✔
2814
                        return i;
2815

2816
        return NULL;
2817
}
2818

2819
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
6,596✔
2820
        _cleanup_strv_free_ char **s = NULL;
×
2821
        _cleanup_free_ char *p = NULL;
6,596✔
2822
        ExecDirectoryItem *existing;
6,596✔
2823
        int r;
6,596✔
2824

2825
        assert(d);
6,596✔
2826
        assert(path);
6,596✔
2827

2828
        existing = exec_directory_find(d, path);
6,596✔
2829
        if (existing) {
6,596✔
2830
                r = strv_extend(&existing->symlinks, symlink);
15✔
2831
                if (r < 0)
15✔
2832
                        return r;
2833

2834
                existing->flags |= flags;
15✔
2835

2836
                return 0; /* existing item is updated */
15✔
2837
        }
2838

2839
        p = strdup(path);
6,581✔
2840
        if (!p)
6,581✔
2841
                return -ENOMEM;
2842

2843
        if (symlink) {
6,581✔
2844
                s = strv_new(symlink);
6✔
2845
                if (!s)
6✔
2846
                        return -ENOMEM;
2847
        }
2848

2849
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
6,581✔
2850
                return -ENOMEM;
2851

2852
        d->items[d->n_items++] = (ExecDirectoryItem) {
6,581✔
2853
                .path = TAKE_PTR(p),
6,581✔
2854
                .symlinks = TAKE_PTR(s),
6,581✔
2855
                .flags = flags,
2856
        };
2857

2858
        return 1; /* new item is added */
6,581✔
2859
}
2860

2861
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
1,226✔
2862
        assert(a);
1,226✔
2863
        assert(b);
1,226✔
2864

2865
        return path_compare(a->path, b->path);
1,226✔
2866
}
2867

2868
void exec_directory_sort(ExecDirectory *d) {
143,324✔
2869
        assert(d);
143,324✔
2870

2871
        /* Sort the exec directories to make always parent directories processed at first in
2872
         * setup_exec_directory(), e.g., even if StateDirectory=foo/bar foo, we need to create foo at first,
2873
         * then foo/bar. Also, set the ONLY_CREATE flag if one of the parent directories is contained in the
2874
         * list. See also comments in setup_exec_directory() and issue #24783. */
2875

2876
        if (d->n_items <= 1)
143,324✔
2877
                return;
2878

2879
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
208✔
2880

2881
        for (size_t i = 1; i < d->n_items; i++)
935✔
2882
                for (size_t j = 0; j < i; j++)
2,452✔
2883
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,740✔
2884
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
2885
                                break;
15✔
2886
                        }
2887
}
2888

2889
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
2890
        ExecDirectoryType t;
×
2891

2892
        assert(s);
×
2893

2894
        if (streq(s, "all"))
×
2895
                return EXEC_CLEAN_ALL;
2896
        if (streq(s, "fdstore"))
×
2897
                return EXEC_CLEAN_FDSTORE;
2898

2899
        t = exec_resource_type_from_string(s);
×
2900
        if (t < 0)
×
2901
                return (ExecCleanMask) t;
2902

2903
        return 1U << t;
×
2904
}
2905

2906
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
2907
        [EXEC_INPUT_NULL]      = "null",
2908
        [EXEC_INPUT_TTY]       = "tty",
2909
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
2910
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
2911
        [EXEC_INPUT_SOCKET]    = "socket",
2912
        [EXEC_INPUT_NAMED_FD]  = "fd",
2913
        [EXEC_INPUT_DATA]      = "data",
2914
        [EXEC_INPUT_FILE]      = "file",
2915
};
2916

2917
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
19,406✔
2918

2919
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
2920
        [EXEC_OUTPUT_INHERIT]             = "inherit",
2921
        [EXEC_OUTPUT_NULL]                = "null",
2922
        [EXEC_OUTPUT_TTY]                 = "tty",
2923
        [EXEC_OUTPUT_KMSG]                = "kmsg",
2924
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
2925
        [EXEC_OUTPUT_JOURNAL]             = "journal",
2926
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
2927
        [EXEC_OUTPUT_SOCKET]              = "socket",
2928
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
2929
        [EXEC_OUTPUT_FILE]                = "file",
2930
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
2931
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
2932
};
2933

2934
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
38,964✔
2935

2936
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
2937
        [EXEC_UTMP_INIT]  = "init",
2938
        [EXEC_UTMP_LOGIN] = "login",
2939
        [EXEC_UTMP_USER]  = "user",
2940
};
2941

2942
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
18,454✔
2943

2944
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
2945
        [EXEC_PRESERVE_NO]      = "no",
2946
        [EXEC_PRESERVE_YES]     = "yes",
2947
        [EXEC_PRESERVE_RESTART] = "restart",
2948
};
2949

2950
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
20,386✔
2951

2952
/* This table maps ExecDirectoryType to the setting it is configured with in the unit */
2953
static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2954
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectory",
2955
        [EXEC_DIRECTORY_STATE]         = "StateDirectory",
2956
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectory",
2957
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectory",
2958
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectory",
2959
};
2960

2961
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
174,656✔
2962

2963
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
2964
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2965
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
2966
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
2967
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
2968
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
2969
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
2970
};
2971

2972
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
2973

2974
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2975
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
2976
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
2977
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
2978
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
2979
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
2980
};
2981

2982
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
2983

2984
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
2985
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
2986
 * directories, specifically .timer units with their timestamp touch file. */
2987
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2988
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
2989
        [EXEC_DIRECTORY_STATE]         = "state",
2990
        [EXEC_DIRECTORY_CACHE]         = "cache",
2991
        [EXEC_DIRECTORY_LOGS]          = "logs",
2992
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
2993
};
2994

2995
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
247✔
2996

2997
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
2998
        [EXEC_KEYRING_INHERIT] = "inherit",
2999
        [EXEC_KEYRING_PRIVATE] = "private",
3000
        [EXEC_KEYRING_SHARED]  = "shared",
3001
};
3002

3003
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
18,797✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc