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

systemd / systemd / 20286914613

16 Dec 2025 11:25PM UTC coverage: 72.736% (+0.03%) from 72.703%
20286914613

push

github

web-flow
core/exec-credential: fix credentials plain dir exchanging (#40108)

Follow-ups for #39637

Split out from #40093

45 of 72 new or added lines in 3 files covered. (62.5%)

2104 existing lines in 43 files now uncovered.

309764 of 425877 relevant lines covered (72.74%)

1155086.73 hits per line

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

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

3
#include <fcntl.h>
4
#include <poll.h>
5
#include <sys/mman.h>
6
#include <sys/mount.h>
7
#include <sys/prctl.h>
8
#include <sys/socket.h>
9
#include <sys/stat.h>
10
#include <unistd.h>
11

12
#include "af-list.h"
13
#include "alloc-util.h"
14
#include "async.h"
15
#include "bitfield.h"
16
#include "capability-list.h"
17
#include "capability-util.h"
18
#include "cgroup-setup.h"
19
#include "coredump-util.h"
20
#include "cpu-set-util.h"
21
#include "dissect-image.h"
22
#include "dynamic-user.h"
23
#include "env-file.h"
24
#include "env-util.h"
25
#include "escape.h"
26
#include "execute.h"
27
#include "execute-serialize.h"
28
#include "fd-util.h"
29
#include "fdset.h"
30
#include "fileio.h"
31
#include "format-util.h"
32
#include "fs-util.h"
33
#include "glob-util.h"
34
#include "hexdecoct.h"
35
#include "image-policy.h"
36
#include "io-util.h"
37
#include "ioprio-util.h"
38
#include "log.h"
39
#include "manager.h"
40
#include "mkdir.h"
41
#include "namespace-util.h"
42
#include "namespace.h"
43
#include "nsflags.h"
44
#include "open-file.h"
45
#include "ordered-set.h"
46
#include "osc-context.h"
47
#include "parse-util.h"
48
#include "path-util.h"
49
#include "pidref.h"
50
#include "process-util.h"
51
#include "rlimit-util.h"
52
#include "rm-rf.h"
53
#include "seccomp-util.h"
54
#include "securebits-util.h"
55
#include "serialize.h"
56
#include "set.h"
57
#include "sort-util.h"
58
#include "string-table.h"
59
#include "string-util.h"
60
#include "strv.h"
61
#include "syslog-util.h"
62
#include "terminal-util.h"
63
#include "tmpfile-util.h"
64
#include "utmp-wtmp.h"
65
#include "vpick.h"
66

67
const char* exec_context_tty_path(const ExecContext *context) {
12,938✔
68
        assert(context);
12,938✔
69

70
        if (context->stdio_as_fds)
12,938✔
71
                return NULL;
72

73
        if (context->tty_path)
12,141✔
74
                return context->tty_path;
501✔
75

76
        return "/dev/console";
77
}
78

79
int exec_context_apply_tty_size(
1,038✔
80
                const ExecContext *context,
81
                int input_fd,
82
                int output_fd,
83
                const char *tty_path) {
84

85
        unsigned rows, cols;
1,038✔
86
        int r;
1,038✔
87

88
        assert(context);
1,038✔
89
        assert(input_fd >= 0);
1,038✔
90
        assert(output_fd >= 0);
1,038✔
91

92
        if (!isatty_safe(output_fd))
1,038✔
93
                return 0;
1,038✔
94

95
        if (!tty_path)
520✔
96
                tty_path = exec_context_tty_path(context);
262✔
97

98
        /* Preferably use explicitly configured data */
99
        rows = context->tty_rows;
520✔
100
        cols = context->tty_cols;
520✔
101

102
        /* Fill in data from kernel command line if anything is unspecified */
103
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
520✔
104
                (void) proc_cmdline_tty_size(
478✔
105
                                tty_path,
106
                                rows == UINT_MAX ? &rows : NULL,
107
                                cols == UINT_MAX ? &cols : NULL);
108

109
        /* If we got nothing so far and we are talking to a physical device, then let's query dimensions from
110
         * the ANSI terminal driver. Note that we will not bother with this in case terminal reset via ansi
111
         * sequences is not enabled, as the DSR logic relies on ANSI sequences after all, and if we shall not
112
         * use those during initialization we need to skip it. */
113
        if (rows == UINT_MAX && cols == UINT_MAX &&
660✔
114
            exec_context_shall_ansi_seq_reset(context) &&
242✔
115
            isatty_safe(input_fd)) {
102✔
116
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
102✔
117
                if (r < 0)
102✔
118
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
102✔
119
        }
120

121
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
520✔
122
}
123

124
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *parameters, sd_id128_t invocation_id) {
11,939✔
125
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
23,878✔
126
        int fd, r;
11,939✔
127

128
        assert(context);
11,939✔
129

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

135
        const char *path = exec_context_tty_path(context);
11,939✔
136

137
        if (parameters && parameters->stdout_fd >= 0 && isatty_safe(parameters->stdout_fd))
11,939✔
138
                fd = parameters->stdout_fd;
21✔
139
        else if (path && exec_context_has_tty(context)) {
11,918✔
140
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
258✔
141
                if (fd < 0)
258✔
142
                        return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path);
×
143
        } else
144
                return;   /* nothing to do */
145

146
        /* Take a synchronization lock for the duration of the setup that we do here.
147
         * systemd-vconsole-setup.service also takes the lock to avoid being interrupted. We open a new fd
148
         * that will be closed automatically, and operate on it for convenience. */
149
        lock_fd = lock_dev_console();
279✔
150
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
279✔
151
                log_debug_errno(lock_fd, "No privileges to lock /dev/console, proceeding without lock: %m");
×
152
        else if (ERRNO_IS_NEG_DEVICE_ABSENT(lock_fd))
279✔
153
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
154
        else if (lock_fd < 0)
279✔
155
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
156

157
        if (context->tty_reset)
279✔
158
                (void) terminal_reset_defensive(
220✔
159
                                fd,
160
                                TERMINAL_RESET_SWITCH_TO_TEXT |
161
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
217✔
162

163
        r = exec_context_apply_tty_size(context, fd, fd, path);
279✔
164
        if (r < 0)
279✔
165
                log_debug_errno(r, "Failed to configure TTY dimensions, ignoring: %m");
×
166

167
        if (!sd_id128_is_null(invocation_id) && exec_context_shall_ansi_seq_reset(context)) {
521✔
168
                sd_id128_t context_id;
23✔
169

170
                r = osc_context_id_from_invocation_id(invocation_id, &context_id);
23✔
171
                if (r < 0)
23✔
172
                        log_debug_errno(r, "Failed to derive context ID from invocation ID, ignoring: %m");
23✔
173
                else {
174
                        _cleanup_free_ char *seq = NULL;
23✔
175

176
                        r = osc_context_close(context_id, &seq);
23✔
177
                        if (r < 0)
23✔
178
                                log_debug_errno(r, "Failed to acquire OSC close sequence, ignoring: %m");
×
179
                        else
180
                                (void) loop_write(fd, seq, SIZE_MAX);
23✔
181
                }
182
        }
183

184
        if (context->tty_vhangup)
279✔
185
                (void) terminal_vhangup_fd(fd);
99✔
186

187
        /* We don't need the fd anymore now, and it potentially points to a hungup TTY anyway, let's close it
188
         * hence. */
189
        _fd = safe_close(_fd);
279✔
190

191
        if (context->tty_vt_disallocate && path)
279✔
192
                (void) vt_disallocate(path);
51✔
193
}
194

195
bool exec_needs_network_namespace(const ExecContext *context) {
61,250✔
196
        assert(context);
61,250✔
197

198
        return context->private_network || context->network_namespace_path;
61,250✔
199
}
200

201
static bool exec_needs_ephemeral(const ExecContext *context) {
6,593✔
202
        return (context->root_image || context->root_directory) && context->root_ephemeral;
6,593✔
203
}
204

205
bool exec_needs_ipc_namespace(const ExecContext *context) {
55,726✔
206
        assert(context);
55,726✔
207

208
        return context->private_ipc || context->ipc_namespace_path;
55,726✔
209
}
210

211
static bool needs_cgroup_namespace(ProtectControlGroups i) {
133,888✔
212
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
133,888✔
213
}
214

215
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
83,082✔
216
        assert(context);
83,082✔
217

218
        /* If cgroup namespace is configured via ProtectControlGroups=private or strict but we can't actually
219
         * use cgroup namespace, we ignore the setting and do not unshare the namespace.
220
         * ProtectControlGroups=private and strict get downgraded to no and yes respectively. This ensures
221
         * that strict always gets a read-only mount of /sys/fs/cgroup/. */
222
        if (needs_cgroup_namespace(context->protect_control_groups) && !namespace_type_supported(NAMESPACE_CGROUP)) {
83,082✔
223
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_PRIVATE)
×
224
                        return PROTECT_CONTROL_GROUPS_NO;
225
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_STRICT)
×
226
                        return PROTECT_CONTROL_GROUPS_YES;
227
        }
228
        return context->protect_control_groups;
83,082✔
229
}
230

231
bool exec_needs_cgroup_namespace(const ExecContext *context) {
50,806✔
232
        assert(context);
50,806✔
233

234
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
50,806✔
235
}
236

237
bool exec_needs_cgroup_mount(const ExecContext *context) {
28,117✔
238
        assert(context);
28,117✔
239

240
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
28,117✔
241
}
242

243
bool exec_is_cgroup_mount_read_only(const ExecContext *context) {
2,080✔
244
        assert(context);
2,080✔
245

246
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
2,080✔
247
}
248

249
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
90,480✔
250
        assert(context);
90,480✔
251

252
        /* PID namespaces don't really make sense for control processes so let's not use them for those. */
253
        if (params && FLAGS_SET(params->flags, EXEC_IS_CONTROL))
90,480✔
254
                return false;
255

256
        return context->private_pids != PRIVATE_PIDS_NO && namespace_type_supported(NAMESPACE_PID);
81,039✔
257
}
258

259
bool exec_needs_mount_namespace(
33,267✔
260
                const ExecContext *context,
261
                const ExecParameters *params,
262
                const ExecRuntime *runtime) {
263

264
        assert(context);
33,267✔
265

266
        if (context->root_image)
33,267✔
267
                return true;
268

269
        if (context->root_directory_as_fd)
33,217✔
270
                return true;
271

272
        if (!strv_isempty(context->read_write_paths) ||
33,211✔
273
            !strv_isempty(context->read_only_paths) ||
30,987✔
274
            !strv_isempty(context->inaccessible_paths) ||
30,974✔
275
            !strv_isempty(context->exec_paths) ||
30,955✔
276
            !strv_isempty(context->no_exec_paths))
30,955✔
277
                return true;
278

279
        if (context->n_bind_mounts > 0)
30,955✔
280
                return true;
281

282
        if (context->n_temporary_filesystems > 0)
30,904✔
283
                return true;
284

285
        if (context->n_mount_images > 0)
30,745✔
286
                return true;
287

288
        if (context->n_extension_images > 0)
30,722✔
289
                return true;
290

291
        if (!strv_isempty(context->extension_directories))
30,692✔
292
                return true;
293

294
        if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
30,687✔
295
                return true;
296

297
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
30,687✔
298
                return true;
299

300
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
30,022✔
301
                return true;
302

303
        if (context->private_devices ||
29,496✔
304
            context->private_mounts > 0 ||
29,121✔
305
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,609✔
306
            context->protect_system != PROTECT_SYSTEM_NO ||
28,580✔
307
            context->protect_home != PROTECT_HOME_NO ||
28,580✔
308
            context->protect_kernel_tunables ||
28,580✔
309
            context->protect_kernel_modules ||
28,580✔
310
            context->protect_kernel_logs ||
53,549✔
311
            exec_needs_cgroup_mount(context) ||
26,773✔
312
            context->protect_proc != PROTECT_PROC_DEFAULT ||
26,755✔
313
            context->proc_subset != PROC_SUBSET_ALL ||
26,687✔
314
            context->private_bpf != PRIVATE_BPF_NO ||
53,356✔
315
            exec_needs_ipc_namespace(context) ||
53,356✔
316
            exec_needs_pid_namespace(context, params))
26,678✔
317
                return true;
2,863✔
318

319
        if (context->root_directory) {
26,633✔
320
                if (exec_context_get_effective_mount_apivfs(context))
5✔
321
                        return true;
322

323
                for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
324
                        if (params && !params->prefix[t])
×
325
                                continue;
×
326

327
                        if (context->directories[t].n_items > 0)
×
328
                                return true;
329
                }
330
        }
331

332
        if (context->dynamic_user &&
26,628✔
333
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
334
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
335
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
336
                return true;
337

338
        if (exec_context_get_effective_bind_log_sockets(context))
26,628✔
339
                return true;
340

341
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
159,424✔
342
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
137,839✔
343
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,037✔
344
                                return true;
345

346
        return false;
347
}
348

349
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
4,022✔
350
        assert(context);
4,022✔
351
        assert(params);
4,022✔
352

353
        if (!params->notify_socket)
4,022✔
354
                return NULL;
355

356
        if (!needs_sandboxing)
3,314✔
357
                return NULL;
358

359
        if (!context->root_directory && !context->root_image && !context->root_directory_as_fd)
3,314✔
360
                return NULL;
361

362
        if (!exec_context_get_effective_mount_apivfs(context))
×
363
                return NULL;
364

365
        if (!FLAGS_SET(params->flags, EXEC_APPLY_CHROOT))
×
366
                return NULL;
×
367

368
        return "/run/host/notify";
369
}
370

371
int exec_log_level_max_with_exec_params(const ExecContext *context, const ExecParameters *params) {
20,440✔
372
        assert(params);
20,440✔
373

374
        if (params->debug_invocation)
20,440✔
375
                return LOG_DEBUG;
376

377
        return exec_log_level_max(context);
20,436✔
378
}
379

380
int exec_log_level_max(const ExecContext *context) {
20,482✔
381
        assert(context);
20,482✔
382
        return context->log_level_max < 0 ? log_get_max_level() : context->log_level_max;
20,482✔
383
}
384

385
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
10,386✔
386
        assert(context);
10,386✔
387

388
        if (!context->dynamic_user)
10,386✔
389
                return false;
390

391
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
106✔
392
                return false;
393

394
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
100✔
395
                return false;
16✔
396

397
        return true;
398
}
399

400
int exec_params_needs_control_subcgroup(const ExecParameters *params) {
2,564✔
401
        /* Keep this in sync with exec_params_get_cgroup_path(). */
402
        return FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE|EXEC_CONTROL_CGROUP|EXEC_IS_CONTROL);
2,564✔
403
}
404

405
int exec_params_get_cgroup_path(
10,942✔
406
                const ExecParameters *params,
407
                const CGroupContext *c,
408
                const char *prefix,
409
                char **ret) {
410

411
        const char *subgroup = NULL;
10,942✔
412
        char *p;
10,942✔
413

414
        assert(params);
10,942✔
415
        assert(c);
10,942✔
416
        assert(ret);
10,942✔
417

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

427
        /* Keep this in sync with exec_params_needs_control_subcgroup(). */
428
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
10,942✔
429
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
732✔
430
                        subgroup = ".control";
431
                else
432
                        subgroup = c->delegate_subgroup;
686✔
433
        }
434

435
        if (subgroup)
686✔
436
                p = path_join(prefix, subgroup);
732✔
437
        else
438
                p = strdup(strempty(prefix));
10,218✔
439
        if (!p)
10,942✔
440
                return -ENOMEM;
441

442
        *ret = p;
10,942✔
443
        return !!subgroup;
10,942✔
444
}
445

446
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
2,839✔
447
        assert(c);
2,839✔
448

449
        return c->cpu_affinity_from_numa;
2,839✔
450
}
451

452
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
2,534✔
453
        assert(unit);
2,534✔
454
        assert(msg);
2,534✔
455
        assert(executable);
2,534✔
456

457
        if (!DEBUG_LOGGING)
2,534✔
458
                return;
2,534✔
459

460
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
5,068✔
461

462
        log_unit_struct(unit, LOG_DEBUG,
2,534✔
463
                        LOG_ITEM("EXECUTABLE=%s", executable),
464
                        LOG_UNIT_MESSAGE(unit, "%s: %s", msg, strnull(cmdline)),
465
                        LOG_UNIT_INVOCATION_ID(unit));
466
}
467

468
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret);
469

470
int exec_spawn(
2,534✔
471
                Unit *unit,
472
                ExecCommand *command,
473
                const ExecContext *context,
474
                ExecParameters *params,
475
                ExecRuntime *runtime,
476
                const CGroupContext *cgroup_context,
477
                PidRef *ret) {
478

479
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL;
2,534✔
480
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
481
        _cleanup_fclose_ FILE *f = NULL;
2,534✔
482
        int r;
2,534✔
483

484
        assert(unit);
2,534✔
485
        assert(unit->manager);
2,534✔
486
        assert(unit->manager->executor_fd >= 0);
2,534✔
487
        assert(unit->manager->executor_path);
2,534✔
488
        assert(command);
2,534✔
489
        assert(context);
2,534✔
490
        assert(params);
2,534✔
491
        assert(params->fds || (params->n_socket_fds + params->n_stashed_fds == 0 && !params->fd_names));
2,534✔
492
        assert(params->n_stashed_fds == 0 || FLAGS_SET(params->flags, EXEC_PASS_FDS));
2,534✔
493
        assert(!params->files_env); /* We fill this field, ensure it comes NULL-initialized to us */
2,534✔
494
        assert(ret);
2,534✔
495

496
        LOG_CONTEXT_PUSH_UNIT(unit);
5,068✔
497

498
        r = exec_context_load_environment(unit, context, &params->files_env);
2,534✔
499
        if (r < 0)
2,534✔
500
                return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
×
501

502
        /* We won't know the real executable path until we create the mount namespace in the child, but we
503
           want to log from the parent, so we use the possibly inaccurate path here. */
504
        log_command_line(unit, "About to execute", command->path, command->argv);
2,534✔
505

506
        /* We cannot spawn the main service process into the subcgroup as it might need to unshare the cgroup
507
         * namespace first if one is configured to make sure the root of the cgroup namespace is the service
508
         * cgroup and not the subcgroup. However, when running control commands on a live service, the
509
         * commands have to be spawned inside a subcgroup, otherwise we violate the no inner processes rule
510
         * of cgroupv2 as the main service process might already have enabled controllers by writing to
511
         * cgroup.subtree_control. */
512

513
        const char *cgtarget;
2,534✔
514
        if (exec_params_needs_control_subcgroup(params)) {
2,534✔
515
                r = exec_params_get_cgroup_path(params, cgroup_context, params->cgroup_path, &subcgroup_path);
4✔
516
                if (r < 0)
4✔
517
                        return log_unit_error_errno(unit, r, "Failed to acquire subcgroup path: %m");
×
518
                if (r > 0) {
4✔
519
                        /* If there's a subcgroup, then let's create it here now (the main cgroup was already
520
                         * realized by the unit logic) */
521

522
                        r = cg_create(subcgroup_path);
4✔
523
                        if (r < 0)
4✔
524
                                return log_unit_error_errno(unit, r, "Failed to create subcgroup '%s': %m", subcgroup_path);
×
525
                }
526

527
                cgtarget = subcgroup_path;
4✔
528
        } else
529
                cgtarget = params->cgroup_path;
2,530✔
530

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

538
        r = open_serialization_file("sd-executor-state", &f);
2,534✔
539
        if (r < 0)
2,534✔
540
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
541

542
        fdset = fdset_new();
2,534✔
543
        if (!fdset)
2,534✔
544
                return log_oom();
×
545

546
        r = exec_serialize_invocation(f, fdset, context, command, params, runtime, cgroup_context);
2,534✔
547
        if (r < 0)
2,534✔
548
                return log_unit_error_errno(unit, r, "Failed to serialize parameters: %m");
×
549

550
        r = finish_serialization_file(f);
2,534✔
551
        if (r < 0)
2,534✔
552
                return log_unit_error_errno(unit, r, "Failed to finish serialization stream: %m");
×
553

554
        r = fd_cloexec(fileno(f), false);
2,534✔
555
        if (r < 0)
2,534✔
556
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m");
×
557

558
        r = fdset_cloexec(fdset, false);
2,534✔
559
        if (r < 0)
2,534✔
560
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m");
×
561

562
        /* If LogLevelMax= is specified, then let's use the specified log level at the beginning of the
563
         * executor process. To achieve that the specified log level is passed as an argument, rather than
564
         * the one for the manager process. */
565
        r = log_max_levels_to_string(context->log_level_max >= 0 ? context->log_level_max : log_get_max_level(), &max_log_levels);
2,534✔
566
        if (r < 0)
2,534✔
567
                return log_unit_error_errno(unit, r, "Failed to convert max log levels to string: %m");
×
568

569
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,534✔
570
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,534✔
571

572
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,534✔
573
        dual_timestamp start_timestamp;
2,534✔
574

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

581
        /* Record the start timestamp before we fork so that it is guaranteed to be earlier than the
582
         * handoff timestamp. */
583
        dual_timestamp_now(&start_timestamp);
2,534✔
584

585
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
586
        r = posix_spawn_wrapper(
2,534✔
587
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
2,534✔
588
                        STRV_MAKE(unit->manager->executor_path,
2,534✔
589
                                  "--deserialize", serialization_fd_number,
590
                                  "--log-level", max_log_levels,
591
                                  "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))),
592
                        environ,
593
                        cgtarget,
594
                        &pidref);
595

596
        /* Drop the ambient set again, so no processes other than sd-executore spawned from the manager inherit it. */
597
        (void) capability_ambient_set_apply(0, /* also_inherit= */ false);
2,534✔
598

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

613
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
2,534✔
614
                       command->path, pidref.pid, r > 0 ? "via" : "without");
615

616
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,534✔
617

618
        *ret = TAKE_PIDREF(pidref);
2,534✔
619
        return 0;
2,534✔
620
}
621

622
void exec_context_init(ExecContext *c) {
62,933✔
623
        assert(c);
62,933✔
624

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

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

655
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
377,598✔
656
                d->mode = 0755;
314,665✔
657

658
        numa_policy_reset(&c->numa_policy);
62,933✔
659

660
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
62,933✔
661
}
62,933✔
662

663
void exec_context_done(ExecContext *c) {
52,746✔
664
        assert(c);
52,746✔
665

666
        c->environment = strv_free(c->environment);
52,746✔
667
        c->environment_files = strv_free(c->environment_files);
52,746✔
668
        c->pass_environment = strv_free(c->pass_environment);
52,746✔
669
        c->unset_environment = strv_free(c->unset_environment);
52,746✔
670

671
        rlimit_free_all(c->rlimit);
52,746✔
672

673
        for (size_t l = 0; l < 3; l++) {
210,984✔
674
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
158,238✔
675
                c->stdio_file[l] = mfree(c->stdio_file[l]);
158,238✔
676
        }
677

678
        c->working_directory = mfree(c->working_directory);
52,746✔
679
        c->root_directory = mfree(c->root_directory);
52,746✔
680
        c->root_image = mfree(c->root_image);
52,746✔
681
        c->root_image_options = mount_options_free_all(c->root_image_options);
52,746✔
682
        iovec_done(&c->root_hash);
52,746✔
683
        c->root_hash_path = mfree(c->root_hash_path);
52,746✔
684
        iovec_done(&c->root_hash_sig);
52,746✔
685
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
52,746✔
686
        c->root_verity = mfree(c->root_verity);
52,746✔
687
        c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
52,746✔
688
        c->extension_directories = strv_free(c->extension_directories);
52,746✔
689
        c->tty_path = mfree(c->tty_path);
52,746✔
690
        c->syslog_identifier = mfree(c->syslog_identifier);
52,746✔
691
        c->user = mfree(c->user);
52,746✔
692
        c->group = mfree(c->group);
52,746✔
693

694
        c->supplementary_groups = strv_free(c->supplementary_groups);
52,746✔
695

696
        c->pam_name = mfree(c->pam_name);
52,746✔
697

698
        c->read_only_paths = strv_free(c->read_only_paths);
52,746✔
699
        c->read_write_paths = strv_free(c->read_write_paths);
52,746✔
700
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
52,746✔
701
        c->exec_paths = strv_free(c->exec_paths);
52,746✔
702
        c->no_exec_paths = strv_free(c->no_exec_paths);
52,746✔
703
        c->exec_search_path = strv_free(c->exec_search_path);
52,746✔
704

705
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
52,746✔
706
        c->bind_mounts = NULL;
52,746✔
707
        c->n_bind_mounts = 0;
52,746✔
708
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
52,746✔
709
        c->temporary_filesystems = NULL;
52,746✔
710
        c->n_temporary_filesystems = 0;
52,746✔
711
        c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
52,746✔
712

713
        cpu_set_done(&c->cpu_set);
52,746✔
714
        numa_policy_reset(&c->numa_policy);
52,746✔
715

716
        c->utmp_id = mfree(c->utmp_id);
52,746✔
717
        c->selinux_context = mfree(c->selinux_context);
52,746✔
718
        c->apparmor_profile = mfree(c->apparmor_profile);
52,746✔
719
        c->smack_process_label = mfree(c->smack_process_label);
52,746✔
720

721
        c->restrict_filesystems = set_free(c->restrict_filesystems);
52,746✔
722

723
        c->syscall_filter = hashmap_free(c->syscall_filter);
52,746✔
724
        c->syscall_archs = set_free(c->syscall_archs);
52,746✔
725
        c->syscall_log = hashmap_free(c->syscall_log);
52,746✔
726
        c->address_families = set_free(c->address_families);
52,746✔
727

728
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
316,476✔
729
                exec_directory_done(d);
263,730✔
730

731
        c->log_level_max = -1;
52,746✔
732

733
        exec_context_free_log_extra_fields(c);
52,746✔
734
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
52,746✔
735
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
52,746✔
736

737
        c->log_ratelimit = (RateLimit) {};
52,746✔
738

739
        c->stdin_data = mfree(c->stdin_data);
52,746✔
740
        c->stdin_data_size = 0;
52,746✔
741

742
        c->user_namespace_path = mfree(c->user_namespace_path);
52,746✔
743
        c->network_namespace_path = mfree(c->network_namespace_path);
52,746✔
744
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
52,746✔
745

746
        c->log_namespace = mfree(c->log_namespace);
52,746✔
747

748
        c->load_credentials = hashmap_free(c->load_credentials);
52,746✔
749
        c->set_credentials = hashmap_free(c->set_credentials);
52,746✔
750
        c->import_credentials = ordered_set_free(c->import_credentials);
52,746✔
751

752
        c->root_image_policy = image_policy_free(c->root_image_policy);
52,746✔
753
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
52,746✔
754
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
52,746✔
755

756
        c->private_hostname = mfree(c->private_hostname);
52,746✔
757
}
52,746✔
758

759
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
6,282✔
760
        assert(c);
6,282✔
761

762
        if (!runtime_prefix)
6,282✔
763
                return 0;
764

765
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
6,286✔
766
                _cleanup_free_ char *p = NULL;
4✔
767

768
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
4✔
769
                        p = path_join(runtime_prefix, "private", i->path);
×
770
                else
771
                        p = path_join(runtime_prefix, i->path);
4✔
772
                if (!p)
4✔
773
                        return -ENOMEM;
774

775
                /* We execute this synchronously, since we need to be sure this is gone when we start the
776
                 * service next. */
777
                (void) rm_rf(p, REMOVE_ROOT);
4✔
778

779
                STRV_FOREACH(symlink, i->symlinks) {
4✔
780
                        _cleanup_free_ char *symlink_abs = NULL;
×
781

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

789
                        (void) unlink(symlink_abs);
×
790
                }
791
        }
792

793
        return 0;
794
}
795

796
int exec_context_destroy_mount_ns_dir(Unit *u) {
12,929✔
797
        _cleanup_free_ char *p = NULL;
12,929✔
798

799
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
12,929✔
800
                return 0;
801

802
        p = path_join("/run/systemd/propagate/", u->id);
2,671✔
803
        if (!p)
2,671✔
804
                return -ENOMEM;
805

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

810
        return 0;
811
}
812

813
void exec_command_done(ExecCommand *c) {
110,920✔
814
        assert(c);
110,920✔
815

816
        c->path = mfree(c->path);
110,920✔
817
        c->argv = strv_free(c->argv);
110,920✔
818
}
110,920✔
819

820
void exec_command_done_array(ExecCommand *c, size_t n) {
30,947✔
821
        FOREACH_ARRAY(i, c, n)
123,786✔
822
                exec_command_done(i);
92,839✔
823
}
30,947✔
824

825
ExecCommand* exec_command_free(ExecCommand *c) {
18,049✔
826
        if (!c)
18,049✔
827
                return NULL;
828

829
        exec_command_done(c);
18,049✔
830
        return mfree(c);
18,049✔
831
}
832

833
ExecCommand* exec_command_free_list(ExecCommand *c) {
153,868✔
834
        ExecCommand *i;
153,868✔
835

836
        while ((i = LIST_POP(command, c)))
171,917✔
837
                exec_command_free(i);
18,049✔
838

839
        return NULL;
153,868✔
840
}
841

842
void exec_command_free_array(ExecCommand **c, size_t n) {
21,767✔
843
        FOREACH_ARRAY(i, c, n)
175,605✔
844
                *i = exec_command_free_list(*i);
153,838✔
845
}
21,767✔
846

847
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
7,665✔
848
        FOREACH_ARRAY(i, c, n)
30,659✔
849
                exec_status_reset(&i->exec_status);
22,994✔
850
}
7,665✔
851

852
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
5,528✔
853
        FOREACH_ARRAY(i, c, n)
39,690✔
854
                LIST_FOREACH(command, z, *i)
36,616✔
855
                        exec_status_reset(&z->exec_status);
2,454✔
856
}
5,528✔
857

858
typedef struct InvalidEnvInfo {
859
        const Unit *unit;
860
        const char *path;
861
} InvalidEnvInfo;
862

863
static void invalid_env(const char *p, void *userdata) {
×
864
        InvalidEnvInfo *info = userdata;
×
865

866
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
867
}
×
868

869
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
39,177✔
870
        assert(c);
39,177✔
871

872
        switch (fd_index) {
39,177✔
873

874
        case STDIN_FILENO:
13,059✔
875
                if (c->std_input != EXEC_INPUT_NAMED_FD)
13,059✔
876
                        return NULL;
877

878
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
879

880
        case STDOUT_FILENO:
13,059✔
881
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
13,059✔
882
                        return NULL;
883

884
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
885

886
        case STDERR_FILENO:
13,059✔
887
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
13,059✔
888
                        return NULL;
889

890
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
891

892
        default:
893
                return NULL;
894
        }
895
}
896

897
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
2,534✔
898
        _cleanup_strv_free_ char **v = NULL;
2,534✔
899
        int r;
2,534✔
900

901
        assert(c);
2,534✔
902
        assert(ret);
2,534✔
903

904
        STRV_FOREACH(i, c->environment_files) {
2,537✔
905
                _cleanup_strv_free_ char **paths = NULL;
3✔
906
                bool ignore = false;
3✔
907
                char *fn = *i;
3✔
908

909
                if (fn[0] == '-') {
3✔
910
                        ignore = true;
2✔
911
                        fn++;
2✔
912
                }
913

914
                if (!path_is_absolute(fn)) {
3✔
915
                        if (ignore)
×
916
                                continue;
×
917
                        return -EINVAL;
918
                }
919

920
                /* Filename supports globbing, take all matching files */
921
                r = safe_glob(fn, /* flags= */ 0, &paths);
3✔
922
                if (r < 0) {
3✔
923
                        if (ignore)
2✔
924
                                continue;
2✔
925
                        return r;
926
                }
927

928
                /* When we don't match anything, -ENOENT should be returned */
929
                assert(!strv_isempty(paths));
1✔
930

931
                STRV_FOREACH(path, paths) {
2✔
932
                        _cleanup_strv_free_ char **p = NULL;
1✔
933

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

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

948
                                strv_env_clean_with_callback(p, invalid_env, &info);
1✔
949
                        }
950

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

958
                                strv_free_and_replace(v, m);
×
959
                        }
960
                }
961
        }
962

963
        *ret = TAKE_PTR(v);
2,534✔
964

965
        return 0;
2,534✔
966
}
967

968
static bool tty_may_match_dev_console(const char *tty) {
255✔
969
        _cleanup_free_ char *resolved = NULL;
255✔
970

971
        if (!tty)
255✔
972
                return true;
973

974
        tty = skip_dev_prefix(tty);
255✔
975

976
        /* trivial identity? */
977
        if (streq(tty, "console"))
255✔
978
                return true;
979

980
        if (resolve_dev_console(&resolved) < 0)
45✔
981
                return true; /* if we could not resolve, assume it may */
982

983
        /* "tty0" means the active VC, so it may be the same sometimes */
984
        return path_equal(skip_dev_prefix(resolved), tty) || (streq(skip_dev_prefix(resolved), "tty0") && tty_is_vc(tty));
45✔
985
}
986

987
static bool exec_context_may_touch_tty(const ExecContext *ec) {
26,271✔
988
        assert(ec);
26,271✔
989

990
        return ec->tty_reset ||
52,299✔
991
                ec->tty_vhangup ||
26,028✔
992
                ec->tty_vt_disallocate ||
26,028✔
993
                exec_input_is_terminal(ec->std_input) ||
26,028✔
994
                ec->std_output == EXEC_OUTPUT_TTY ||
52,299✔
995
                ec->std_error == EXEC_OUTPUT_TTY;
25,979✔
996
}
997

998
bool exec_context_may_touch_console(const ExecContext *ec) {
24,551✔
999

1000
        return exec_context_may_touch_tty(ec) &&
24,806✔
1001
               tty_may_match_dev_console(exec_context_tty_path(ec));
255✔
1002
}
1003

1004
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,153✔
1005
        assert(c);
1,153✔
1006

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

1014
        if (!c->tty_reset)
1,153✔
1015
                return false;
1016

1017
        /* FIXME:
1018
         * On invocation, we generate $TERM based on settings for StandardOutput= and friends and the kernel
1019
         * command line options, or propagate $TERM from the service manager. See setup_term_environment(). */
1020
        return !streq_ptr(strv_env_get(c->environment, "TERM"), "dumb");
542✔
1021
}
1022

1023
static void strv_fprintf(FILE *f, char **l) {
2✔
1024
        assert(f);
2✔
1025

1026
        STRV_FOREACH(g, l)
6✔
1027
                fprintf(f, " %s", *g);
4✔
1028
}
2✔
1029

1030
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
3,088✔
1031
        assert(f);
3,088✔
1032
        assert(prefix);
3,088✔
1033
        assert(name);
3,088✔
1034

1035
        if (!strv_isempty(strv)) {
3,088✔
1036
                fprintf(f, "%s%s:", prefix, name);
2✔
1037
                strv_fprintf(f, strv);
2✔
1038
                fputs("\n", f);
2✔
1039
        }
1040
}
3,088✔
1041

1042
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1043
        assert(p);
×
1044
        assert(f);
×
1045

1046
        prefix = strempty(prefix);
×
1047

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

1074
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1075
        strv_dump(f, prefix, "Environment", p->environment);
×
1076
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1077

1078
        LIST_FOREACH(open_files, file, p->open_files)
×
1079
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1080

1081
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1082
}
×
1083

1084
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
386✔
1085
        int r;
386✔
1086

1087
        assert(c);
386✔
1088
        assert(f);
386✔
1089

1090
        prefix = strempty(prefix);
386✔
1091

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

1150
        if (c->private_bpf == PRIVATE_BPF_YES) {
386✔
1151
                _cleanup_free_ char
×
1152
                        *commands = bpf_delegate_commands_to_string(c->bpf_delegate_commands),
×
1153
                        *maps = bpf_delegate_maps_to_string(c->bpf_delegate_maps),
×
1154
                        *programs = bpf_delegate_programs_to_string(c->bpf_delegate_programs),
×
1155
                        *attachments = bpf_delegate_attachments_to_string(c->bpf_delegate_attachments);
×
1156

1157
                fprintf(f, "%sBPFDelegateCommands: %s\n", prefix, strna(commands));
×
1158
                fprintf(f, "%sBPFDelegateMaps: %s\n", prefix, strna(maps));
×
1159
                fprintf(f, "%sBPFDelegatePrograms: %s\n", prefix, strna(programs));
×
1160
                fprintf(f, "%sBPFDelegateAttachments: %s\n", prefix, strna(attachments));
×
1161
        }
1162

1163
        if (c->set_login_environment >= 0)
386✔
1164
                fprintf(f, "%sSetLoginEnvironment: %s\n", prefix, yes_no(c->set_login_environment > 0));
2✔
1165

1166
        if (c->root_image)
386✔
1167
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1168

1169
        if (c->root_image_options) {
386✔
1170
                fprintf(f, "%sRootImageOptions:", prefix);
×
1171
                LIST_FOREACH(mount_options, o, c->root_image_options)
×
1172
                        if (!isempty(o->options))
×
1173
                                fprintf(f, " %s:%s",
×
1174
                                        partition_designator_to_string(o->partition_designator),
1175
                                        o->options);
1176
                fprintf(f, "\n");
×
1177
        }
1178

1179
        if (iovec_is_set(&c->root_hash)) {
1180
                _cleanup_free_ char *encoded = NULL;
×
1181
                encoded = hexmem(c->root_hash.iov_base, c->root_hash.iov_len);
×
1182
                if (encoded)
×
1183
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1184
        }
1185

1186
        if (c->root_hash_path)
386✔
1187
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1188

1189
        if (iovec_is_set(&c->root_hash_sig)) {
1190
                _cleanup_free_ char *encoded = NULL;
×
1191
                ssize_t len;
×
1192
                len = base64mem(c->root_hash_sig.iov_base, c->root_hash_sig.iov_len, &encoded);
×
1193
                if (len)
×
1194
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1195
        }
1196

1197
        if (c->root_hash_sig_path)
386✔
1198
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1199

1200
        if (c->root_verity)
386✔
1201
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1202

1203
        STRV_FOREACH(e, c->environment)
392✔
1204
                fprintf(f, "%sEnvironment: %s\n", prefix, *e);
6✔
1205

1206
        STRV_FOREACH(e, c->environment_files)
386✔
1207
                fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
×
1208

1209
        STRV_FOREACH(e, c->pass_environment)
398✔
1210
                fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
12✔
1211

1212
        STRV_FOREACH(e, c->unset_environment)
401✔
1213
                fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
15✔
1214

1215
        fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
386✔
1216

1217
        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
2,316✔
1218
                fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode);
1,930✔
1219

1220
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
1,953✔
1221
                        fprintf(f,
23✔
1222
                                "%s%s: %s%s\n",
1223
                                prefix,
1224
                                exec_directory_type_to_string(dt),
1225
                                c->directories[dt].items[i].path,
1226
                                FLAGS_SET(c->directories[dt].items[i].flags, EXEC_DIRECTORY_READ_ONLY) ? " (ro)" : "");
23✔
1227

1228
                        STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
23✔
1229
                                fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
×
1230
                }
1231
        }
1232

1233
        fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC));
386✔
1234

1235
        if (c->memory_ksm >= 0)
386✔
1236
                fprintf(f, "%sMemoryKSM: %s\n", prefix, yes_no(c->memory_ksm > 0));
2✔
1237

1238
        if (c->nice_set)
386✔
1239
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
2✔
1240

1241
        if (c->oom_score_adjust_set)
386✔
1242
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
16✔
1243

1244
        if (c->coredump_filter_set)
386✔
1245
                fprintf(f, "%sCoredumpFilter: 0x%"PRIx64"\n", prefix, c->coredump_filter);
×
1246

1247
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
6,562✔
1248
                if (c->rlimit[i]) {
6,176✔
1249
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
348✔
1250
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1251
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
348✔
1252
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
348✔
1253
                }
1254

1255
        if (c->ioprio_is_set) {
386✔
1256
                _cleanup_free_ char *class_str = NULL;
3✔
1257

1258
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
3✔
1259
                if (r >= 0)
3✔
1260
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
3✔
1261

1262
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
3✔
1263
        }
1264

1265
        if (c->cpu_sched_set) {
386✔
1266
                _cleanup_free_ char *policy_str = NULL;
×
1267

1268
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1269
                if (r >= 0)
×
1270
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1271

1272
                fprintf(f,
×
1273
                        "%sCPUSchedulingPriority: %i\n"
1274
                        "%sCPUSchedulingResetOnFork: %s\n",
1275
                        prefix, c->cpu_sched_priority,
×
1276
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1277
        }
1278

1279
        if (c->cpu_set.set) {
386✔
1280
                _cleanup_free_ char *affinity = NULL;
×
1281

1282
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1283
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1284
        }
1285

1286
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
386✔
1287
                _cleanup_free_ char *nodes = NULL;
1✔
1288

1289
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1290
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1291
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1292
        }
1293

1294
        if (c->timer_slack_nsec != NSEC_INFINITY)
386✔
1295
                fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
1✔
1296

1297
        fprintf(f,
386✔
1298
                "%sStandardInput: %s\n"
1299
                "%sStandardOutput: %s\n"
1300
                "%sStandardError: %s\n",
1301
                prefix, exec_input_to_string(c->std_input),
386✔
1302
                prefix, exec_output_to_string(c->std_output),
386✔
1303
                prefix, exec_output_to_string(c->std_error));
386✔
1304

1305
        if (c->std_input == EXEC_INPUT_NAMED_FD)
386✔
1306
                fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
×
1307
        if (c->std_output == EXEC_OUTPUT_NAMED_FD)
386✔
1308
                fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
×
1309
        if (c->std_error == EXEC_OUTPUT_NAMED_FD)
386✔
1310
                fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
×
1311

1312
        if (c->std_input == EXEC_INPUT_FILE)
386✔
1313
                fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
×
1314
        if (c->std_output == EXEC_OUTPUT_FILE)
386✔
1315
                fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1316
        if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
386✔
1317
                fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1318
        if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE)
386✔
1319
                fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1320
        if (c->std_error == EXEC_OUTPUT_FILE)
386✔
1321
                fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1322
        if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
386✔
1323
                fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1324
        if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
386✔
1325
                fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1326

1327
        if (c->tty_path)
386✔
1328
                fprintf(f,
2✔
1329
                        "%sTTYPath: %s\n"
1330
                        "%sTTYReset: %s\n"
1331
                        "%sTTYVHangup: %s\n"
1332
                        "%sTTYVTDisallocate: %s\n"
1333
                        "%sTTYRows: %u\n"
1334
                        "%sTTYColumns: %u\n",
1335
                        prefix, c->tty_path,
1336
                        prefix, yes_no(c->tty_reset),
2✔
1337
                        prefix, yes_no(c->tty_vhangup),
2✔
1338
                        prefix, yes_no(c->tty_vt_disallocate),
2✔
1339
                        prefix, c->tty_rows,
2✔
1340
                        prefix, c->tty_cols);
2✔
1341

1342
        if (IN_SET(c->std_output,
386✔
1343
                   EXEC_OUTPUT_KMSG,
1344
                   EXEC_OUTPUT_JOURNAL,
1345
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1346
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1347
            IN_SET(c->std_error,
24✔
1348
                   EXEC_OUTPUT_KMSG,
1349
                   EXEC_OUTPUT_JOURNAL,
1350
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1351
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1352

1353
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
362✔
1354

1355
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
362✔
1356
                if (r >= 0)
362✔
1357
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
362✔
1358

1359
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
362✔
1360
                if (r >= 0)
362✔
1361
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
362✔
1362
        }
1363

1364
        if (c->log_level_max >= 0) {
386✔
1365
                _cleanup_free_ char *t = NULL;
1✔
1366

1367
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1368

1369
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1370
        }
1371

1372
        if (c->log_ratelimit.interval > 0)
386✔
1373
                fprintf(f,
×
1374
                        "%sLogRateLimitIntervalSec: %s\n",
1375
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1376

1377
        if (c->log_ratelimit.burst > 0)
386✔
1378
                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit.burst);
×
1379

1380
        if (!set_isempty(c->log_filter_allowed_patterns) || !set_isempty(c->log_filter_denied_patterns)) {
386✔
1381
                fprintf(f, "%sLogFilterPatterns:", prefix);
×
1382

1383
                char *pattern;
×
1384
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1385
                        fprintf(f, " %s", pattern);
×
1386
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1387
                        fprintf(f, " ~%s", pattern);
×
1388
                fputc('\n', f);
×
1389
        }
1390

1391
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
390✔
1392
                fprintf(f, "%sLogExtraFields: ", prefix);
4✔
1393
                fwrite(field->iov_base, 1, field->iov_len, f);
4✔
1394
                fputc('\n', f);
4✔
1395
        }
1396

1397
        if (c->log_namespace)
386✔
1398
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1399

1400
        if (c->secure_bits) {
386✔
1401
                _cleanup_free_ char *str = NULL;
×
1402

1403
                r = secure_bits_to_string_alloc(c->secure_bits, &str);
×
1404
                if (r >= 0)
×
1405
                        fprintf(f, "%sSecure Bits: %s\n", prefix, str);
×
1406
        }
1407

1408
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
386✔
1409
                _cleanup_free_ char *str = NULL;
386✔
1410

1411
                r = capability_set_to_string(c->capability_bounding_set, &str);
386✔
1412
                if (r >= 0)
386✔
1413
                        fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
386✔
1414
        }
1415

1416
        if (c->capability_ambient_set != 0) {
386✔
1417
                _cleanup_free_ char *str = NULL;
3✔
1418

1419
                r = capability_set_to_string(c->capability_ambient_set, &str);
3✔
1420
                if (r >= 0)
3✔
1421
                        fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
3✔
1422
        }
1423

1424
        if (c->user)
386✔
1425
                fprintf(f, "%sUser: %s\n", prefix, c->user);
7✔
1426
        if (c->group)
386✔
1427
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1428

1429
        fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
771✔
1430

1431
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
386✔
1432

1433
        if (c->pam_name)
386✔
1434
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
1✔
1435

1436
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
386✔
1437
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
386✔
1438
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
386✔
1439
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
386✔
1440
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
386✔
1441
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
386✔
1442

1443
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
390✔
1444
                fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
4✔
1445
                        mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
4✔
1446
                        mount->ignore_enoent ? "-": "",
4✔
1447
                        mount->source,
1448
                        mount->destination,
1449
                        mount->recursive ? "rbind" : "norbind");
4✔
1450

1451
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
386✔
1452
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1453
                        tmpfs->path,
1454
                        isempty(tmpfs->options) ? "" : ":",
×
1455
                        strempty(tmpfs->options));
×
1456

1457
        if (c->utmp_id)
386✔
1458
                fprintf(f,
2✔
1459
                        "%sUtmpIdentifier: %s\n",
1460
                        prefix, c->utmp_id);
1461

1462
        if (c->selinux_context)
386✔
1463
                fprintf(f,
×
1464
                        "%sSELinuxContext: %s%s\n",
1465
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1466

1467
        if (c->apparmor_profile)
386✔
1468
                fprintf(f,
×
1469
                        "%sAppArmorProfile: %s%s\n",
1470
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1471

1472
        if (c->smack_process_label)
386✔
1473
                fprintf(f,
×
1474
                        "%sSmackProcessLabel: %s%s\n",
1475
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1476

1477
        if (c->personality != PERSONALITY_INVALID)
386✔
1478
                fprintf(f,
1✔
1479
                        "%sPersonality: %s\n",
1480
                        prefix, strna(personality_to_string(c->personality)));
1481

1482
        fprintf(f,
386✔
1483
                "%sLockPersonality: %s\n",
1484
                prefix, yes_no(c->lock_personality));
386✔
1485

1486
        if (c->syscall_filter) {
386✔
1487
                fprintf(f,
24✔
1488
                        "%sSystemCallFilter: ",
1489
                        prefix);
1490

1491
                if (!c->syscall_allow_list)
24✔
1492
                        fputc('~', f);
×
1493

1494
#if HAVE_SECCOMP
1495
                if (dlopen_libseccomp() >= 0) {
24✔
1496
                        void *id, *val;
24✔
1497
                        bool first = true;
24✔
1498
                        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
9,356✔
1499
                                _cleanup_free_ char *name = NULL;
9,332✔
1500
                                const char *errno_name = NULL;
9,332✔
1501
                                int num = PTR_TO_INT(val);
9,332✔
1502

1503
                                if (first)
9,332✔
1504
                                        first = false;
1505
                                else
1506
                                        fputc(' ', f);
9,308✔
1507

1508
                                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
9,332✔
1509
                                fputs(strna(name), f);
9,332✔
1510

1511
                                if (num >= 0) {
9,332✔
1512
                                        errno_name = seccomp_errno_or_action_to_string(num);
×
1513
                                        if (errno_name)
×
1514
                                                fprintf(f, ":%s", errno_name);
×
1515
                                        else
1516
                                                fprintf(f, ":%d", num);
×
1517
                                }
1518
                        }
1519
                }
1520
#endif
1521

1522
                fputc('\n', f);
24✔
1523
        }
1524

1525
        if (c->syscall_archs) {
386✔
1526
                fprintf(f,
26✔
1527
                        "%sSystemCallArchitectures:",
1528
                        prefix);
1529

1530
#if HAVE_SECCOMP
1531
                void *id;
26✔
1532
                SET_FOREACH(id, c->syscall_archs)
52✔
1533
                        fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
26✔
1534
#endif
1535
                fputc('\n', f);
26✔
1536
        }
1537

1538
        if (exec_context_restrict_namespaces_set(c)) {
386✔
1539
                _cleanup_free_ char *s = NULL;
22✔
1540

1541
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
22✔
1542
                if (r >= 0)
22✔
1543
                        fprintf(f, "%sRestrictNamespaces: %s\n",
22✔
1544
                                prefix, strna(s));
1545
        }
1546

1547
#if HAVE_LIBBPF
1548
        if (exec_context_restrict_filesystems_set(c)) {
386✔
1549
                char *fs;
×
1550
                SET_FOREACH(fs, c->restrict_filesystems)
×
1551
                        fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
×
1552
        }
1553
#endif
1554

1555
        if (c->user_namespace_path)
386✔
1556
                fprintf(f,
×
1557
                        "%sUserNamespacePath: %s\n",
1558
                        prefix, c->user_namespace_path);
1559

1560
        if (c->network_namespace_path)
386✔
1561
                fprintf(f,
×
1562
                        "%sNetworkNamespacePath: %s\n",
1563
                        prefix, c->network_namespace_path);
1564

1565
        if (c->syscall_errno > 0) {
386✔
1566
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
385✔
1567

1568
#if HAVE_SECCOMP
1569
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
385✔
1570
                if (errno_name)
385✔
1571
                        fputs(errno_name, f);
385✔
1572
                else
1573
                        fprintf(f, "%d", c->syscall_errno);
×
1574
#endif
1575
                fputc('\n', f);
385✔
1576
        }
1577

1578
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
386✔
1579
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1580
                        mount->ignore_enoent ? "-": "",
×
1581
                        mount->source,
1582
                        mount->destination);
1583
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1584
                        fprintf(f, ":%s:%s",
×
1585
                                partition_designator_to_string(o->partition_designator),
1586
                                strempty(o->options));
×
1587
                fprintf(f, "\n");
×
1588
        }
1589

1590
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
386✔
1591
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1592
                        mount->ignore_enoent ? "-": "",
×
1593
                        mount->source);
1594
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1595
                        fprintf(f, ":%s:%s",
×
1596
                                partition_designator_to_string(o->partition_designator),
1597
                                strempty(o->options));
×
1598
                fprintf(f, "\n");
×
1599
        }
1600

1601
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
386✔
1602
}
386✔
1603

1604
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1605
        assert(c);
×
1606

1607
        /* Returns true if the process forked off would run under
1608
         * an unchanged UID or as root. */
1609

1610
        if (!c->user)
×
1611
                return true;
1612

1613
        if (STR_IN_SET(c->user, "root", "0"))
×
1614
                return true;
×
1615

1616
        return false;
×
1617
}
1618

1619
int exec_context_get_effective_ioprio(const ExecContext *c) {
5,678✔
1620
        int p;
5,678✔
1621

1622
        assert(c);
5,678✔
1623

1624
        if (c->ioprio_is_set)
5,678✔
1625
                return c->ioprio;
14✔
1626

1627
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
5,664✔
1628
        if (p < 0)
5,664✔
1629
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1630

1631
        return ioprio_normalize(p);
5,664✔
1632
}
1633

1634
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
39,310✔
1635
        assert(c);
39,310✔
1636

1637
        /* Explicit setting wins */
1638
        if (c->mount_apivfs >= 0)
39,310✔
1639
                return c->mount_apivfs > 0;
119✔
1640

1641
        /* Default to "yes" if root directory or image are specified */
1642
        if (exec_context_with_rootfs(c))
39,191✔
1643
                return true;
122✔
1644

1645
        return false;
1646
}
1647

1648
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
31,932✔
1649
        assert(c);
31,932✔
1650

1651
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1652
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1653
        if (c->log_namespace)
31,932✔
1654
                return true;
1655

1656
        if (c->bind_log_sockets >= 0)
31,924✔
1657
                return c->bind_log_sockets > 0;
2✔
1658

1659
        if (exec_context_get_effective_mount_apivfs(c))
31,922✔
1660
                return true;
1661

1662
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1663
        if (exec_context_with_rootfs(c) && c->private_devices)
31,823✔
1664
                return true;
×
1665

1666
        return false;
1667
}
1668

1669
void exec_context_free_log_extra_fields(ExecContext *c) {
52,748✔
1670
        assert(c);
52,748✔
1671

1672
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
52,999✔
1673
                free(field->iov_base);
251✔
1674

1675
        c->log_extra_fields = mfree(c->log_extra_fields);
52,748✔
1676
        c->n_log_extra_fields = 0;
52,748✔
1677
}
52,748✔
1678

1679
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,720✔
1680
        _cleanup_close_ int fd = -EBADF;
1,720✔
1681
        const char *path;
1,720✔
1682
        struct stat st;
1,720✔
1683
        int r;
1,720✔
1684

1685
        assert(c);
1,720✔
1686

1687
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1688
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,720✔
1689

1690
        /* And then undo what chown_terminal() did earlier. Note that we only do this if we have a path
1691
         * configured. If the TTY was passed to us as file descriptor we assume the TTY is opened and managed
1692
         * by whoever passed it to us and thus knows better when and how to chmod()/chown() it back. */
1693
        if (!exec_context_may_touch_tty(c))
1,720✔
1694
                return;
1695

1696
        path = exec_context_tty_path(c);
37✔
1697
        if (!path)
37✔
1698
                return;
1699

1700
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
37✔
1701
        if (fd < 0)
37✔
1702
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1703
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1704
                                             path);
1705

1706
        if (fstat(fd, &st) < 0)
37✔
1707
                return (void) log_warning_errno(errno, "Failed to stat TTY '%s', ignoring: %m", path);
×
1708

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

1717
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
37✔
1718
        if (r < 0)
37✔
1719
                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);
37✔
1720
}
1721

1722
int exec_context_get_clean_directories(
1✔
1723
                ExecContext *c,
1724
                char **prefix,
1725
                ExecCleanMask mask,
1726
                char ***ret) {
1727

1728
        _cleanup_strv_free_ char **l = NULL;
1✔
1729
        int r;
1✔
1730

1731
        assert(c);
1✔
1732
        assert(prefix);
1✔
1733
        assert(ret);
1✔
1734

1735
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
6✔
1736
                if (!BIT_SET(mask, t))
5✔
1737
                        continue;
×
1738

1739
                if (!prefix[t])
5✔
1740
                        continue;
×
1741

1742
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
7✔
1743
                        char *j;
2✔
1744

1745
                        j = path_join(prefix[t], i->path);
2✔
1746
                        if (!j)
2✔
1747
                                return -ENOMEM;
1748

1749
                        r = strv_consume(&l, j);
2✔
1750
                        if (r < 0)
2✔
1751
                                return r;
1752

1753
                        /* Also remove private directories unconditionally. */
1754
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
2✔
1755
                                j = path_join(prefix[t], "private", i->path);
2✔
1756
                                if (!j)
2✔
1757
                                        return -ENOMEM;
1758

1759
                                r = strv_consume(&l, j);
2✔
1760
                                if (r < 0)
2✔
1761
                                        return r;
1762
                        }
1763

1764
                        STRV_FOREACH(symlink, i->symlinks) {
2✔
1765
                                j = path_join(prefix[t], *symlink);
×
1766
                                if (!j)
×
1767
                                        return -ENOMEM;
1768

1769
                                r = strv_consume(&l, j);
×
1770
                                if (r < 0)
×
1771
                                        return r;
1772
                        }
1773
                }
1774
        }
1775

1776
        *ret = TAKE_PTR(l);
1✔
1777
        return 0;
1✔
1778
}
1779

1780
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
2,823✔
1781
        ExecCleanMask mask = 0;
2,823✔
1782

1783
        assert(c);
2,823✔
1784
        assert(ret);
2,823✔
1785

1786
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
16,938✔
1787
                if (c->directories[t].n_items > 0)
14,115✔
1788
                        mask |= 1U << t;
273✔
1789

1790
        *ret = mask;
2,823✔
1791
        return 0;
2,823✔
1792
}
1793

1794
int exec_context_get_oom_score_adjust(const ExecContext *c) {
2,839✔
1795
        int n = 0, r;
2,839✔
1796

1797
        assert(c);
2,839✔
1798

1799
        if (c->oom_score_adjust_set)
2,839✔
1800
                return c->oom_score_adjust;
595✔
1801

1802
        r = get_oom_score_adjust(&n);
2,244✔
1803
        if (r < 0)
2,244✔
1804
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1805

1806
        return n;
2,244✔
1807
}
1808

1809
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
2,839✔
1810
        _cleanup_free_ char *t = NULL;
2,839✔
1811
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
2,839✔
1812
        int r;
2,839✔
1813

1814
        assert(c);
2,839✔
1815

1816
        if (c->coredump_filter_set)
2,839✔
1817
                return c->coredump_filter;
×
1818

1819
        r = read_one_line_file("/proc/self/coredump_filter", &t);
2,839✔
1820
        if (r < 0)
2,839✔
1821
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1822
        else {
1823
                r = safe_atoux64(t, &n);
2,839✔
1824
                if (r < 0)
2,839✔
1825
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1826
        }
1827

1828
        return n;
2,839✔
1829
}
1830

1831
int exec_context_get_nice(const ExecContext *c) {
2,839✔
1832
        int n;
2,839✔
1833

1834
        assert(c);
2,839✔
1835

1836
        if (c->nice_set)
2,839✔
1837
                return c->nice;
8✔
1838

1839
        errno = 0;
2,831✔
1840
        n = getpriority(PRIO_PROCESS, 0);
2,831✔
1841
        if (errno > 0) {
2,831✔
1842
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1843
                n = 0;
1844
        }
1845

1846
        return n;
1847
}
1848

1849
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
2,839✔
1850
        int n;
2,839✔
1851

1852
        assert(c);
2,839✔
1853

1854
        if (c->cpu_sched_set)
2,839✔
1855
                return c->cpu_sched_policy;
×
1856

1857
        n = sched_getscheduler(0);
2,839✔
1858
        if (n < 0)
2,839✔
1859
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1860

1861
        return n < 0 ? SCHED_OTHER : n;
2,839✔
1862
}
1863

1864
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
2,839✔
1865
        struct sched_param p = {};
2,839✔
1866
        int r;
2,839✔
1867

1868
        assert(c);
2,839✔
1869

1870
        if (c->cpu_sched_set)
2,839✔
1871
                return c->cpu_sched_priority;
×
1872

1873
        r = sched_getparam(0, &p);
2,839✔
1874
        if (r < 0)
2,839✔
1875
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1876

1877
        return r >= 0 ? p.sched_priority : 0;
2,839✔
1878
}
1879

1880
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
2,839✔
1881
        int r;
2,839✔
1882

1883
        assert(c);
2,839✔
1884

1885
        if (c->timer_slack_nsec != NSEC_INFINITY)
2,839✔
1886
                return c->timer_slack_nsec;
1887

1888
        r = prctl(PR_GET_TIMERSLACK);
2,839✔
1889
        if (r < 0)
2,839✔
1890
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1891

1892
        return (uint64_t) MAX(r, 0);
2,839✔
1893
}
1894

1895
bool exec_context_get_set_login_environment(const ExecContext *c) {
13,007✔
1896
        assert(c);
13,007✔
1897

1898
        if (c->set_login_environment >= 0)
13,007✔
1899
                return c->set_login_environment;
×
1900

1901
        return c->user || c->dynamic_user || c->pam_name;
22,491✔
1902
}
1903

1904
char** exec_context_get_syscall_filter(const ExecContext *c) {
2,839✔
1905
        _cleanup_strv_free_ char **l = NULL;
2,839✔
1906

1907
        assert(c);
2,839✔
1908

1909
#if HAVE_SECCOMP
1910
        if (dlopen_libseccomp() < 0)
2,839✔
1911
                return strv_new(NULL);
×
1912

1913
        void *id, *val;
2,839✔
1914
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
23,544✔
1915
                _cleanup_free_ char *name = NULL;
20,705✔
1916
                const char *e = NULL;
20,705✔
1917
                char *s;
20,705✔
1918
                int num = PTR_TO_INT(val);
20,705✔
1919

1920
                if (c->syscall_allow_list && num >= 0)
20,705✔
1921
                        /* syscall with num >= 0 in allow-list is denied. */
1922
                        continue;
×
1923

1924
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
20,705✔
1925
                if (!name)
20,705✔
1926
                        continue;
×
1927

1928
                if (num >= 0) {
20,705✔
1929
                        e = seccomp_errno_or_action_to_string(num);
×
1930
                        if (e) {
×
1931
                                s = strjoin(name, ":", e);
×
1932
                                if (!s)
×
1933
                                        return NULL;
1934
                        } else {
1935
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1936
                                        return NULL;
1937
                        }
1938
                } else
1939
                        s = TAKE_PTR(name);
20,705✔
1940

1941
                if (strv_consume(&l, s) < 0)
20,705✔
1942
                        return NULL;
1943
        }
1944

1945
        strv_sort(l);
2,839✔
1946
#endif
1947

1948
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,839✔
1949
}
1950

1951
char** exec_context_get_syscall_archs(const ExecContext *c) {
2,839✔
1952
        _cleanup_strv_free_ char **l = NULL;
2,839✔
1953

1954
        assert(c);
2,839✔
1955

1956
#if HAVE_SECCOMP
1957
        void *id;
2,839✔
1958
        SET_FOREACH(id, c->syscall_archs) {
2,896✔
1959
                const char *name;
57✔
1960

1961
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
57✔
1962
                if (!name)
57✔
1963
                        continue;
×
1964

1965
                if (strv_extend(&l, name) < 0)
57✔
1966
                        return NULL;
×
1967
        }
1968

1969
        strv_sort(l);
2,839✔
1970
#endif
1971

1972
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,839✔
1973
}
1974

1975
char** exec_context_get_syscall_log(const ExecContext *c) {
2,839✔
1976
        _cleanup_strv_free_ char **l = NULL;
2,839✔
1977

1978
        assert(c);
2,839✔
1979

1980
#if HAVE_SECCOMP
1981
        if (dlopen_libseccomp() < 0)
2,839✔
1982
                return strv_new(NULL);
×
1983

1984
        void *id, *val;
2,839✔
1985
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
2,839✔
1986
                char *name = NULL;
×
1987

1988
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1989
                if (!name)
×
1990
                        continue;
×
1991

1992
                if (strv_consume(&l, name) < 0)
×
1993
                        return NULL;
×
1994
        }
1995

1996
        strv_sort(l);
2,839✔
1997
#endif
1998

1999
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,839✔
2000
}
2001

2002
char** exec_context_get_address_families(const ExecContext *c) {
2,839✔
2003
        _cleanup_strv_free_ char **l = NULL;
2,839✔
2004
        void *af;
2,839✔
2005

2006
        assert(c);
2,839✔
2007

2008
        SET_FOREACH(af, c->address_families) {
3,063✔
2009
                const char *name;
224✔
2010

2011
                name = af_to_name(PTR_TO_INT(af));
224✔
2012
                if (!name)
224✔
2013
                        continue;
×
2014

2015
                if (strv_extend(&l, name) < 0)
224✔
2016
                        return NULL;
×
2017
        }
2018

2019
        strv_sort(l);
2,839✔
2020

2021
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,839✔
2022
}
2023

2024
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
2,839✔
2025
        assert(c);
2,839✔
2026

2027
#if HAVE_LIBBPF
2028
        char **l = set_get_strv(c->restrict_filesystems);
2,839✔
2029
        if (!l)
2,839✔
2030
                return NULL;
2031

2032
        return strv_sort(l);
2,839✔
2033
#else
2034
        return strv_new(NULL);
2035
#endif
2036
}
2037

2038
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
13,836✔
2039
        assert(c);
13,836✔
2040

2041
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
13,836✔
2042
}
2043

2044
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
15,637✔
2045
        assert(c);
15,637✔
2046

2047
        return c->restrict_filesystems_allow_list ||
15,637✔
2048
          !set_isempty(c->restrict_filesystems);
15,637✔
2049
}
2050

2051
bool exec_context_with_rootfs(const ExecContext *c) {
71,304✔
2052
        assert(c);
71,304✔
2053

2054
        /* Checks if RootDirectory=, RootImage= or RootDirectoryFileDescriptor= are used */
2055

2056
        return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd;
71,304✔
2057
}
2058

2059
int exec_context_has_vpicked_extensions(const ExecContext *context) {
4✔
2060
        int r;
4✔
2061

2062
        assert(context);
4✔
2063

2064
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
4✔
2065
                r = path_uses_vpick(mi->source);
×
2066
                if (r != 0)
×
2067
                        return r;
2068
        }
2069
        STRV_FOREACH(ed, context->extension_directories) {
4✔
2070
                r = path_uses_vpick(*ed);
×
2071
                if (r != 0)
×
2072
                        return r;
2073
        }
2074

2075
        return 0;
2076
}
2077

2078
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
5,078✔
2079
        assert(s);
5,078✔
2080

2081
        *s = (ExecStatus) {
5,078✔
2082
                .pid = pid,
2083
        };
2084

2085
        if (ts)
5,078✔
2086
                s->start_timestamp = *ts;
5,078✔
2087
        else
2088
                dual_timestamp_now(&s->start_timestamp);
×
2089
}
5,078✔
2090

2091
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,345✔
2092
        assert(s);
2,345✔
2093

2094
        if (s->pid != pid)
2,345✔
2095
                *s = (ExecStatus) {
7✔
2096
                        .pid = pid,
2097
                };
2098

2099
        dual_timestamp_now(&s->exit_timestamp);
2,345✔
2100

2101
        s->code = code;
2,345✔
2102
        s->status = status;
2,345✔
2103

2104
        if (context && context->utmp_id)
2,345✔
2105
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
15✔
2106
}
2,345✔
2107

2108
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
8,662✔
2109
        assert(s);
8,662✔
2110
        assert(ucred);
8,662✔
2111
        assert(ts);
8,662✔
2112

2113
        if (ucred->pid != s->pid)
8,662✔
2114
                *s = (ExecStatus) {
8✔
2115
                        .pid = ucred->pid,
2116
                };
2117

2118
        s->handoff_timestamp = *ts;
8,662✔
2119
}
8,662✔
2120

2121
void exec_status_reset(ExecStatus *s) {
27,622✔
2122
        assert(s);
27,622✔
2123

2124
        *s = (ExecStatus) {};
27,622✔
2125
}
27,622✔
2126

2127
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
225✔
2128
        assert(s);
225✔
2129
        assert(f);
225✔
2130

2131
        if (s->pid <= 0)
225✔
2132
                return;
2133

2134
        prefix = strempty(prefix);
43✔
2135

2136
        fprintf(f,
43✔
2137
                "%sPID: "PID_FMT"\n",
2138
                prefix, s->pid);
2139

2140
        if (dual_timestamp_is_set(&s->start_timestamp))
43✔
2141
                fprintf(f,
43✔
2142
                        "%sStart Timestamp: %s\n",
2143
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
43✔
2144

2145
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
43✔
2146
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
42✔
2147
                fprintf(f,
42✔
2148
                        "%sHandoff Timestamp: %s since start\n",
2149
                        prefix,
2150
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
84✔
2151
        else
2152
                fprintf(f,
1✔
2153
                        "%sHandoff Timestamp: %s\n",
2154
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
1✔
2155

2156
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
43✔
2157

2158
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
21✔
2159
                        fprintf(f,
21✔
2160
                                "%sExit Timestamp: %s since handoff\n",
2161
                                prefix,
2162
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
42✔
2163
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2164
                        fprintf(f,
×
2165
                                "%sExit Timestamp: %s since start\n",
2166
                                prefix,
2167
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2168
                else
2169
                        fprintf(f,
×
2170
                                "%sExit Timestamp: %s\n",
2171
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2172

2173
                fprintf(f,
21✔
2174
                        "%sExit Code: %s\n"
2175
                        "%sExit Status: %i\n",
2176
                        prefix, sigchld_code_to_string(s->code),
21✔
2177
                        prefix, s->status);
21✔
2178
        }
2179
}
2180

2181
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
225✔
2182
        _cleanup_free_ char *cmd = NULL;
450✔
2183
        const char *prefix2;
225✔
2184

2185
        assert(c);
225✔
2186
        assert(f);
225✔
2187

2188
        prefix = strempty(prefix);
225✔
2189
        prefix2 = strjoina(prefix, "\t");
1,125✔
2190

2191
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
225✔
2192

2193
        fprintf(f,
225✔
2194
                "%sCommand Line: %s\n",
2195
                prefix, strnull(cmd));
2196

2197
        exec_status_dump(&c->exec_status, f, prefix2);
225✔
2198
}
225✔
2199

2200
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
220✔
2201
        assert(f);
220✔
2202

2203
        prefix = strempty(prefix);
220✔
2204

2205
        LIST_FOREACH(command, i, c)
444✔
2206
                exec_command_dump(i, f, prefix);
224✔
2207
}
220✔
2208

2209
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
18,049✔
2210
        ExecCommand *end;
18,049✔
2211

2212
        assert(l);
18,049✔
2213
        assert(e);
18,049✔
2214

2215
        if (*l) {
18,049✔
2216
                /* It's kind of important, that we keep the order here */
2217
                end = LIST_FIND_TAIL(command, *l);
373✔
2218
                LIST_INSERT_AFTER(command, *l, end, e);
136✔
2219
        } else
2220
                *l = e;
17,913✔
2221
}
18,049✔
2222

2223
int exec_command_set(ExecCommand *c, const char *path, ...) {
213✔
2224
        va_list ap;
213✔
2225
        char **l, *p;
213✔
2226

2227
        assert(c);
213✔
2228
        assert(path);
213✔
2229

2230
        va_start(ap, path);
213✔
2231
        l = strv_new_ap(path, ap);
213✔
2232
        va_end(ap);
213✔
2233

2234
        if (!l)
213✔
2235
                return -ENOMEM;
213✔
2236

2237
        p = strdup(path);
213✔
2238
        if (!p) {
213✔
2239
                strv_free(l);
×
2240
                return -ENOMEM;
×
2241
        }
2242

2243
        free_and_replace(c->path, p);
213✔
2244

2245
        return strv_free_and_replace(c->argv, l);
213✔
2246
}
2247

2248
int exec_command_append(ExecCommand *c, const char *path, ...) {
308✔
2249
        char **l;
308✔
2250
        va_list ap;
308✔
2251
        int r;
308✔
2252

2253
        assert(c);
308✔
2254
        assert(path);
308✔
2255

2256
        va_start(ap, path);
308✔
2257
        l = strv_new_ap(path, ap);
308✔
2258
        va_end(ap);
308✔
2259

2260
        if (!l)
308✔
2261
                return -ENOMEM;
308✔
2262

2263
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates= */ false);
308✔
2264
        if (r < 0)
308✔
2265
                return r;
×
2266

2267
        return 0;
2268
}
2269

2270
static char *destroy_tree(char *path) {
283✔
2271
        if (!path)
283✔
2272
                return NULL;
2273

2274
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
96✔
2275
                log_debug("Spawning process to nuke '%s'", path);
96✔
2276

2277
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
96✔
2278
        }
2279

2280
        return mfree(path);
96✔
2281
}
2282

2283
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
113,734✔
2284
        assert(rt);
113,734✔
2285

2286
        if (rt->manager)
113,734✔
2287
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
175✔
2288

2289
        rt->id = mfree(rt->id);
113,734✔
2290
        rt->tmp_dir = mfree(rt->tmp_dir);
113,734✔
2291
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
113,734✔
2292
        safe_close_pair(rt->userns_storage_socket);
113,734✔
2293
        safe_close_pair(rt->netns_storage_socket);
113,734✔
2294
        safe_close_pair(rt->ipcns_storage_socket);
113,734✔
2295
}
113,734✔
2296

2297
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
113,702✔
2298
        if (!rt)
113,702✔
2299
                return NULL;
2300

2301
        exec_shared_runtime_done(rt);
113,702✔
2302
        return mfree(rt);
113,702✔
2303
}
2304

2305
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
177✔
2306
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
116,769✔
2307

2308
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
55✔
2309
        if (!rt)
55✔
2310
                return NULL;
2311

2312
        assert(rt->n_ref > 0);
53✔
2313
        rt->n_ref--;
53✔
2314

2315
        if (rt->n_ref > 0)
53✔
2316
                return NULL;
2317

2318
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
53✔
2319
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
53✔
2320

2321
        return exec_shared_runtime_free(rt);
53✔
2322
}
2323

2324
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
113,702✔
2325
        _cleanup_free_ char *id_copy = NULL;
227,404✔
2326
        ExecSharedRuntime *n;
113,702✔
2327

2328
        assert(ret);
113,702✔
2329

2330
        id_copy = strdup(id);
113,702✔
2331
        if (!id_copy)
113,702✔
2332
                return -ENOMEM;
2333

2334
        n = new(ExecSharedRuntime, 1);
113,702✔
2335
        if (!n)
113,702✔
2336
                return -ENOMEM;
2337

2338
        *n = (ExecSharedRuntime) {
113,702✔
2339
                .id = TAKE_PTR(id_copy),
113,702✔
2340
                .userns_storage_socket = EBADF_PAIR,
2341
                .netns_storage_socket = EBADF_PAIR,
2342
                .ipcns_storage_socket = EBADF_PAIR,
2343
        };
2344

2345
        *ret = n;
113,702✔
2346
        return 0;
113,702✔
2347
}
2348

2349
static int exec_shared_runtime_add(
175✔
2350
                Manager *m,
2351
                const char *id,
2352
                char **tmp_dir,
2353
                char **var_tmp_dir,
2354
                int userns_storage_socket[2],
2355
                int netns_storage_socket[2],
2356
                int ipcns_storage_socket[2],
2357
                ExecSharedRuntime **ret) {
2358

2359
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
175✔
2360
        int r;
175✔
2361

2362
        assert(m);
175✔
2363
        assert(id);
175✔
2364

2365
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2366

2367
        r = exec_shared_runtime_allocate(&rt, id);
175✔
2368
        if (r < 0)
175✔
2369
                return r;
2370

2371
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
175✔
2372
        if (r < 0)
175✔
2373
                return r;
2374

2375
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
175✔
2376
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
175✔
2377
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
175✔
2378

2379
        if (userns_storage_socket) {
175✔
2380
                rt->userns_storage_socket[0] = TAKE_FD(userns_storage_socket[0]);
175✔
2381
                rt->userns_storage_socket[1] = TAKE_FD(userns_storage_socket[1]);
175✔
2382
        }
2383

2384
        if (netns_storage_socket) {
175✔
2385
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
175✔
2386
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
175✔
2387
        }
2388

2389
        if (ipcns_storage_socket) {
175✔
2390
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
175✔
2391
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
175✔
2392
        }
2393

2394
        rt->manager = m;
175✔
2395

2396
        if (ret)
175✔
2397
                *ret = rt;
77✔
2398
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2399
        TAKE_PTR(rt);
175✔
2400
        return 0;
175✔
2401
}
2402

2403
static int exec_shared_runtime_make(
6,495✔
2404
                Manager *m,
2405
                const ExecContext *c,
2406
                const char *id,
2407
                ExecSharedRuntime **ret) {
2408

2409
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
6,495✔
2410
        _cleanup_close_pair_ int userns_storage_socket[2] = EBADF_PAIR, netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
19,485✔
2411
        int r;
6,495✔
2412

2413
        assert(m);
6,495✔
2414
        assert(c);
6,495✔
2415
        assert(id);
6,495✔
2416

2417
        /* It is not necessary to create ExecSharedRuntime object. */
2418
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
6,495✔
2419
                *ret = NULL;
6,418✔
2420
                return 0;
6,418✔
2421
        }
2422

2423
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
149✔
2424
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
72✔
2425
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
2426
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2427
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
72✔
2428
                if (r < 0)
72✔
2429
                        return r;
2430
        }
2431

2432
        if (c->user_namespace_path)
77✔
2433
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, userns_storage_socket) < 0)
×
2434
                        return -errno;
×
2435

2436
        if (exec_needs_network_namespace(c))
77✔
2437
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
7✔
2438
                        return -errno;
×
2439

2440
        if (exec_needs_ipc_namespace(c))
77✔
2441
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
2442
                        return -errno;
×
2443

2444
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_storage_socket, netns_storage_socket, ipcns_storage_socket, ret);
77✔
2445
        if (r < 0)
77✔
2446
                return r;
×
2447

2448
        return 1;
2449
}
2450

2451
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
6,593✔
2452
        ExecSharedRuntime *rt;
6,593✔
2453
        int r;
6,593✔
2454

2455
        assert(m);
6,593✔
2456
        assert(id);
6,593✔
2457
        assert(ret);
6,593✔
2458

2459
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
6,593✔
2460
        if (rt)
6,593✔
2461
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2462
                goto ref;
98✔
2463

2464
        if (!create) {
6,495✔
2465
                *ret = NULL;
×
2466
                return 0;
×
2467
        }
2468

2469
        /* If not found, then create a new object. */
2470
        r = exec_shared_runtime_make(m, c, id, &rt);
6,495✔
2471
        if (r < 0)
6,495✔
2472
                return r;
2473
        if (r == 0) {
6,495✔
2474
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2475
                *ret = NULL;
6,418✔
2476
                return 0;
6,418✔
2477
        }
2478

2479
ref:
77✔
2480
        /* increment reference counter. */
2481
        rt->n_ref++;
175✔
2482
        *ret = rt;
175✔
2483
        return 1;
175✔
2484
}
2485

2486
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
72✔
2487
        ExecSharedRuntime *rt;
72✔
2488

2489
        assert(m);
72✔
2490
        assert(f);
72✔
2491
        assert(fds);
72✔
2492

2493
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
194✔
2494
                fprintf(f, "exec-runtime=%s", rt->id);
122✔
2495

2496
                if (rt->tmp_dir)
122✔
2497
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
122✔
2498

2499
                if (rt->var_tmp_dir)
122✔
2500
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
122✔
2501

2502
                if (rt->userns_storage_socket[0] >= 0) {
122✔
2503
                        int copy;
×
2504

2505
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[0]);
×
2506
                        if (copy < 0)
×
2507
                                return copy;
×
2508

2509
                        fprintf(f, " userns-socket-0=%i", copy);
×
2510
                }
2511

2512
                if (rt->userns_storage_socket[1] >= 0) {
122✔
2513
                        int copy;
×
2514

2515
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[1]);
×
2516
                        if (copy < 0)
×
2517
                                return copy;
2518

2519
                        fprintf(f, " userns-socket-1=%i", copy);
×
2520
                }
2521

2522
                if (rt->netns_storage_socket[0] >= 0) {
122✔
2523
                        int copy;
4✔
2524

2525
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
4✔
2526
                        if (copy < 0)
4✔
2527
                                return copy;
2528

2529
                        fprintf(f, " netns-socket-0=%i", copy);
4✔
2530
                }
2531

2532
                if (rt->netns_storage_socket[1] >= 0) {
122✔
2533
                        int copy;
4✔
2534

2535
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
4✔
2536
                        if (copy < 0)
4✔
2537
                                return copy;
2538

2539
                        fprintf(f, " netns-socket-1=%i", copy);
4✔
2540
                }
2541

2542
                if (rt->ipcns_storage_socket[0] >= 0) {
122✔
2543
                        int copy;
×
2544

2545
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2546
                        if (copy < 0)
×
2547
                                return copy;
2548

2549
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2550
                }
2551

2552
                if (rt->ipcns_storage_socket[1] >= 0) {
122✔
2553
                        int copy;
×
2554

2555
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2556
                        if (copy < 0)
×
2557
                                return copy;
2558

2559
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2560
                }
2561

2562
                fputc('\n', f);
122✔
2563
        }
2564

2565
        return 0;
72✔
2566
}
2567

2568
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
116,594✔
2569
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
116,594✔
2570
        ExecSharedRuntime *rt = NULL;
116,594✔
2571
        int r;
116,594✔
2572

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

2578
        assert(u);
116,594✔
2579
        assert(key);
116,594✔
2580
        assert(value);
116,594✔
2581

2582
        /* Manager manages ExecSharedRuntime objects by the unit id.
2583
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2584
        if (isempty(u->id)) {
116,594✔
2585
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
2586
                return 0;
×
2587
        }
2588

2589
        if (u->manager) {
116,594✔
2590
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
116,594✔
2591
                        return log_oom();
×
2592

2593
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
116,594✔
2594
        }
2595
        if (!rt) {
116,594✔
2596
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
113,527✔
2597
                        return log_oom();
×
2598

2599
                rt = rt_create;
113,527✔
2600
        }
2601

2602
        if (streq(key, "tmp-dir")) {
116,594✔
2603
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2604
                        return -ENOMEM;
2605

2606
        } else if (streq(key, "var-tmp-dir")) {
116,594✔
2607
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2608
                        return -ENOMEM;
2609

2610
        } else if (streq(key, "netns-socket-0")) {
116,594✔
2611

2612
                safe_close(rt->netns_storage_socket[0]);
×
2613
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2614
                if (rt->netns_storage_socket[0] < 0)
×
2615
                        return 0;
2616

2617
        } else if (streq(key, "netns-socket-1")) {
116,594✔
2618

2619
                safe_close(rt->netns_storage_socket[1]);
×
2620
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2621
                if (rt->netns_storage_socket[1] < 0)
×
2622
                        return 0;
2623
        } else
2624
                return 0;
2625

2626
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
2627
        if (rt_create && u->manager) {
×
2628
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
2629
                if (r < 0) {
×
2630
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
2631
                        return 0;
×
2632
                }
2633

2634
                rt_create->manager = u->manager;
×
2635

2636
                /* Avoid cleanup */
2637
                TAKE_PTR(rt_create);
×
2638
        }
2639

2640
        return 1;
2641
}
2642

2643
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
98✔
2644
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
98✔
2645
        char *id = NULL;
98✔
2646
        int r, userns_fdpair[] = {-1, -1}, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
98✔
2647
        const char *p, *v = ASSERT_PTR(value);
98✔
2648
        size_t n;
98✔
2649

2650
        assert(m);
98✔
2651
        assert(fds);
98✔
2652

2653
        n = strcspn(v, " ");
98✔
2654
        id = strndupa_safe(v, n);
98✔
2655
        if (v[n] != ' ')
98✔
2656
                goto finalize;
×
2657
        p = v + n + 1;
98✔
2658

2659
        v = startswith(p, "tmp-dir=");
98✔
2660
        if (v) {
98✔
2661
                n = strcspn(v, " ");
98✔
2662
                tmp_dir = strndup(v, n);
98✔
2663
                if (!tmp_dir)
98✔
2664
                        return log_oom();
×
2665
                if (v[n] != ' ')
98✔
2666
                        goto finalize;
×
2667
                p = v + n + 1;
98✔
2668
        }
2669

2670
        v = startswith(p, "var-tmp-dir=");
98✔
2671
        if (v) {
98✔
2672
                n = strcspn(v, " ");
98✔
2673
                var_tmp_dir = strndup(v, n);
98✔
2674
                if (!var_tmp_dir)
98✔
2675
                        return log_oom();
×
2676
                if (v[n] != ' ')
98✔
2677
                        goto finalize;
96✔
2678
                p = v + n + 1;
2✔
2679
        }
2680

2681
        v = startswith(p, "userns-socket-0=");
2✔
2682
        if (v) {
2✔
2683
                char *buf;
×
2684

2685
                n = strcspn(v, " ");
×
2686
                buf = strndupa_safe(v, n);
×
2687

2688
                userns_fdpair[0] = deserialize_fd(fds, buf);
×
2689
                if (userns_fdpair[0] < 0)
×
2690
                        return userns_fdpair[0];
2691
                if (v[n] != ' ')
×
2692
                        goto finalize;
×
2693
                p = v + n + 1;
×
2694
        }
2695

2696
        v = startswith(p, "userns-socket-1=");
2✔
2697
        if (v) {
2✔
2698
                char *buf;
×
2699

2700
                n = strcspn(v, " ");
×
2701
                buf = strndupa_safe(v, n);
×
2702

2703
                userns_fdpair[1] = deserialize_fd(fds, buf);
×
2704
                if (userns_fdpair[1] < 0)
×
2705
                        return userns_fdpair[1];
2706
                if (v[n] != ' ')
×
2707
                        goto finalize;
×
2708
                p = v + n + 1;
×
2709
        }
2710

2711
        v = startswith(p, "netns-socket-0=");
2✔
2712
        if (v) {
2✔
2713
                char *buf;
2✔
2714

2715
                n = strcspn(v, " ");
2✔
2716
                buf = strndupa_safe(v, n);
2✔
2717

2718
                netns_fdpair[0] = deserialize_fd(fds, buf);
2✔
2719
                if (netns_fdpair[0] < 0)
2✔
2720
                        return netns_fdpair[0];
2721
                if (v[n] != ' ')
2✔
2722
                        goto finalize;
×
2723
                p = v + n + 1;
2✔
2724
        }
2725

2726
        v = startswith(p, "netns-socket-1=");
2✔
2727
        if (v) {
2✔
2728
                char *buf;
2✔
2729

2730
                n = strcspn(v, " ");
2✔
2731
                buf = strndupa_safe(v, n);
2✔
2732

2733
                netns_fdpair[1] = deserialize_fd(fds, buf);
2✔
2734
                if (netns_fdpair[1] < 0)
2✔
2735
                        return netns_fdpair[1];
2736
                if (v[n] != ' ')
2✔
2737
                        goto finalize;
2✔
2738
                p = v + n + 1;
×
2739
        }
2740

2741
        v = startswith(p, "ipcns-socket-0=");
×
2742
        if (v) {
×
2743
                char *buf;
×
2744

2745
                n = strcspn(v, " ");
×
2746
                buf = strndupa_safe(v, n);
×
2747

2748
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2749
                if (ipcns_fdpair[0] < 0)
×
2750
                        return ipcns_fdpair[0];
2751
                if (v[n] != ' ')
×
2752
                        goto finalize;
×
2753
                p = v + n + 1;
×
2754
        }
2755

2756
        v = startswith(p, "ipcns-socket-1=");
×
2757
        if (v) {
×
2758
                char *buf;
×
2759

2760
                n = strcspn(v, " ");
×
2761
                buf = strndupa_safe(v, n);
×
2762

2763
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2764
                if (ipcns_fdpair[1] < 0)
×
2765
                        return ipcns_fdpair[1];
2766
        }
2767

2768
finalize:
×
2769
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_fdpair, netns_fdpair, ipcns_fdpair, NULL);
98✔
2770
        if (r < 0)
98✔
2771
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2772
        return 0;
2773
}
2774

2775
void exec_shared_runtime_vacuum(Manager *m) {
1,550✔
2776
        ExecSharedRuntime *rt;
1,550✔
2777

2778
        assert(m);
1,550✔
2779

2780
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2781

2782
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,648✔
2783
                if (rt->n_ref > 0)
98✔
2784
                        continue;
98✔
2785

2786
                (void) exec_shared_runtime_free(rt);
×
2787
        }
2788
}
1,550✔
2789

2790
int exec_runtime_make(
6,593✔
2791
                const Unit *unit,
2792
                const ExecContext *context,
2793
                ExecSharedRuntime *shared,
2794
                DynamicCreds *creds,
2795
                ExecRuntime **ret) {
2796
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
6,593✔
2797
        _cleanup_free_ char *ephemeral = NULL;
6,593✔
2798
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
6,593✔
2799
        int r;
6,593✔
2800

2801
        assert(unit);
6,593✔
2802
        assert(context);
6,593✔
2803
        assert(ret);
6,593✔
2804

2805
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
6,593✔
2806
                *ret = NULL;
6,416✔
2807
                return 0;
6,416✔
2808
        }
2809

2810
        if (exec_needs_ephemeral(context)) {
177✔
2811
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2812
                if (r < 0)
×
2813
                        return r;
2814

2815
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2816
                if (r < 0)
×
2817
                        return r;
2818

2819
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2820
                        return -errno;
×
2821
        }
2822

2823
        rt = new(ExecRuntime, 1);
177✔
2824
        if (!rt)
177✔
2825
                return -ENOMEM;
2826

2827
        *rt = (ExecRuntime) {
177✔
2828
                .shared = shared,
2829
                .dynamic_creds = creds,
2830
                .ephemeral_copy = TAKE_PTR(ephemeral),
177✔
2831
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
177✔
2832
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
177✔
2833
        };
2834

2835
        *ret = TAKE_PTR(rt);
177✔
2836
        return 1;
177✔
2837
}
2838

2839
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
52,769✔
2840
        if (!rt)
52,769✔
2841
                return NULL;
2842

2843
        exec_shared_runtime_unref(rt->shared);
177✔
2844
        dynamic_creds_unref(rt->dynamic_creds);
177✔
2845

2846
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
177✔
2847

2848
        safe_close_pair(rt->ephemeral_storage_socket);
177✔
2849
        return mfree(rt);
177✔
2850
}
2851

2852
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
6,362✔
2853
        if (!rt)
6,362✔
2854
                return NULL;
2855

2856
        rt->shared = exec_shared_runtime_destroy(rt->shared);
55✔
2857
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
55✔
2858
        return exec_runtime_free(rt);
55✔
2859
}
2860

2861
void exec_runtime_clear(ExecRuntime *rt) {
32✔
2862
        if (!rt)
32✔
2863
                return;
2864

2865
        safe_close_pair(rt->ephemeral_storage_socket);
32✔
2866
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
32✔
2867
}
2868

2869
void exec_params_shallow_clear(ExecParameters *p) {
2,566✔
2870
        if (!p)
2,566✔
2871
                return;
2872

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

2876
        p->environment = strv_free(p->environment);
2,566✔
2877
        p->fd_names = strv_free(p->fd_names);
2,566✔
2878
        p->files_env = strv_free(p->files_env);
2,566✔
2879
        p->fds = mfree(p->fds);
2,566✔
2880
        p->root_directory_fd = safe_close(p->root_directory_fd);
2,566✔
2881
        p->exec_fd = safe_close(p->exec_fd);
2,566✔
2882
        p->user_lookup_fd = -EBADF;
2,566✔
2883
        p->bpf_restrict_fs_map_fd = -EBADF;
2,566✔
2884
        p->unit_id = mfree(p->unit_id);
2,566✔
2885
        p->invocation_id = SD_ID128_NULL;
2,566✔
2886
        p->invocation_id_string[0] = '\0';
2,566✔
2887
        p->confirm_spawn = mfree(p->confirm_spawn);
2,566✔
2888
}
2889

2890
void exec_params_deep_clear(ExecParameters *p) {
32✔
2891
        if (!p)
32✔
2892
                return;
2893

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

2898
        close_many_unset(p->fds, p->n_socket_fds + p->n_stashed_fds);
32✔
2899

2900
        p->cgroup_path = mfree(p->cgroup_path);
32✔
2901

2902
        if (p->prefix) {
32✔
2903
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
32✔
2904
                p->prefix = mfree(p->prefix);
32✔
2905
        }
2906

2907
        p->received_credentials_directory = mfree(p->received_credentials_directory);
32✔
2908
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
32✔
2909

2910
        if (p->idle_pipe) {
32✔
2911
                close_many_and_free(p->idle_pipe, 4);
×
UNCOV
2912
                p->idle_pipe = NULL;
×
2913
        }
2914

2915
        p->stdin_fd = safe_close(p->stdin_fd);
32✔
2916
        p->stdout_fd = safe_close(p->stdout_fd);
32✔
2917
        p->stderr_fd = safe_close(p->stderr_fd);
32✔
2918
        p->root_directory_fd = safe_close(p->root_directory_fd);
32✔
2919

2920
        p->notify_socket = mfree(p->notify_socket);
32✔
2921

2922
        open_file_free_many(&p->open_files);
32✔
2923

2924
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
32✔
2925

2926
        exec_params_shallow_clear(p);
32✔
2927
}
2928

2929
void exec_directory_done(ExecDirectory *d) {
263,730✔
2930
        if (!d)
263,730✔
2931
                return;
2932

2933
        FOREACH_ARRAY(i, d->items, d->n_items) {
265,388✔
2934
                free(i->path);
1,658✔
2935
                strv_free(i->symlinks);
1,658✔
2936
        }
2937

2938
        d->items = mfree(d->items);
263,730✔
2939
        d->n_items = 0;
263,730✔
2940
        d->mode = 0755;
263,730✔
2941
}
2942

2943
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
4,723✔
2944
        assert(d);
4,723✔
2945
        assert(path);
4,723✔
2946

2947
        FOREACH_ARRAY(i, d->items, d->n_items)
7,187✔
2948
                if (path_equal(i->path, path))
2,479✔
2949
                        return i;
2950

2951
        return NULL;
2952
}
2953

2954
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
4,723✔
UNCOV
2955
        _cleanup_strv_free_ char **s = NULL;
×
2956
        _cleanup_free_ char *p = NULL;
4,723✔
2957
        ExecDirectoryItem *existing;
4,723✔
2958
        int r;
4,723✔
2959

2960
        assert(d);
4,723✔
2961
        assert(path);
4,723✔
2962

2963
        existing = exec_directory_find(d, path);
4,723✔
2964
        if (existing) {
4,723✔
2965
                r = strv_extend(&existing->symlinks, symlink);
15✔
2966
                if (r < 0)
15✔
2967
                        return r;
2968

2969
                existing->flags |= flags;
15✔
2970

2971
                return 0; /* existing item is updated */
15✔
2972
        }
2973

2974
        p = strdup(path);
4,708✔
2975
        if (!p)
4,708✔
2976
                return -ENOMEM;
2977

2978
        if (symlink) {
4,708✔
2979
                s = strv_new(symlink);
6✔
2980
                if (!s)
6✔
2981
                        return -ENOMEM;
2982
        }
2983

2984
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
4,708✔
2985
                return -ENOMEM;
2986

2987
        d->items[d->n_items++] = (ExecDirectoryItem) {
4,708✔
2988
                .path = TAKE_PTR(p),
4,708✔
2989
                .symlinks = TAKE_PTR(s),
4,708✔
2990
                .flags = flags,
2991
        };
2992

2993
        return 1; /* new item is added */
4,708✔
2994
}
2995

2996
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
932✔
2997
        assert(a);
932✔
2998
        assert(b);
932✔
2999

3000
        return path_compare(a->path, b->path);
932✔
3001
}
3002

3003
void exec_directory_sort(ExecDirectory *d) {
145,596✔
3004
        assert(d);
145,596✔
3005

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

3011
        if (d->n_items <= 1)
145,596✔
3012
                return;
3013

3014
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
166✔
3015

3016
        for (size_t i = 1; i < d->n_items; i++)
725✔
3017
                for (size_t j = 0; j < i; j++)
1,864✔
3018
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,320✔
3019
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
3020
                                break;
15✔
3021
                        }
3022
}
3023

3024
ExecCleanMask exec_clean_mask_from_string(const char *s) {
4✔
3025
        ExecDirectoryType t;
4✔
3026

3027
        assert(s);
4✔
3028

3029
        if (streq(s, "all"))
4✔
3030
                return EXEC_CLEAN_ALL;
3031
        if (streq(s, "fdstore"))
3✔
3032
                return EXEC_CLEAN_FDSTORE;
3033

3034
        t = exec_resource_type_from_string(s);
2✔
3035
        if (t < 0)
2✔
3036
                return (ExecCleanMask) t;
3037

3038
        return 1U << t;
2✔
3039
}
3040

3041
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
3042
        [EXEC_INPUT_NULL]      = "null",
3043
        [EXEC_INPUT_TTY]       = "tty",
3044
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
3045
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
3046
        [EXEC_INPUT_SOCKET]    = "socket",
3047
        [EXEC_INPUT_NAMED_FD]  = "fd",
3048
        [EXEC_INPUT_DATA]      = "data",
3049
        [EXEC_INPUT_FILE]      = "file",
3050
};
3051

3052
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,568✔
3053

3054
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
3055
        [EXEC_OUTPUT_INHERIT]             = "inherit",
3056
        [EXEC_OUTPUT_NULL]                = "null",
3057
        [EXEC_OUTPUT_TTY]                 = "tty",
3058
        [EXEC_OUTPUT_KMSG]                = "kmsg",
3059
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
3060
        [EXEC_OUTPUT_JOURNAL]             = "journal",
3061
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
3062
        [EXEC_OUTPUT_SOCKET]              = "socket",
3063
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
3064
        [EXEC_OUTPUT_FILE]                = "file",
3065
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
3066
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
3067
};
3068

3069
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
33,358✔
3070

3071
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
3072
        [EXEC_UTMP_INIT]  = "init",
3073
        [EXEC_UTMP_LOGIN] = "login",
3074
        [EXEC_UTMP_USER]  = "user",
3075
};
3076

3077
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,645✔
3078

3079
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
3080
        [EXEC_PRESERVE_NO]      = "no",
3081
        [EXEC_PRESERVE_YES]     = "yes",
3082
        [EXEC_PRESERVE_RESTART] = "restart",
3083
};
3084

3085
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
19,419✔
3086

3087
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
3088
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3089
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
3090
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
3091
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
3092
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
3093
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
3094
};
3095

3096
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3097

3098
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3099
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
3100
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
3101
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
3102
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
3103
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
3104
};
3105

UNCOV
3106
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3107

3108
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
3109
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
3110
 * directories, specifically .timer units with their timestamp touch file. */
3111
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3112
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
3113
        [EXEC_DIRECTORY_STATE]         = "state",
3114
        [EXEC_DIRECTORY_CACHE]         = "cache",
3115
        [EXEC_DIRECTORY_LOGS]          = "logs",
3116
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
3117
};
3118

3119
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
279✔
3120

3121
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3122
        [EXEC_KEYRING_INHERIT] = "inherit",
3123
        [EXEC_KEYRING_PRIVATE] = "private",
3124
        [EXEC_KEYRING_SHARED]  = "shared",
3125
};
3126

3127
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
16,172✔
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