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

systemd / systemd / 20939507887

12 Jan 2026 11:33PM UTC coverage: 72.39% (-0.3%) from 72.693%
20939507887

push

github

yuwata
units: Fix the missing dependency in systemd-pcrproduct

NvPCR need to read from /var/lib/nvpcr and the current unit file is
executed before /var is mounted. This adds the dep back so its always
runs after /var is mounted like systemd-tpm2-setup.service

308905 of 426721 relevant lines covered (72.39%)

1143460.48 hits per line

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

79.4
/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,778✔
68
        assert(context);
12,778✔
69

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

73
        if (context->tty_path)
11,977✔
74
                return context->tty_path;
498✔
75

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

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

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

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

92
        if (!isatty_safe(output_fd))
1,042✔
93
                return 0;
1,042✔
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 &&
657✔
114
            exec_context_shall_ansi_seq_reset(context) &&
236✔
115
            isatty_safe(input_fd)) {
99✔
116
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
99✔
117
                if (r < 0)
99✔
118
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
99✔
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,784✔
125
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
23,568✔
126
        int fd, r;
11,784✔
127

128
        assert(context);
11,784✔
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,784✔
136

137
        if (parameters && parameters->stdout_fd >= 0 && isatty_safe(parameters->stdout_fd))
11,784✔
138
                fd = parameters->stdout_fd;
21✔
139
        else if (path && exec_context_has_tty(context)) {
11,763✔
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);
49✔
193
}
194

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

198
        return context->private_network || context->network_namespace_path;
60,544✔
199
}
200

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

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

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

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

215
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
82,350✔
216
        assert(context);
82,350✔
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)) {
82,350✔
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;
82,350✔
229
}
230

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

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

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

240
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
27,980✔
241
}
242

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

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

249
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
89,834✔
250
        assert(context);
89,834✔
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))
89,834✔
254
                return false;
255

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

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

264
        assert(context);
33,022✔
265

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

269
        if (context->root_directory_as_fd)
32,971✔
270
                return true;
271

272
        if (!strv_isempty(context->read_write_paths) ||
32,965✔
273
            !strv_isempty(context->read_only_paths) ||
30,739✔
274
            !strv_isempty(context->inaccessible_paths) ||
30,732✔
275
            !strv_isempty(context->exec_paths) ||
30,713✔
276
            !strv_isempty(context->no_exec_paths))
30,713✔
277
                return true;
278

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

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

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

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

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

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

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

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

303
        if (context->private_devices ||
29,262✔
304
            context->private_mounts > 0 ||
28,887✔
305
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,386✔
306
            context->protect_system != PROTECT_SYSTEM_NO ||
28,357✔
307
            context->protect_home != PROTECT_HOME_NO ||
28,357✔
308
            context->protect_kernel_tunables ||
28,357✔
309
            context->protect_kernel_modules ||
28,357✔
310
            context->protect_kernel_logs ||
53,305✔
311
            exec_needs_cgroup_mount(context) ||
26,651✔
312
            context->protect_proc != PROTECT_PROC_DEFAULT ||
26,633✔
313
            context->proc_subset != PROC_SUBSET_ALL ||
26,565✔
314
            context->private_bpf != PRIVATE_BPF_NO ||
53,100✔
315
            exec_needs_ipc_namespace(context) ||
53,100✔
316
            exec_needs_pid_namespace(context, params))
26,550✔
317
                return true;
2,765✔
318

319
        if (context->root_directory) {
26,497✔
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,492✔
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,492✔
339
                return true;
340

341
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
158,560✔
342
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
137,121✔
343
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,047✔
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) {
3,940✔
350
        assert(context);
3,940✔
351
        assert(params);
3,940✔
352

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

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

359
        if (!context->root_directory && !context->root_image && !context->root_directory_as_fd)
3,228✔
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,228✔
372
        assert(params);
20,228✔
373

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

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

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

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

388
        if (!context->dynamic_user)
10,031✔
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,500✔
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,500✔
403
}
404

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

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

414
        assert(params);
10,828✔
415
        assert(c);
10,828✔
416
        assert(ret);
10,828✔
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,828✔
429
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
716✔
430
                        subgroup = ".control";
431
                else
432
                        subgroup = c->delegate_subgroup;
670✔
433
        }
434

435
        if (subgroup)
670✔
436
                p = path_join(prefix, subgroup);
716✔
437
        else
438
                p = strdup(strempty(prefix));
10,120✔
439
        if (!p)
10,828✔
440
                return -ENOMEM;
441

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

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

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

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

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

460
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
4,940✔
461

462
        log_unit_struct(unit, LOG_DEBUG,
2,470✔
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,470✔
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,470✔
480
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
481
        _cleanup_fclose_ FILE *f = NULL;
2,470✔
482
        int r;
2,470✔
483

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

496
        LOG_CONTEXT_PUSH_UNIT(unit);
4,940✔
497

498
        r = exec_context_load_environment(unit, context, &params->files_env);
2,470✔
499
        if (r < 0)
2,470✔
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,470✔
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,470✔
514
        if (exec_params_needs_control_subcgroup(params)) {
2,470✔
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,466✔
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,470✔
539
        if (r < 0)
2,470✔
540
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
541

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

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

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

554
        r = fd_cloexec(fileno(f), false);
2,470✔
555
        if (r < 0)
2,470✔
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,470✔
559
        if (r < 0)
2,470✔
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,470✔
566
        if (r < 0)
2,470✔
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,470✔
570
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,470✔
571

572
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,470✔
573
        dual_timestamp start_timestamp;
2,470✔
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,470✔
578
        if (r < 0)
2,470✔
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,470✔
584

585
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
586
        r = posix_spawn_wrapper(
2,470✔
587
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
2,470✔
588
                        STRV_MAKE(unit->manager->executor_path,
2,470✔
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,470✔
598

599
        if (r == -EUCLEAN && cgtarget)
2,470✔
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,470✔
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,470✔
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,470✔
614
                       command->path, pidref.pid, r > 0 ? "via" : "without");
615

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

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

622
void exec_context_init(ExecContext *c) {
62,521✔
623
        assert(c);
62,521✔
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,521✔
629
                .umask = 0022,
630
                .ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO,
62,521✔
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)
375,126✔
656
                d->mode = 0755;
312,605✔
657

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

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

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

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

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

673
        for (size_t l = 0; l < 3; l++) {
209,760✔
674
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
157,320✔
675
                c->stdio_file[l] = mfree(c->stdio_file[l]);
157,320✔
676
        }
677

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

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

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

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

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

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

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

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

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

728
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
314,640✔
729
                exec_directory_done(d);
262,200✔
730

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

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

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

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

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

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

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

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

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

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

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

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

768
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
3✔
769
                        p = path_join(runtime_prefix, "private", i->path);
×
770
                else
771
                        p = path_join(runtime_prefix, i->path);
3✔
772
                if (!p)
3✔
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);
3✔
778

779
                STRV_FOREACH(symlink, i->symlinks) {
3✔
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,799✔
797
        _cleanup_free_ char *p = NULL;
12,799✔
798

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

802
        p = path_join("/run/systemd/propagate/", u->id);
2,604✔
803
        if (!p)
2,604✔
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,604✔
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,349✔
814
        assert(c);
110,349✔
815

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

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

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

829
        exec_command_done(c);
17,835✔
830
        return mfree(c);
17,835✔
831
}
832

833
ExecCommand* exec_command_free_list(ExecCommand *c) {
152,471✔
834
        ExecCommand *i;
152,471✔
835

836
        while ((i = LIST_POP(command, c)))
170,306✔
837
                exec_command_free(i);
17,835✔
838

839
        return NULL;
152,471✔
840
}
841

842
void exec_command_free_array(ExecCommand **c, size_t n) {
21,580✔
843
        FOREACH_ARRAY(i, c, n)
174,030✔
844
                *i = exec_command_free_list(*i);
152,450✔
845
}
21,580✔
846

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

852
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
5,416✔
853
        FOREACH_ARRAY(i, c, n)
38,871✔
854
                LIST_FOREACH(command, z, *i)
35,844✔
855
                        exec_status_reset(&z->exec_status);
2,389✔
856
}
5,416✔
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,084✔
870
        assert(c);
39,084✔
871

872
        switch (fd_index) {
39,084✔
873

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

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

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

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

886
        case STDERR_FILENO:
13,028✔
887
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
13,028✔
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,470✔
898
        _cleanup_strv_free_ char **v = NULL;
2,470✔
899
        int r;
2,470✔
900

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

904
        STRV_FOREACH(i, c->environment_files) {
2,473✔
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,470✔
964

965
        return 0;
2,470✔
966
}
967

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

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

974
        tty = skip_dev_prefix(tty);
250✔
975

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

980
        if (resolve_dev_console(&resolved) < 0)
39✔
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));
39✔
985
}
986

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

990
        return ec->tty_reset ||
51,156✔
991
                ec->tty_vhangup ||
25,459✔
992
                ec->tty_vt_disallocate ||
25,459✔
993
                exec_input_is_terminal(ec->std_input) ||
25,459✔
994
                ec->std_output == EXEC_OUTPUT_TTY ||
51,156✔
995
                ec->std_error == EXEC_OUTPUT_TTY;
25,410✔
996
}
997

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

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

1004
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,154✔
1005
        assert(c);
1,154✔
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,154✔
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");
539✔
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,104✔
1031
        assert(f);
3,104✔
1032
        assert(prefix);
3,104✔
1033
        assert(name);
3,104✔
1034

1035
        if (!strv_isempty(strv)) {
3,104✔
1036
                fprintf(f, "%s%s:", prefix, name);
2✔
1037
                strv_fprintf(f, strv);
2✔
1038
                fputs("\n", f);
2✔
1039
        }
1040
}
3,104✔
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) {
388✔
1085
        int r;
388✔
1086

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

1090
        prefix = strempty(prefix);
388✔
1091

1092
        fprintf(f,
388✔
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
                "%sMemoryTHP: %s\n",
1122
                prefix, c->umask,
388✔
1123
                prefix, empty_to_root(c->working_directory),
388✔
1124
                prefix, empty_to_root(c->root_directory),
388✔
1125
                prefix, yes_no(c->root_ephemeral),
388✔
1126
                prefix, yes_no(c->non_blocking),
388✔
1127
                prefix, private_tmp_to_string(c->private_tmp),
388✔
1128
                prefix, yes_no(c->private_devices),
388✔
1129
                prefix, yes_no(c->protect_kernel_tunables),
388✔
1130
                prefix, yes_no(c->protect_kernel_modules),
388✔
1131
                prefix, yes_no(c->protect_kernel_logs),
388✔
1132
                prefix, yes_no(c->protect_clock),
388✔
1133
                prefix, protect_control_groups_to_string(c->protect_control_groups),
388✔
1134
                prefix, yes_no(c->private_network),
388✔
1135
                prefix, private_users_to_string(c->private_users),
388✔
1136
                prefix, private_pids_to_string(c->private_pids),
388✔
1137
                prefix, protect_home_to_string(c->protect_home),
388✔
1138
                prefix, protect_system_to_string(c->protect_system),
388✔
1139
                prefix, yes_no(exec_context_get_effective_mount_apivfs(c)),
388✔
1140
                prefix, yes_no(exec_context_get_effective_bind_log_sockets(c)),
388✔
1141
                prefix, yes_no(c->ignore_sigpipe),
388✔
1142
                prefix, yes_no(c->memory_deny_write_execute),
388✔
1143
                prefix, yes_no(c->restrict_realtime),
388✔
1144
                prefix, yes_no(c->restrict_suid_sgid),
388✔
1145
                prefix, exec_keyring_mode_to_string(c->keyring_mode),
388✔
1146
                prefix, protect_hostname_to_string(c->protect_hostname), c->private_hostname ? ":" : "", strempty(c->private_hostname),
776✔
1147
                prefix, protect_proc_to_string(c->protect_proc),
388✔
1148
                prefix, proc_subset_to_string(c->proc_subset),
388✔
1149
                prefix, private_bpf_to_string(c->private_bpf),
388✔
1150
                prefix, memory_thp_to_string(c->memory_thp));
388✔
1151

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

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

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

1168
        if (c->root_image)
388✔
1169
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1170

1171
        if (c->root_image_options) {
388✔
1172
                _cleanup_free_ char *opts_str = NULL;
×
1173

1174
                if (mount_options_to_string(c->root_image_options, &opts_str) >= 0 && !isempty(opts_str))
×
1175
                        fprintf(f, "%sRootImageOptions: %s\n", prefix, opts_str);
×
1176
        }
1177

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1352
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
364✔
1353

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1430
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
388✔
1431

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1564
        if (c->syscall_errno > 0) {
388✔
1565
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
387✔
1566

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

1577
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
388✔
1578
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1579
                        mount->ignore_enoent ? "-": "",
×
1580
                        mount->source,
1581
                        mount->destination);
1582
                if (mount->mount_options) {
×
1583
                        _cleanup_free_ char *opts = NULL;
×
1584

1585
                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
×
1586
                                fprintf(f, " %s", opts);
×
1587
                }
1588
                fprintf(f, "\n");
×
1589
        }
1590

1591
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
388✔
1592
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1593
                        mount->ignore_enoent ? "-": "",
×
1594
                        mount->source);
1595
                if (mount->mount_options) {
×
1596
                        _cleanup_free_ char *opts = NULL;
×
1597

1598
                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
×
1599
                                fprintf(f, " %s", opts);
×
1600
                }
1601
                fprintf(f, "\n");
×
1602
        }
1603

1604
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
388✔
1605
}
388✔
1606

1607
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1608
        assert(c);
×
1609

1610
        /* Returns true if the process forked off would run under
1611
         * an unchanged UID or as root. */
1612

1613
        if (!c->user)
×
1614
                return true;
1615

1616
        if (STR_IN_SET(c->user, "root", "0"))
×
1617
                return true;
×
1618

1619
        return false;
×
1620
}
1621

1622
int exec_context_get_effective_ioprio(const ExecContext *c) {
5,828✔
1623
        int p;
5,828✔
1624

1625
        assert(c);
5,828✔
1626

1627
        if (c->ioprio_is_set)
5,828✔
1628
                return c->ioprio;
14✔
1629

1630
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
5,814✔
1631
        if (p < 0)
5,814✔
1632
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1633

1634
        return ioprio_normalize(p);
5,814✔
1635
}
1636

1637
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
39,220✔
1638
        assert(c);
39,220✔
1639

1640
        /* Explicit setting wins */
1641
        if (c->mount_apivfs >= 0)
39,220✔
1642
                return c->mount_apivfs > 0;
119✔
1643

1644
        /* Default to "yes" if root directory or image are specified */
1645
        if (exec_context_with_rootfs(c))
39,101✔
1646
                return true;
148✔
1647

1648
        return false;
1649
}
1650

1651
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
31,837✔
1652
        assert(c);
31,837✔
1653

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

1659
        if (c->bind_log_sockets >= 0)
31,829✔
1660
                return c->bind_log_sockets > 0;
2✔
1661

1662
        if (exec_context_get_effective_mount_apivfs(c))
31,827✔
1663
                return true;
1664

1665
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1666
        if (exec_context_with_rootfs(c) && c->private_devices)
31,715✔
1667
                return true;
×
1668

1669
        return false;
1670
}
1671

1672
void exec_context_free_log_extra_fields(ExecContext *c) {
52,442✔
1673
        assert(c);
52,442✔
1674

1675
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
52,692✔
1676
                free(field->iov_base);
250✔
1677

1678
        c->log_extra_fields = mfree(c->log_extra_fields);
52,442✔
1679
        c->n_log_extra_fields = 0;
52,442✔
1680
}
52,442✔
1681

1682
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,671✔
1683
        _cleanup_close_ int fd = -EBADF;
1,671✔
1684
        const char *path;
1,671✔
1685
        struct stat st;
1,671✔
1686
        int r;
1,671✔
1687

1688
        assert(c);
1,671✔
1689

1690
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1691
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,671✔
1692

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

1699
        path = exec_context_tty_path(c);
37✔
1700
        if (!path)
37✔
1701
                return;
1702

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

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

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

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

1725
int exec_context_get_clean_directories(
1✔
1726
                ExecContext *c,
1727
                char **prefix,
1728
                ExecCleanMask mask,
1729
                char ***ret) {
1730

1731
        _cleanup_strv_free_ char **l = NULL;
1✔
1732
        int r;
1✔
1733

1734
        assert(c);
1✔
1735
        assert(prefix);
1✔
1736
        assert(ret);
1✔
1737

1738
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
6✔
1739
                if (!BIT_SET(mask, t))
5✔
1740
                        continue;
×
1741

1742
                if (!prefix[t])
5✔
1743
                        continue;
×
1744

1745
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
7✔
1746
                        char *j;
2✔
1747

1748
                        j = path_join(prefix[t], i->path);
2✔
1749
                        if (!j)
2✔
1750
                                return -ENOMEM;
1751

1752
                        r = strv_consume(&l, j);
2✔
1753
                        if (r < 0)
2✔
1754
                                return r;
1755

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

1762
                                r = strv_consume(&l, j);
2✔
1763
                                if (r < 0)
2✔
1764
                                        return r;
1765
                        }
1766

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

1772
                                r = strv_consume(&l, j);
×
1773
                                if (r < 0)
×
1774
                                        return r;
1775
                        }
1776
                }
1777
        }
1778

1779
        *ret = TAKE_PTR(l);
1✔
1780
        return 0;
1✔
1781
}
1782

1783
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
2,898✔
1784
        ExecCleanMask mask = 0;
2,898✔
1785

1786
        assert(c);
2,898✔
1787
        assert(ret);
2,898✔
1788

1789
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
17,388✔
1790
                if (c->directories[t].n_items > 0)
14,490✔
1791
                        mask |= 1U << t;
307✔
1792

1793
        *ret = mask;
2,898✔
1794
        return 0;
2,898✔
1795
}
1796

1797
int exec_context_get_oom_score_adjust(const ExecContext *c) {
2,914✔
1798
        int n = 0, r;
2,914✔
1799

1800
        assert(c);
2,914✔
1801

1802
        if (c->oom_score_adjust_set)
2,914✔
1803
                return c->oom_score_adjust;
617✔
1804

1805
        r = get_oom_score_adjust(&n);
2,297✔
1806
        if (r < 0)
2,297✔
1807
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1808

1809
        return n;
2,297✔
1810
}
1811

1812
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
2,914✔
1813
        _cleanup_free_ char *t = NULL;
2,914✔
1814
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
2,914✔
1815
        int r;
2,914✔
1816

1817
        assert(c);
2,914✔
1818

1819
        if (c->coredump_filter_set)
2,914✔
1820
                return c->coredump_filter;
×
1821

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

1831
        return n;
2,914✔
1832
}
1833

1834
int exec_context_get_nice(const ExecContext *c) {
2,914✔
1835
        int n;
2,914✔
1836

1837
        assert(c);
2,914✔
1838

1839
        if (c->nice_set)
2,914✔
1840
                return c->nice;
14✔
1841

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

1849
        return n;
1850
}
1851

1852
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
2,914✔
1853
        int n;
2,914✔
1854

1855
        assert(c);
2,914✔
1856

1857
        if (c->cpu_sched_set)
2,914✔
1858
                return c->cpu_sched_policy;
×
1859

1860
        n = sched_getscheduler(0);
2,914✔
1861
        if (n < 0)
2,914✔
1862
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1863

1864
        return n < 0 ? SCHED_OTHER : n;
2,914✔
1865
}
1866

1867
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
2,914✔
1868
        struct sched_param p = {};
2,914✔
1869
        int r;
2,914✔
1870

1871
        assert(c);
2,914✔
1872

1873
        if (c->cpu_sched_set)
2,914✔
1874
                return c->cpu_sched_priority;
×
1875

1876
        r = sched_getparam(0, &p);
2,914✔
1877
        if (r < 0)
2,914✔
1878
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1879

1880
        return r >= 0 ? p.sched_priority : 0;
2,914✔
1881
}
1882

1883
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
2,914✔
1884
        int r;
2,914✔
1885

1886
        assert(c);
2,914✔
1887

1888
        if (c->timer_slack_nsec != NSEC_INFINITY)
2,914✔
1889
                return c->timer_slack_nsec;
1890

1891
        r = prctl(PR_GET_TIMERSLACK);
2,914✔
1892
        if (r < 0)
2,914✔
1893
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1894

1895
        return (uint64_t) MAX(r, 0);
2,914✔
1896
}
1897

1898
bool exec_context_get_set_login_environment(const ExecContext *c) {
12,976✔
1899
        assert(c);
12,976✔
1900

1901
        if (c->set_login_environment >= 0)
12,976✔
1902
                return c->set_login_environment;
×
1903

1904
        return c->user || c->dynamic_user || c->pam_name;
22,529✔
1905
}
1906

1907
char** exec_context_get_syscall_filter(const ExecContext *c) {
2,914✔
1908
        _cleanup_strv_free_ char **l = NULL;
2,914✔
1909

1910
        assert(c);
2,914✔
1911

1912
#if HAVE_SECCOMP
1913
        if (dlopen_libseccomp() < 0)
2,914✔
1914
                return strv_new(NULL);
×
1915

1916
        void *id, *val;
2,914✔
1917
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
14,678✔
1918
                _cleanup_free_ char *name = NULL;
11,764✔
1919
                const char *e = NULL;
11,764✔
1920
                char *s;
11,764✔
1921
                int num = PTR_TO_INT(val);
11,764✔
1922

1923
                if (c->syscall_allow_list && num >= 0)
11,764✔
1924
                        /* syscall with num >= 0 in allow-list is denied. */
1925
                        continue;
×
1926

1927
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
11,764✔
1928
                if (!name)
11,764✔
1929
                        continue;
×
1930

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

1944
                if (strv_consume(&l, s) < 0)
11,764✔
1945
                        return NULL;
1946
        }
1947

1948
        strv_sort(l);
2,914✔
1949
#endif
1950

1951
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,914✔
1952
}
1953

1954
char** exec_context_get_syscall_archs(const ExecContext *c) {
2,914✔
1955
        _cleanup_strv_free_ char **l = NULL;
2,914✔
1956

1957
        assert(c);
2,914✔
1958

1959
#if HAVE_SECCOMP
1960
        void *id;
2,914✔
1961
        SET_FOREACH(id, c->syscall_archs) {
2,948✔
1962
                const char *name;
34✔
1963

1964
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
34✔
1965
                if (!name)
34✔
1966
                        continue;
×
1967

1968
                if (strv_extend(&l, name) < 0)
34✔
1969
                        return NULL;
×
1970
        }
1971

1972
        strv_sort(l);
2,914✔
1973
#endif
1974

1975
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,914✔
1976
}
1977

1978
char** exec_context_get_syscall_log(const ExecContext *c) {
2,914✔
1979
        _cleanup_strv_free_ char **l = NULL;
2,914✔
1980

1981
        assert(c);
2,914✔
1982

1983
#if HAVE_SECCOMP
1984
        if (dlopen_libseccomp() < 0)
2,914✔
1985
                return strv_new(NULL);
×
1986

1987
        void *id, *val;
2,914✔
1988
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
2,914✔
1989
                char *name = NULL;
×
1990

1991
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1992
                if (!name)
×
1993
                        continue;
×
1994

1995
                if (strv_consume(&l, name) < 0)
×
1996
                        return NULL;
×
1997
        }
1998

1999
        strv_sort(l);
2,914✔
2000
#endif
2001

2002
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,914✔
2003
}
2004

2005
char** exec_context_get_address_families(const ExecContext *c) {
2,914✔
2006
        _cleanup_strv_free_ char **l = NULL;
2,914✔
2007
        void *af;
2,914✔
2008

2009
        assert(c);
2,914✔
2010

2011
        SET_FOREACH(af, c->address_families) {
3,024✔
2012
                const char *name;
110✔
2013

2014
                name = af_to_name(PTR_TO_INT(af));
110✔
2015
                if (!name)
110✔
2016
                        continue;
×
2017

2018
                if (strv_extend(&l, name) < 0)
110✔
2019
                        return NULL;
×
2020
        }
2021

2022
        strv_sort(l);
2,914✔
2023

2024
        return l ? TAKE_PTR(l) : strv_new(NULL);
2,914✔
2025
}
2026

2027
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
2,914✔
2028
        assert(c);
2,914✔
2029

2030
#if HAVE_LIBBPF
2031
        char **l = set_get_strv(c->restrict_filesystems);
2,914✔
2032
        if (!l)
2,914✔
2033
                return NULL;
2034

2035
        return strv_sort(l);
2,914✔
2036
#else
2037
        return strv_new(NULL);
2038
#endif
2039
}
2040

2041
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
13,625✔
2042
        assert(c);
13,625✔
2043

2044
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
13,625✔
2045
}
2046

2047
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
15,405✔
2048
        assert(c);
15,405✔
2049

2050
        return c->restrict_filesystems_allow_list ||
15,405✔
2051
          !set_isempty(c->restrict_filesystems);
15,405✔
2052
}
2053

2054
bool exec_context_with_rootfs(const ExecContext *c) {
71,108✔
2055
        assert(c);
71,108✔
2056

2057
        /* Checks if RootDirectory=, RootImage= or RootDirectoryFileDescriptor= are used */
2058

2059
        return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd;
71,108✔
2060
}
2061

2062
int exec_context_has_vpicked_extensions(const ExecContext *context) {
4✔
2063
        int r;
4✔
2064

2065
        assert(context);
4✔
2066

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

2078
        return 0;
2079
}
2080

2081
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
4,943✔
2082
        assert(s);
4,943✔
2083

2084
        *s = (ExecStatus) {
4,943✔
2085
                .pid = pid,
2086
        };
2087

2088
        if (ts)
4,943✔
2089
                s->start_timestamp = *ts;
4,943✔
2090
        else
2091
                dual_timestamp_now(&s->start_timestamp);
×
2092
}
4,943✔
2093

2094
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,282✔
2095
        assert(s);
2,282✔
2096

2097
        if (s->pid != pid)
2,282✔
2098
                *s = (ExecStatus) {
5✔
2099
                        .pid = pid,
2100
                };
2101

2102
        dual_timestamp_now(&s->exit_timestamp);
2,282✔
2103

2104
        s->code = code;
2,282✔
2105
        s->status = status;
2,282✔
2106

2107
        if (context && context->utmp_id)
2,282✔
2108
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
15✔
2109
}
2,282✔
2110

2111
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
8,446✔
2112
        assert(s);
8,446✔
2113
        assert(ucred);
8,446✔
2114
        assert(ts);
8,446✔
2115

2116
        if (ucred->pid != s->pid)
8,446✔
2117
                *s = (ExecStatus) {
7✔
2118
                        .pid = ucred->pid,
2119
                };
2120

2121
        s->handoff_timestamp = *ts;
8,446✔
2122
}
8,446✔
2123

2124
void exec_status_reset(ExecStatus *s) {
27,496✔
2125
        assert(s);
27,496✔
2126

2127
        *s = (ExecStatus) {};
27,496✔
2128
}
27,496✔
2129

2130
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
226✔
2131
        assert(s);
226✔
2132
        assert(f);
226✔
2133

2134
        if (s->pid <= 0)
226✔
2135
                return;
2136

2137
        prefix = strempty(prefix);
43✔
2138

2139
        fprintf(f,
43✔
2140
                "%sPID: "PID_FMT"\n",
2141
                prefix, s->pid);
2142

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

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

2159
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
43✔
2160

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

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

2184
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
226✔
2185
        _cleanup_free_ char *cmd = NULL;
452✔
2186
        const char *prefix2;
226✔
2187

2188
        assert(c);
226✔
2189
        assert(f);
226✔
2190

2191
        prefix = strempty(prefix);
226✔
2192
        prefix2 = strjoina(prefix, "\t");
1,130✔
2193

2194
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
226✔
2195

2196
        fprintf(f,
226✔
2197
                "%sCommand Line: %s\n",
2198
                prefix, strnull(cmd));
2199

2200
        exec_status_dump(&c->exec_status, f, prefix2);
226✔
2201
}
226✔
2202

2203
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
221✔
2204
        assert(f);
221✔
2205

2206
        prefix = strempty(prefix);
221✔
2207

2208
        LIST_FOREACH(command, i, c)
446✔
2209
                exec_command_dump(i, f, prefix);
225✔
2210
}
221✔
2211

2212
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
17,835✔
2213
        ExecCommand *end;
17,835✔
2214

2215
        assert(l);
17,835✔
2216
        assert(e);
17,835✔
2217

2218
        if (*l) {
17,835✔
2219
                /* It's kind of important, that we keep the order here */
2220
                end = LIST_FIND_TAIL(command, *l);
371✔
2221
                LIST_INSERT_AFTER(command, *l, end, e);
134✔
2222
        } else
2223
                *l = e;
17,701✔
2224
}
17,835✔
2225

2226
int exec_command_set(ExecCommand *c, const char *path, ...) {
213✔
2227
        va_list ap;
213✔
2228
        char **l, *p;
213✔
2229

2230
        assert(c);
213✔
2231
        assert(path);
213✔
2232

2233
        va_start(ap, path);
213✔
2234
        l = strv_new_ap(path, ap);
213✔
2235
        va_end(ap);
213✔
2236

2237
        if (!l)
213✔
2238
                return -ENOMEM;
213✔
2239

2240
        p = strdup(path);
213✔
2241
        if (!p) {
213✔
2242
                strv_free(l);
×
2243
                return -ENOMEM;
×
2244
        }
2245

2246
        free_and_replace(c->path, p);
213✔
2247

2248
        return strv_free_and_replace(c->argv, l);
213✔
2249
}
2250

2251
int exec_command_append(ExecCommand *c, const char *path, ...) {
308✔
2252
        char **l;
308✔
2253
        va_list ap;
308✔
2254
        int r;
308✔
2255

2256
        assert(c);
308✔
2257
        assert(path);
308✔
2258

2259
        va_start(ap, path);
308✔
2260
        l = strv_new_ap(path, ap);
308✔
2261
        va_end(ap);
308✔
2262

2263
        if (!l)
308✔
2264
                return -ENOMEM;
308✔
2265

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

2270
        return 0;
2271
}
2272

2273
static char *destroy_tree(char *path) {
275✔
2274
        if (!path)
275✔
2275
                return NULL;
2276

2277
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
92✔
2278
                log_debug("Spawning process to nuke '%s'", path);
92✔
2279

2280
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
92✔
2281
        }
2282

2283
        return mfree(path);
92✔
2284
}
2285

2286
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
109,617✔
2287
        assert(rt);
109,617✔
2288

2289
        if (rt->manager)
109,617✔
2290
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
168✔
2291

2292
        rt->id = mfree(rt->id);
109,617✔
2293
        rt->tmp_dir = mfree(rt->tmp_dir);
109,617✔
2294
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
109,617✔
2295
        safe_close_pair(rt->userns_storage_socket);
109,617✔
2296
        safe_close_pair(rt->netns_storage_socket);
109,617✔
2297
        safe_close_pair(rt->ipcns_storage_socket);
109,617✔
2298
}
109,617✔
2299

2300
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
109,585✔
2301
        if (!rt)
109,585✔
2302
                return NULL;
2303

2304
        exec_shared_runtime_done(rt);
109,585✔
2305
        return mfree(rt);
109,585✔
2306
}
2307

2308
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
171✔
2309
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
112,464✔
2310

2311
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
55✔
2312
        if (!rt)
55✔
2313
                return NULL;
2314

2315
        assert(rt->n_ref > 0);
52✔
2316
        rt->n_ref--;
52✔
2317

2318
        if (rt->n_ref > 0)
52✔
2319
                return NULL;
2320

2321
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
52✔
2322
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
52✔
2323

2324
        return exec_shared_runtime_free(rt);
52✔
2325
}
2326

2327
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
109,585✔
2328
        _cleanup_free_ char *id_copy = NULL;
219,170✔
2329
        ExecSharedRuntime *n;
109,585✔
2330

2331
        assert(ret);
109,585✔
2332

2333
        id_copy = strdup(id);
109,585✔
2334
        if (!id_copy)
109,585✔
2335
                return -ENOMEM;
2336

2337
        n = new(ExecSharedRuntime, 1);
109,585✔
2338
        if (!n)
109,585✔
2339
                return -ENOMEM;
2340

2341
        *n = (ExecSharedRuntime) {
109,585✔
2342
                .id = TAKE_PTR(id_copy),
109,585✔
2343
                .userns_storage_socket = EBADF_PAIR,
2344
                .netns_storage_socket = EBADF_PAIR,
2345
                .ipcns_storage_socket = EBADF_PAIR,
2346
        };
2347

2348
        *ret = n;
109,585✔
2349
        return 0;
109,585✔
2350
}
2351

2352
static int exec_shared_runtime_add(
168✔
2353
                Manager *m,
2354
                const char *id,
2355
                char **tmp_dir,
2356
                char **var_tmp_dir,
2357
                int userns_storage_socket[2],
2358
                int netns_storage_socket[2],
2359
                int ipcns_storage_socket[2],
2360
                ExecSharedRuntime **ret) {
2361

2362
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
168✔
2363
        int r;
168✔
2364

2365
        assert(m);
168✔
2366
        assert(id);
168✔
2367

2368
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2369

2370
        r = exec_shared_runtime_allocate(&rt, id);
168✔
2371
        if (r < 0)
168✔
2372
                return r;
2373

2374
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
168✔
2375
        if (r < 0)
168✔
2376
                return r;
2377

2378
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
168✔
2379
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
168✔
2380
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
168✔
2381

2382
        if (userns_storage_socket) {
168✔
2383
                rt->userns_storage_socket[0] = TAKE_FD(userns_storage_socket[0]);
168✔
2384
                rt->userns_storage_socket[1] = TAKE_FD(userns_storage_socket[1]);
168✔
2385
        }
2386

2387
        if (netns_storage_socket) {
168✔
2388
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
168✔
2389
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
168✔
2390
        }
2391

2392
        if (ipcns_storage_socket) {
168✔
2393
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
168✔
2394
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
168✔
2395
        }
2396

2397
        rt->manager = m;
168✔
2398

2399
        if (ret)
168✔
2400
                *ret = rt;
76✔
2401
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2402
        TAKE_PTR(rt);
168✔
2403
        return 0;
168✔
2404
}
2405

2406
static int exec_shared_runtime_make(
6,327✔
2407
                Manager *m,
2408
                const ExecContext *c,
2409
                const char *id,
2410
                ExecSharedRuntime **ret) {
2411

2412
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
6,327✔
2413
        _cleanup_close_pair_ int userns_storage_socket[2] = EBADF_PAIR, netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
18,981✔
2414
        int r;
6,327✔
2415

2416
        assert(m);
6,327✔
2417
        assert(c);
6,327✔
2418
        assert(id);
6,327✔
2419

2420
        /* It is not necessary to create ExecSharedRuntime object. */
2421
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
6,327✔
2422
                *ret = NULL;
6,251✔
2423
                return 0;
6,251✔
2424
        }
2425

2426
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
146✔
2427
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
70✔
2428
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
2429
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2430
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
70✔
2431
                if (r < 0)
70✔
2432
                        return r;
2433
        }
2434

2435
        if (c->user_namespace_path)
76✔
2436
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, userns_storage_socket) < 0)
×
2437
                        return -errno;
×
2438

2439
        if (exec_needs_network_namespace(c))
76✔
2440
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
8✔
2441
                        return -errno;
×
2442

2443
        if (exec_needs_ipc_namespace(c))
76✔
2444
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
2445
                        return -errno;
×
2446

2447
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_storage_socket, netns_storage_socket, ipcns_storage_socket, ret);
76✔
2448
        if (r < 0)
76✔
2449
                return r;
×
2450

2451
        return 1;
2452
}
2453

2454
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
6,419✔
2455
        ExecSharedRuntime *rt;
6,419✔
2456
        int r;
6,419✔
2457

2458
        assert(m);
6,419✔
2459
        assert(id);
6,419✔
2460
        assert(ret);
6,419✔
2461

2462
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
6,419✔
2463
        if (rt)
6,419✔
2464
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2465
                goto ref;
92✔
2466

2467
        if (!create) {
6,327✔
2468
                *ret = NULL;
×
2469
                return 0;
×
2470
        }
2471

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

2482
ref:
76✔
2483
        /* increment reference counter. */
2484
        rt->n_ref++;
168✔
2485
        *ret = rt;
168✔
2486
        return 1;
168✔
2487
}
2488

2489
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
70✔
2490
        ExecSharedRuntime *rt;
70✔
2491

2492
        assert(m);
70✔
2493
        assert(f);
70✔
2494
        assert(fds);
70✔
2495

2496
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
186✔
2497
                fprintf(f, "exec-runtime=%s", rt->id);
116✔
2498

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

2502
                if (rt->var_tmp_dir)
116✔
2503
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
116✔
2504

2505
                if (rt->userns_storage_socket[0] >= 0) {
116✔
2506
                        int copy;
×
2507

2508
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[0]);
×
2509
                        if (copy < 0)
×
2510
                                return copy;
×
2511

2512
                        fprintf(f, " userns-socket-0=%i", copy);
×
2513
                }
2514

2515
                if (rt->userns_storage_socket[1] >= 0) {
116✔
2516
                        int copy;
×
2517

2518
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[1]);
×
2519
                        if (copy < 0)
×
2520
                                return copy;
2521

2522
                        fprintf(f, " userns-socket-1=%i", copy);
×
2523
                }
2524

2525
                if (rt->netns_storage_socket[0] >= 0) {
116✔
2526
                        int copy;
4✔
2527

2528
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
4✔
2529
                        if (copy < 0)
4✔
2530
                                return copy;
2531

2532
                        fprintf(f, " netns-socket-0=%i", copy);
4✔
2533
                }
2534

2535
                if (rt->netns_storage_socket[1] >= 0) {
116✔
2536
                        int copy;
4✔
2537

2538
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
4✔
2539
                        if (copy < 0)
4✔
2540
                                return copy;
2541

2542
                        fprintf(f, " netns-socket-1=%i", copy);
4✔
2543
                }
2544

2545
                if (rt->ipcns_storage_socket[0] >= 0) {
116✔
2546
                        int copy;
×
2547

2548
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2549
                        if (copy < 0)
×
2550
                                return copy;
2551

2552
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2553
                }
2554

2555
                if (rt->ipcns_storage_socket[1] >= 0) {
116✔
2556
                        int copy;
×
2557

2558
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2559
                        if (copy < 0)
×
2560
                                return copy;
2561

2562
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2563
                }
2564

2565
                fputc('\n', f);
116✔
2566
        }
2567

2568
        return 0;
70✔
2569
}
2570

2571
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
112,296✔
2572
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
112,296✔
2573
        ExecSharedRuntime *rt = NULL;
112,296✔
2574
        int r;
112,296✔
2575

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

2581
        assert(u);
112,296✔
2582
        assert(key);
112,296✔
2583
        assert(value);
112,296✔
2584

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

2592
        if (u->manager) {
112,296✔
2593
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
112,296✔
2594
                        return log_oom();
×
2595

2596
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
112,296✔
2597
        }
2598
        if (!rt) {
112,296✔
2599
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
109,417✔
2600
                        return log_oom();
×
2601

2602
                rt = rt_create;
109,417✔
2603
        }
2604

2605
        if (streq(key, "tmp-dir")) {
112,296✔
2606
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2607
                        return -ENOMEM;
2608

2609
        } else if (streq(key, "var-tmp-dir")) {
112,296✔
2610
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2611
                        return -ENOMEM;
2612

2613
        } else if (streq(key, "netns-socket-0")) {
112,296✔
2614

2615
                safe_close(rt->netns_storage_socket[0]);
×
2616
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2617
                if (rt->netns_storage_socket[0] < 0)
×
2618
                        return 0;
2619

2620
        } else if (streq(key, "netns-socket-1")) {
112,296✔
2621

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

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

2637
                rt_create->manager = u->manager;
×
2638

2639
                /* Avoid cleanup */
2640
                TAKE_PTR(rt_create);
×
2641
        }
2642

2643
        return 1;
2644
}
2645

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

2653
        assert(m);
92✔
2654
        assert(fds);
92✔
2655

2656
        n = strcspn(v, " ");
92✔
2657
        id = strndupa_safe(v, n);
92✔
2658
        if (v[n] != ' ')
92✔
2659
                goto finalize;
×
2660
        p = v + n + 1;
92✔
2661

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

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

2684
        v = startswith(p, "userns-socket-0=");
2✔
2685
        if (v) {
2✔
2686
                char *buf;
×
2687

2688
                n = strcspn(v, " ");
×
2689
                buf = strndupa_safe(v, n);
×
2690

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

2699
        v = startswith(p, "userns-socket-1=");
2✔
2700
        if (v) {
2✔
2701
                char *buf;
×
2702

2703
                n = strcspn(v, " ");
×
2704
                buf = strndupa_safe(v, n);
×
2705

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

2714
        v = startswith(p, "netns-socket-0=");
2✔
2715
        if (v) {
2✔
2716
                char *buf;
2✔
2717

2718
                n = strcspn(v, " ");
2✔
2719
                buf = strndupa_safe(v, n);
2✔
2720

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

2729
        v = startswith(p, "netns-socket-1=");
2✔
2730
        if (v) {
2✔
2731
                char *buf;
2✔
2732

2733
                n = strcspn(v, " ");
2✔
2734
                buf = strndupa_safe(v, n);
2✔
2735

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

2744
        v = startswith(p, "ipcns-socket-0=");
×
2745
        if (v) {
×
2746
                char *buf;
×
2747

2748
                n = strcspn(v, " ");
×
2749
                buf = strndupa_safe(v, n);
×
2750

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

2759
        v = startswith(p, "ipcns-socket-1=");
×
2760
        if (v) {
×
2761
                char *buf;
×
2762

2763
                n = strcspn(v, " ");
×
2764
                buf = strndupa_safe(v, n);
×
2765

2766
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2767
                if (ipcns_fdpair[1] < 0)
×
2768
                        return ipcns_fdpair[1];
2769
        }
2770

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

2778
void exec_shared_runtime_vacuum(Manager *m) {
1,538✔
2779
        ExecSharedRuntime *rt;
1,538✔
2780

2781
        assert(m);
1,538✔
2782

2783
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2784

2785
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,630✔
2786
                if (rt->n_ref > 0)
92✔
2787
                        continue;
92✔
2788

2789
                (void) exec_shared_runtime_free(rt);
×
2790
        }
2791
}
1,538✔
2792

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

2804
        assert(unit);
6,419✔
2805
        assert(context);
6,419✔
2806
        assert(ret);
6,419✔
2807

2808
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
6,419✔
2809
                *ret = NULL;
6,248✔
2810
                return 0;
6,248✔
2811
        }
2812

2813
        if (exec_needs_ephemeral(context)) {
171✔
2814
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2815
                if (r < 0)
×
2816
                        return r;
2817

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

2822
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2823
                        return -errno;
×
2824
        }
2825

2826
        rt = new(ExecRuntime, 1);
171✔
2827
        if (!rt)
171✔
2828
                return -ENOMEM;
2829

2830
        *rt = (ExecRuntime) {
171✔
2831
                .shared = shared,
2832
                .dynamic_creds = creds,
2833
                .ephemeral_copy = TAKE_PTR(ephemeral),
171✔
2834
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
171✔
2835
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
171✔
2836
        };
2837

2838
        *ret = TAKE_PTR(rt);
171✔
2839
        return 1;
171✔
2840
}
2841

2842
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
52,463✔
2843
        if (!rt)
52,463✔
2844
                return NULL;
2845

2846
        exec_shared_runtime_unref(rt->shared);
171✔
2847
        dynamic_creds_unref(rt->dynamic_creds);
171✔
2848

2849
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
171✔
2850

2851
        safe_close_pair(rt->ephemeral_storage_socket);
171✔
2852
        return mfree(rt);
171✔
2853
}
2854

2855
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
6,269✔
2856
        if (!rt)
6,269✔
2857
                return NULL;
2858

2859
        rt->shared = exec_shared_runtime_destroy(rt->shared);
55✔
2860
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
55✔
2861
        return exec_runtime_free(rt);
55✔
2862
}
2863

2864
void exec_runtime_clear(ExecRuntime *rt) {
32✔
2865
        if (!rt)
32✔
2866
                return;
2867

2868
        safe_close_pair(rt->ephemeral_storage_socket);
32✔
2869
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
32✔
2870
}
2871

2872
void exec_params_shallow_clear(ExecParameters *p) {
2,502✔
2873
        if (!p)
2,502✔
2874
                return;
2875

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

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

2893
void exec_params_deep_clear(ExecParameters *p) {
32✔
2894
        if (!p)
32✔
2895
                return;
2896

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

2901
        close_many_unset(p->fds, p->n_socket_fds + p->n_stashed_fds);
32✔
2902

2903
        p->cgroup_path = mfree(p->cgroup_path);
32✔
2904

2905
        if (p->prefix) {
32✔
2906
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
32✔
2907
                p->prefix = mfree(p->prefix);
32✔
2908
        }
2909

2910
        p->received_credentials_directory = mfree(p->received_credentials_directory);
32✔
2911
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
32✔
2912

2913
        if (p->idle_pipe) {
32✔
2914
                close_many_and_free(p->idle_pipe, 4);
×
2915
                p->idle_pipe = NULL;
×
2916
        }
2917

2918
        p->stdin_fd = safe_close(p->stdin_fd);
32✔
2919
        p->stdout_fd = safe_close(p->stdout_fd);
32✔
2920
        p->stderr_fd = safe_close(p->stderr_fd);
32✔
2921
        p->root_directory_fd = safe_close(p->root_directory_fd);
32✔
2922

2923
        p->notify_socket = mfree(p->notify_socket);
32✔
2924

2925
        open_file_free_many(&p->open_files);
32✔
2926

2927
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
32✔
2928

2929
        exec_params_shallow_clear(p);
32✔
2930
}
2931

2932
void exec_directory_done(ExecDirectory *d) {
262,200✔
2933
        if (!d)
262,200✔
2934
                return;
2935

2936
        FOREACH_ARRAY(i, d->items, d->n_items) {
263,826✔
2937
                free(i->path);
1,626✔
2938
                strv_free(i->symlinks);
1,626✔
2939
        }
2940

2941
        d->items = mfree(d->items);
262,200✔
2942
        d->n_items = 0;
262,200✔
2943
        d->mode = 0755;
262,200✔
2944
}
2945

2946
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
4,595✔
2947
        assert(d);
4,595✔
2948
        assert(path);
4,595✔
2949

2950
        FOREACH_ARRAY(i, d->items, d->n_items)
7,019✔
2951
                if (path_equal(i->path, path))
2,439✔
2952
                        return i;
2953

2954
        return NULL;
2955
}
2956

2957
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
4,595✔
2958
        _cleanup_strv_free_ char **s = NULL;
×
2959
        _cleanup_free_ char *p = NULL;
4,595✔
2960
        ExecDirectoryItem *existing;
4,595✔
2961
        int r;
4,595✔
2962

2963
        assert(d);
4,595✔
2964
        assert(path);
4,595✔
2965

2966
        existing = exec_directory_find(d, path);
4,595✔
2967
        if (existing) {
4,595✔
2968
                r = strv_extend(&existing->symlinks, symlink);
15✔
2969
                if (r < 0)
15✔
2970
                        return r;
2971

2972
                existing->flags |= flags;
15✔
2973

2974
                return 0; /* existing item is updated */
15✔
2975
        }
2976

2977
        p = strdup(path);
4,580✔
2978
        if (!p)
4,580✔
2979
                return -ENOMEM;
2980

2981
        if (symlink) {
4,580✔
2982
                s = strv_new(symlink);
6✔
2983
                if (!s)
6✔
2984
                        return -ENOMEM;
2985
        }
2986

2987
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
4,580✔
2988
                return -ENOMEM;
2989

2990
        d->items[d->n_items++] = (ExecDirectoryItem) {
4,580✔
2991
                .path = TAKE_PTR(p),
4,580✔
2992
                .symlinks = TAKE_PTR(s),
4,580✔
2993
                .flags = flags,
2994
        };
2995

2996
        return 1; /* new item is added */
4,580✔
2997
}
2998

2999
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
918✔
3000
        assert(a);
918✔
3001
        assert(b);
918✔
3002

3003
        return path_compare(a->path, b->path);
918✔
3004
}
3005

3006
void exec_directory_sort(ExecDirectory *d) {
144,406✔
3007
        assert(d);
144,406✔
3008

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

3014
        if (d->n_items <= 1)
144,406✔
3015
                return;
3016

3017
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
164✔
3018

3019
        for (size_t i = 1; i < d->n_items; i++)
715✔
3020
                for (size_t j = 0; j < i; j++)
1,836✔
3021
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,300✔
3022
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
3023
                                break;
15✔
3024
                        }
3025
}
3026

3027
ExecCleanMask exec_clean_mask_from_string(const char *s) {
4✔
3028
        ExecDirectoryType t;
4✔
3029

3030
        assert(s);
4✔
3031

3032
        if (streq(s, "all"))
4✔
3033
                return EXEC_CLEAN_ALL;
3034
        if (streq(s, "fdstore"))
3✔
3035
                return EXEC_CLEAN_FDSTORE;
3036

3037
        t = exec_resource_type_from_string(s);
2✔
3038
        if (t < 0)
2✔
3039
                return (ExecCleanMask) t;
3040

3041
        return 1U << t;
2✔
3042
}
3043

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

3055
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,493✔
3056

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

3072
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
33,202✔
3073

3074
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
3075
        [EXEC_UTMP_INIT]  = "init",
3076
        [EXEC_UTMP_LOGIN] = "login",
3077
        [EXEC_UTMP_USER]  = "user",
3078
};
3079

3080
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,550✔
3081

3082
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
3083
        [EXEC_PRESERVE_NO]      = "no",
3084
        [EXEC_PRESERVE_YES]     = "yes",
3085
        [EXEC_PRESERVE_RESTART] = "restart",
3086
};
3087

3088
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
19,387✔
3089

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

3099
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3100

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

3109
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3110

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

3122
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
313✔
3123

3124
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3125
        [EXEC_KEYRING_INHERIT] = "inherit",
3126
        [EXEC_KEYRING_PRIVATE] = "private",
3127
        [EXEC_KEYRING_SHARED]  = "shared",
3128
};
3129

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