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

systemd / systemd / 13620653620

02 Mar 2025 09:00PM UTC coverage: 71.867% (+0.01%) from 71.857%
13620653620

push

github

web-flow
osc-context: several follow-ups (#36579)

11 of 28 new or added lines in 4 files covered. (39.29%)

534 existing lines in 39 files now uncovered.

294792 of 410191 relevant lines covered (71.87%)

715294.62 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,293✔
79
        return IN_SET(i,
43,293✔
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,526✔
86
        return IN_SET(o,
85,526✔
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,678✔
93
        assert(context);
17,678✔
94

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

98
        if (context->tty_path)
17,095✔
99
                return context->tty_path;
682✔
100

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

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

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

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

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

120
        if (!tty_path)
1,085✔
121
                tty_path = exec_context_tty_path(context);
419✔
122

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

127
        /* Fill in data from kernel command line if anything is unspecified */
128
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
1,085✔
129
                (void) proc_cmdline_tty_size(
1,053✔
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,292✔
139
            exec_context_shall_ansi_seq_reset(context) &&
386✔
140
            isatty_safe(input_fd)) {
179✔
141
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
179✔
142
                if (r < 0)
179✔
143
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
179✔
144
        }
145

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

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

153
        assert(context);
16,023✔
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,023✔
161

162
        if (p && p->stdout_fd >= 0 && isatty_safe(p->stdout_fd))
16,023✔
163
                fd = p->stdout_fd;
16✔
164
        else if (path && (context->tty_path || is_terminal_input(context->std_input) ||
16,007✔
165
                        is_terminal_output(context->std_output) || is_terminal_output(context->std_error))) {
15,111✔
166
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
666✔
167
                if (fd < 0)
666✔
UNCOV
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();
682✔
176
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
682✔
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))
682✔
179
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
180
        else if (lock_fd < 0)
682✔
181
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
182

183
        if (context->tty_reset)
682✔
184
                (void) terminal_reset_defensive(
176✔
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));
173✔
188

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

193
        if (!sd_id128_is_null(invocation_id)) {
1,315✔
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✔
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)
682✔
211
                (void) terminal_vhangup_fd(fd);
168✔
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);
682✔
216

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

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

224
        return context->private_network || context->network_namespace_path;
65,281✔
225
}
226

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

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

234
        return context->private_ipc || context->ipc_namespace_path;
60,635✔
235
}
236

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

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

245
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context, const ExecParameters *params) {
71,786✔
246
        assert(context);
71,786✔
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)) {
71,786✔
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;
71,786✔
261
}
262

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

266
        return needs_cgroup_namespace(exec_get_protect_control_groups(context, params));
35,348✔
267
}
268

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

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

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

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

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

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

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

292
        assert(context);
36,623✔
293

294
        if (context->root_image)
36,623✔
295
                return true;
296

297
        if (!strv_isempty(context->read_write_paths) ||
36,599✔
298
            !strv_isempty(context->read_only_paths) ||
33,766✔
299
            !strv_isempty(context->inaccessible_paths) ||
33,753✔
300
            !strv_isempty(context->exec_paths) ||
33,746✔
301
            !strv_isempty(context->no_exec_paths))
33,746✔
302
                return true;
303

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

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

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

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

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

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

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

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

328
        if (context->private_devices ||
31,464✔
329
            context->private_mounts > 0 ||
30,400✔
330
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
29,891✔
331
            context->protect_system != PROTECT_SYSTEM_NO ||
29,888✔
332
            context->protect_home != PROTECT_HOME_NO ||
333
            context->protect_kernel_tunables ||
334
            context->protect_kernel_modules ||
29,888✔
335
            context->protect_kernel_logs ||
28,226✔
336
            exec_needs_cgroup_mount(context, params) ||
28,226✔
337
            context->protect_proc != PROTECT_PROC_DEFAULT ||
28,208✔
338
            context->proc_subset != PROC_SUBSET_ALL ||
28,151✔
339
            exec_needs_ipc_namespace(context) ||
56,302✔
340
            exec_needs_pid_namespace(context))
28,151✔
341
                return true;
3,360✔
342

343
        if (context->root_directory) {
28,104✔
344
                if (exec_context_get_effective_mount_apivfs(context))
5✔
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 &&
28,099✔
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))
28,099✔
363
                return true;
364

365
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
168,250✔
366
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
145,471✔
367
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,314✔
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,016✔
374
        assert(context);
5,016✔
375
        assert(params);
5,016✔
376

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

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

383
        if (!context->root_directory && !context->root_image)
4,378✔
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,988✔
411
                const ExecParameters *params,
412
                const CGroupContext *c,
413
                char **ret) {
414

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

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

421
        if (!params->cgroup_path)
17,988✔
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,988✔
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,247✔
444
        if (!p)
17,988✔
445
                return -ENOMEM;
446

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

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

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

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

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

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

467
        log_unit_struct(unit, LOG_DEBUG,
3,821✔
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,821✔
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,821✔
485
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
486
        _cleanup_fclose_ FILE *f = NULL;
3,821✔
487
        int r;
3,821✔
488

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

500
        LOG_CONTEXT_PUSH_UNIT(unit);
7,642✔
501

502
        r = exec_context_load_environment(unit, context, &params->files_env);
3,821✔
503
        if (r < 0)
3,821✔
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,821✔
509

510
        if (params->cgroup_path) {
3,821✔
511
                r = exec_params_get_cgroup_path(params, cgroup_context, &subcgroup_path);
3,821✔
512
                if (r < 0)
3,821✔
513
                        return log_unit_error_errno(unit, r, "Failed to acquire subcgroup path: %m");
×
514
                if (r > 0) {
3,821✔
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,821✔
532
        if (r < 0)
3,821✔
533
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
534

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

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

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

547
        r = fd_cloexec(fileno(f), false);
3,821✔
548
        if (r < 0)
3,821✔
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,821✔
552
        if (r < 0)
3,821✔
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,821✔
559
        if (r < 0)
3,821✔
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,821✔
563
        if (r < 0)
3,821✔
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,821✔
567
        xsprintf(serialization_fd_number, "%i", fileno(f));
3,821✔
568

569
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
3,821✔
570
        dual_timestamp start_timestamp;
3,821✔
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,821✔
575
        if (r < 0)
3,821✔
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,821✔
581

582
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
583
        r = posix_spawn_wrapper(
7,642✔
584
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
3,821✔
585
                        STRV_MAKE(executor_path,
3,821✔
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,821✔
595

596
        if (r == -EUCLEAN && subcgroup_path)
3,821✔
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,821✔
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,821✔
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,642✔
611
                       command->path, pidref.pid, r > 0 ? "via" : "without");
612

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

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

619
void exec_context_init(ExecContext *c) {
63,487✔
620
        assert(c);
63,487✔
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,487✔
626
                .umask = 0022,
627
                .ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO,
63,487✔
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
                .delegate_namespaces = NAMESPACE_FLAGS_INITIAL,
638
                .log_level_max = -1,
639
#if HAVE_SECCOMP
640
                .syscall_errno = SECCOMP_ERROR_NUMBER_KILL,
641
#endif
642
                .tty_rows = UINT_MAX,
643
                .tty_cols = UINT_MAX,
644
                .private_mounts = -1,
645
                .mount_apivfs = -1,
646
                .bind_log_sockets = -1,
647
                .memory_ksm = -1,
648
                .set_login_environment = -1,
649
        };
650

651
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
380,922✔
652
                d->mode = 0755;
317,435✔
653

654
        numa_policy_reset(&c->numa_policy);
63,487✔
655

656
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
63,487✔
657
}
63,487✔
658

659
void exec_context_done(ExecContext *c) {
50,001✔
660
        assert(c);
50,001✔
661

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

667
        rlimit_free_all(c->rlimit);
50,001✔
668

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

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

692
        c->supplementary_groups = strv_free(c->supplementary_groups);
50,001✔
693

694
        c->pam_name = mfree(c->pam_name);
50,001✔
695

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

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

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

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

719
        c->restrict_filesystems = set_free_free(c->restrict_filesystems);
50,001✔
720

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

726
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
300,006✔
727
                exec_directory_done(d);
250,005✔
728

729
        c->log_level_max = -1;
50,001✔
730

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

735
        c->log_ratelimit = (RateLimit) {};
50,001✔
736

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

740
        c->network_namespace_path = mfree(c->network_namespace_path);
50,001✔
741
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
50,001✔
742

743
        c->log_namespace = mfree(c->log_namespace);
50,001✔
744

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

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

753
        c->private_hostname = mfree(c->private_hostname);
50,001✔
754
}
50,001✔
755

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

759
        if (!runtime_prefix)
5,754✔
760
                return 0;
761

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

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

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

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

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

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

790
        return 0;
791
}
792

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

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

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

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

807
        return 0;
808
}
809

810
void exec_command_done(ExecCommand *c) {
101,659✔
811
        assert(c);
101,659✔
812

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

817
void exec_command_done_array(ExecCommand *c, size_t n) {
26,904✔
818
        FOREACH_ARRAY(i, c, n)
107,615✔
819
                exec_command_done(i);
80,711✔
820
}
26,904✔
821

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

826
        exec_command_done(c);
20,920✔
827
        return mfree(c);
20,920✔
828
}
829

830
ExecCommand* exec_command_free_list(ExecCommand *c) {
150,066✔
831
        ExecCommand *i;
150,066✔
832

833
        while ((i = LIST_POP(command, c)))
170,986✔
834
                exec_command_free(i);
20,920✔
835

836
        return NULL;
150,066✔
837
}
838

839
void exec_command_free_array(ExecCommand **c, size_t n) {
23,069✔
840
        FOREACH_ARRAY(i, c, n)
173,120✔
841
                *i = exec_command_free_list(*i);
150,051✔
842
}
23,069✔
843

844
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
5,469✔
845
        FOREACH_ARRAY(i, c, n)
21,875✔
846
                exec_status_reset(&i->exec_status);
16,406✔
847
}
5,469✔
848

849
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
5,993✔
850
        FOREACH_ARRAY(i, c, n)
42,124✔
851
                LIST_FOREACH(command, z, *i)
39,577✔
852
                        exec_status_reset(&z->exec_status);
3,446✔
853
}
5,993✔
854

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

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

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

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

869
        switch (fd_index) {
43,884✔
870

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

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

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

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

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

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

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

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

898
        assert(c);
3,821✔
899
        assert(ret);
3,821✔
900

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

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

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

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

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

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

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

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

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

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

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

960
        *ret = TAKE_PTR(v);
3,821✔
961

962
        return 0;
3,821✔
963
}
964

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

968
        if (!tty)
347✔
969
                return true;
970

971
        tty = skip_dev_prefix(tty);
347✔
972

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

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

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

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

987
        return ec->tty_reset ||
28,105✔
988
                ec->tty_vhangup ||
28,105✔
989
                ec->tty_vt_disallocate ||
27,973✔
990
                is_terminal_input(ec->std_input) ||
27,973✔
991
                is_terminal_output(ec->std_output) ||
55,988✔
992
                is_terminal_output(ec->std_error);
27,709✔
993
}
994

995
bool exec_context_may_touch_console(const ExecContext *ec) {
25,596✔
996

997
        return exec_context_may_touch_tty(ec) &&
25,943✔
998
               tty_may_match_dev_console(exec_context_tty_path(ec));
347✔
999
}
1000

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

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

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

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

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

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

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

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

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

1040
        prefix = strempty(prefix);
×
1041

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

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

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

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

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

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

1086
        prefix = strempty(prefix);
224✔
1087

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1590
        return false;
×
1591
}
1592

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

1596
        assert(c);
2,226✔
1597

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

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

1605
        return ioprio_normalize(p);
2,208✔
1606
}
1607

1608
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
38,445✔
1609
        assert(c);
38,445✔
1610

1611
        /* Explicit setting wins */
1612
        if (c->mount_apivfs >= 0)
38,445✔
1613
                return c->mount_apivfs > 0;
110✔
1614

1615
        /* Default to "yes" if root directory or image are specified */
1616
        if (exec_context_with_rootfs(c))
38,335✔
1617
                return true;
46✔
1618

1619
        return false;
1620
}
1621

1622
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
31,995✔
1623
        assert(c);
31,995✔
1624

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

1630
        if (c->bind_log_sockets >= 0)
31,987✔
1631
                return c->bind_log_sockets > 0;
2✔
1632

1633
        if (exec_context_get_effective_mount_apivfs(c))
31,985✔
1634
                return true;
1635

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

1640
        return false;
1641
}
1642

1643
void exec_context_free_log_extra_fields(ExecContext *c) {
50,003✔
1644
        assert(c);
50,003✔
1645

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

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

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

1659
        assert(c);
2,509✔
1660

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1757
        assert(c);
1,093✔
1758
        assert(ret);
1,093✔
1759

1760
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
6,558✔
1761
                if (c->directories[t].n_items > 0)
5,465✔
1762
                        mask |= 1U << t;
331✔
1763

1764
        *ret = mask;
1,093✔
1765
        return 0;
1,093✔
1766
}
1767

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

1771
        assert(c);
1,113✔
1772

1773
        if (c->oom_score_adjust_set)
1,113✔
1774
                return c->oom_score_adjust;
332✔
1775

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

1780
        return n;
781✔
1781
}
1782

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

1788
        assert(c);
1,113✔
1789

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

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

1802
        return n;
1,113✔
1803
}
1804

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

1808
        assert(c);
1,113✔
1809

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

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

1820
        return n;
1821
}
1822

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

1826
        assert(c);
1,113✔
1827

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

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

1835
        return n < 0 ? SCHED_OTHER : n;
1,113✔
1836
}
1837

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

1842
        assert(c);
1,113✔
1843

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

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

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

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

1857
        assert(c);
1,113✔
1858

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

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

1866
        return (uint64_t) MAX(r, 0);
1,113✔
1867
}
1868

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

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

1875
        return c->user || c->dynamic_user || c->pam_name;
22,337✔
1876
}
1877

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

1881
        assert(c);
1,113✔
1882

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

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

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

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

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

1916
        strv_sort(l);
1,113✔
1917
#endif
1918

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

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

1925
        assert(c);
1,113✔
1926

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

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

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

1940
        strv_sort(l);
1,113✔
1941
#endif
1942

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

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

1949
        assert(c);
1,113✔
1950

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

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

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

1964
        strv_sort(l);
1,113✔
1965
#endif
1966

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

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

1974
        assert(c);
1,113✔
1975

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

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

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

1987
        strv_sort(l);
1,113✔
1988

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

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

1995
        assert(c);
1,113✔
1996

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

2002
        strv_sort(l);
1,113✔
2003
#endif
2004

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

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

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

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

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

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

2029
        dual_timestamp_now(&s->exit_timestamp);
3,558✔
2030

2031
        s->code = code;
3,558✔
2032
        s->status = status;
3,558✔
2033

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

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

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

2048
        s->handoff_timestamp = *ts;
13,380✔
2049
}
13,380✔
2050

2051
void exec_status_reset(ExecStatus *s) {
22,935✔
2052
        assert(s);
22,935✔
2053

2054
        *s = (ExecStatus) {};
22,935✔
2055
}
22,935✔
2056

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

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

2064
        prefix = strempty(prefix);
10✔
2065

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

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

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

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

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

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

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

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

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

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

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

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

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

2133
        prefix = strempty(prefix);
91✔
2134

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

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

2142
        assert(l);
20,920✔
2143
        assert(e);
20,920✔
2144

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

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

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

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

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

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

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

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

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

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

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

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

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

2197
        return 0;
2198
}
2199

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

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

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

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

2213
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
128,997✔
2214
        assert(rt);
128,997✔
2215

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

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

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

2230
        exec_shared_runtime_done(rt);
128,969✔
2231
        return mfree(rt);
128,969✔
2232
}
2233

2234
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
221✔
2235
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
131,955✔
2236

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

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

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

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

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

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

2257
        assert(ret);
128,969✔
2258

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

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

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

2273
        *ret = n;
128,969✔
2274
        return 0;
128,969✔
2275
}
2276

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

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

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

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

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

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

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

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

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

2316
        rt->manager = m;
220✔
2317

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

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

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

2335
        assert(m);
7,617✔
2336
        assert(c);
7,617✔
2337
        assert(id);
7,617✔
2338

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

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

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

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

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

2366
        return 1;
2367
}
2368

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

2373
        assert(m);
7,716✔
2374
        assert(id);
7,716✔
2375
        assert(ret);
7,716✔
2376

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2463
        return 0;
123✔
2464
}
2465

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

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

2476
        assert(u);
131,735✔
2477
        assert(key);
131,735✔
2478
        assert(value);
131,735✔
2479

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

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

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

2497
                rt = rt_create;
128,749✔
2498
        }
2499

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

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

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

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

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

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

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

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

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

2538
        return 1;
2539
}
2540

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2646
        assert(m);
1,282✔
2647

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

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

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

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

2669
        assert(unit);
7,716✔
2670
        assert(context);
7,716✔
2671
        assert(ret);
7,716✔
2672

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2792
        exec_params_shallow_clear(p);
28✔
2793
}
2794

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

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

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

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

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

2817
        return NULL;
2818
}
2819

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

2826
        assert(d);
6,610✔
2827
        assert(path);
6,610✔
2828

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

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

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

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

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

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

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

2859
        return 1; /* new item is added */
6,595✔
2860
}
2861

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

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

2869
void exec_directory_sort(ExecDirectory *d) {
143,359✔
2870
        assert(d);
143,359✔
2871

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

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

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

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

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

2893
        assert(s);
×
2894

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

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

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

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

2918
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
19,453✔
2919

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

2935
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
39,058✔
2936

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

2943
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
18,501✔
2944

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

2951
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
20,485✔
2952

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

2962
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
174,606✔
2963

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

2973
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
2974

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

2983
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
2984

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

2996
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
337✔
2997

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

3004
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
18,844✔
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