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

systemd / systemd / 23518499972

24 Mar 2026 09:45PM UTC coverage: 72.567% (-0.01%) from 72.581%
23518499972

push

github

web-flow
resolved: add "static RRs" concept (#41213)

split out of #40980

301 of 337 new or added lines in 9 files covered. (89.32%)

3451 existing lines in 67 files now uncovered.

316989 of 436822 relevant lines covered (72.57%)

1153112.24 hits per line

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

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

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

73
        if (context->tty_path)
12,098✔
74
                return context->tty_path;
505✔
75

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

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

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

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

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

95
        if (!tty_path)
526✔
96
                tty_path = exec_context_tty_path(context);
270✔
97

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

102
        /* Fill in data from kernel command line if anything is unspecified */
103
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
526✔
104
                (void) proc_cmdline_tty_size(
476✔
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 &&
677✔
114
            exec_context_shall_ansi_seq_reset(context) &&
256✔
115
            isatty_safe(input_fd)) {
105✔
116
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
105✔
117
                if (r < 0)
105✔
118
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
105✔
119
        }
120

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

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

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

137
        if (parameters && parameters->stdout_fd >= 0 && isatty_safe(parameters->stdout_fd))
11,956✔
138
                fd = parameters->stdout_fd;
25✔
139
        else if (path && exec_context_has_tty(context)) {
11,931✔
140
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
256✔
141
                if (fd < 0)
256✔
UNCOV
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();
281✔
150
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
281✔
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))
281✔
153
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
154
        else if (lock_fd < 0)
281✔
155
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
156

157
        if (context->tty_reset)
281✔
158
                (void) terminal_reset_defensive(
218✔
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));
215✔
162

163
        r = exec_context_apply_tty_size(context, fd, fd, path);
281✔
164
        if (r < 0)
281✔
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)) {
527✔
168
                sd_id128_t context_id;
21✔
169

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

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

184
        if (context->tty_vhangup)
281✔
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);
281✔
190

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

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

198
        return context->private_network || context->network_namespace_path;
62,947✔
199
}
200

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

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

208
        return context->private_ipc || context->ipc_namespace_path;
57,544✔
209
}
210

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

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

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

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

234
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
51,009✔
235
}
236

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

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

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

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

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

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

259
bool exec_needs_mount_namespace(const ExecContext *context, const ExecParameters *params) {
33,541✔
260
        assert(context);
33,541✔
261

262
        if (context->root_image ||
33,541✔
263
            context->root_mstack)
33,462✔
264
                return true;
265

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

269
        if (!strv_isempty(context->read_write_paths) ||
33,436✔
270
            !strv_isempty(context->read_only_paths) ||
31,105✔
271
            !strv_isempty(context->inaccessible_paths) ||
31,098✔
272
            !strv_isempty(context->exec_paths) ||
31,079✔
273
            !strv_isempty(context->no_exec_paths))
31,079✔
274
                return true;
275

276
        if (context->n_bind_mounts > 0)
31,079✔
277
                return true;
278

279
        if (context->n_temporary_filesystems > 0)
31,022✔
280
                return true;
281

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

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

288
        if (!strv_isempty(context->extension_directories))
30,809✔
289
                return true;
290

291
        if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
30,804✔
292
                return true;
293

294
        if (context->private_devices ||
30,804✔
295
            context->private_tmp != PRIVATE_TMP_NO || /* no need to check for private_var_tmp here, private_tmp is never demoted to "no" */
29,370✔
296
            context->private_mounts > 0 ||
29,272✔
297
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,783✔
298
            context->protect_system != PROTECT_SYSTEM_NO ||
28,748✔
299
            context->protect_home != PROTECT_HOME_NO ||
28,748✔
300
            context->protect_kernel_tunables ||
28,748✔
301
            context->protect_kernel_modules ||
28,748✔
302
            context->protect_kernel_logs ||
54,081✔
303
            exec_needs_cgroup_mount(context) ||
27,039✔
304
            context->protect_proc != PROTECT_PROC_DEFAULT ||
27,021✔
305
            context->proc_subset != PROC_SUBSET_ALL ||
26,953✔
306
            context->private_bpf != PRIVATE_BPF_NO ||
53,876✔
307
            exec_needs_ipc_namespace(context) ||
53,876✔
308
            exec_needs_pid_namespace(context, params))
26,938✔
309
                return true;
3,919✔
310

311
        if (context->root_directory) {
26,885✔
312
                if (exec_context_get_effective_mount_apivfs(context))
4✔
313
                        return true;
314

315
                for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
316
                        if (params && !params->prefix[t])
×
317
                                continue;
×
318

319
                        if (context->directories[t].n_items > 0)
×
320
                                return true;
321
                }
322
        }
323

324
        if (context->dynamic_user &&
26,881✔
325
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
326
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
327
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
328
                return true;
329

330
        if (exec_context_get_effective_bind_log_sockets(context))
26,881✔
331
                return true;
332

333
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
160,894✔
334
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
139,079✔
335
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,060✔
336
                                return true;
337

338
        return false;
339
}
340

341
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
3,958✔
342
        assert(context);
3,958✔
343
        assert(params);
3,958✔
344

345
        if (!params->notify_socket)
3,958✔
346
                return NULL;
347

348
        if (!needs_sandboxing)
3,226✔
349
                return NULL;
350

351
        if (!exec_context_with_rootfs(context))
3,226✔
352
                return NULL;
353

354
        if (!exec_context_get_effective_mount_apivfs(context))
×
355
                return NULL;
356

357
        if (!FLAGS_SET(params->flags, EXEC_APPLY_CHROOT))
×
358
                return NULL;
×
359

360
        return "/run/host/notify";
361
}
362

363
int exec_log_level_max_with_exec_params(const ExecContext *context, const ExecParameters *params) {
20,508✔
364
        assert(params);
20,508✔
365

366
        if (params->debug_invocation)
20,508✔
367
                return LOG_DEBUG;
368

369
        return exec_log_level_max(context);
20,504✔
370
}
371

372
int exec_log_level_max(const ExecContext *context) {
20,556✔
373
        assert(context);
20,556✔
374
        return context->log_level_max < 0 ? log_get_max_level() : context->log_level_max;
20,556✔
375
}
376

377
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
10,074✔
378
        assert(context);
10,074✔
379

380
        if (!context->dynamic_user)
10,074✔
381
                return false;
382

383
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
106✔
384
                return false;
385

386
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
100✔
387
                return false;
16✔
388

389
        return true;
390
}
391

392
int exec_params_needs_control_subcgroup(const ExecParameters *params) {
2,547✔
393
        /* Keep this in sync with exec_params_get_cgroup_path(). */
394
        return FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE|EXEC_CONTROL_CGROUP|EXEC_IS_CONTROL);
2,547✔
395
}
396

397
int exec_params_get_cgroup_path(
10,971✔
398
                const ExecParameters *params,
399
                const CGroupContext *c,
400
                const char *prefix,
401
                char **ret) {
402

403
        const char *subgroup = NULL;
10,971✔
404
        char *p;
10,971✔
405

406
        assert(params);
10,971✔
407
        assert(c);
10,971✔
408
        assert(ret);
10,971✔
409

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

419
        /* Keep this in sync with exec_params_needs_control_subcgroup(). */
420
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
10,971✔
421
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
712✔
422
                        subgroup = ".control";
423
                else
424
                        subgroup = c->delegate_subgroup;
666✔
425
        }
426

427
        if (subgroup)
666✔
428
                p = path_join(prefix, subgroup);
712✔
429
        else
430
                p = strdup(strempty(prefix));
10,267✔
431
        if (!p)
10,971✔
432
                return -ENOMEM;
433

434
        *ret = p;
10,971✔
435
        return !!subgroup;
10,971✔
436
}
437

438
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
3,041✔
439
        assert(c);
3,041✔
440

441
        return c->cpu_affinity_from_numa;
3,041✔
442
}
443

444
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
2,517✔
445
        assert(unit);
2,517✔
446
        assert(msg);
2,517✔
447
        assert(executable);
2,517✔
448

449
        if (!DEBUG_LOGGING)
2,517✔
450
                return;
2,517✔
451

452
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
5,034✔
453

454
        log_unit_struct(unit, LOG_DEBUG,
2,517✔
455
                        LOG_ITEM("EXECUTABLE=%s", executable),
456
                        LOG_UNIT_MESSAGE(unit, "%s: %s", msg, strnull(cmdline)),
457
                        LOG_UNIT_INVOCATION_ID(unit));
458
}
459

460
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret);
461

462
int exec_spawn(
2,517✔
463
                Unit *unit,
464
                ExecCommand *command,
465
                const ExecContext *context,
466
                ExecParameters *params,
467
                ExecRuntime *runtime,
468
                const CGroupContext *cgroup_context,
469
                PidRef *ret) {
470

471
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL;
2,517✔
472
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
473
        _cleanup_fclose_ FILE *f = NULL;
2,517✔
474
        int r;
2,517✔
475

476
        assert(unit);
2,517✔
477
        assert(unit->manager);
2,517✔
478
        assert(unit->manager->executor_fd >= 0);
2,517✔
479
        assert(unit->manager->executor_path);
2,517✔
480
        assert(command);
2,517✔
481
        assert(context);
2,517✔
482
        assert(params);
2,517✔
483
        assert(params->fds || (params->n_socket_fds + params->n_stashed_fds == 0 && !params->fd_names));
2,517✔
484
        assert(params->n_stashed_fds == 0 || FLAGS_SET(params->flags, EXEC_PASS_FDS));
2,517✔
485
        assert(!params->files_env); /* We fill this field, ensure it comes NULL-initialized to us */
2,517✔
486
        assert(ret);
2,517✔
487

488
        LOG_CONTEXT_PUSH_UNIT(unit);
5,034✔
489

490
        r = exec_context_load_environment(unit, context, &params->files_env);
2,517✔
491
        if (r < 0)
2,517✔
492
                return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
×
493

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

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

505
        const char *cgtarget;
2,517✔
506
        if (exec_params_needs_control_subcgroup(params)) {
2,517✔
507
                r = exec_params_get_cgroup_path(params, cgroup_context, params->cgroup_path, &subcgroup_path);
4✔
508
                if (r < 0)
4✔
509
                        return log_unit_error_errno(unit, r, "Failed to acquire subcgroup path: %m");
×
510
                if (r > 0) {
4✔
511
                        /* If there's a subcgroup, then let's create it here now (the main cgroup was already
512
                         * realized by the unit logic) */
513

514
                        r = cg_create(subcgroup_path);
4✔
515
                        if (r < 0)
4✔
516
                                return log_unit_error_errno(unit, r, "Failed to create subcgroup '%s': %m", subcgroup_path);
×
517
                }
518

519
                cgtarget = subcgroup_path;
4✔
520
        } else
521
                cgtarget = params->cgroup_path;
2,513✔
522

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

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

534
        fdset = fdset_new();
2,517✔
535
        if (!fdset)
2,517✔
536
                return log_oom();
×
537

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

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

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

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

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

561
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,517✔
562
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,517✔
563

564
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,517✔
565
        dual_timestamp start_timestamp;
2,517✔
566

567
        /* Restore the original ambient capability set the manager was started with to pass it to
568
         * sd-executor. */
569
        r = capability_ambient_set_apply(unit->manager->saved_ambient_set, /* also_inherit= */ false);
2,517✔
570
        if (r < 0)
2,517✔
571
                return log_unit_error_errno(unit, r, "Failed to apply the starting ambient set: %m");
×
572

573
        /* Record the start timestamp before we fork so that it is guaranteed to be earlier than the
574
         * handoff timestamp. */
575
        dual_timestamp_now(&start_timestamp);
2,517✔
576

577
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
578
        r = posix_spawn_wrapper(
2,517✔
579
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
2,517✔
580
                        STRV_MAKE(unit->manager->executor_path,
2,517✔
581
                                  "--deserialize", serialization_fd_number,
582
                                  "--log-level", max_log_levels,
583
                                  "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))),
584
                        environ,
585
                        cgtarget,
586
                        &pidref);
587

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

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

605
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
2,517✔
606
                       command->path, pidref.pid, r > 0 ? "via" : "without");
607

608
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,517✔
609

610
        *ret = TAKE_PIDREF(pidref);
2,517✔
611
        return 0;
2,517✔
612
}
613

614
void exec_context_init(ExecContext *c) {
68,922✔
615
        assert(c);
68,922✔
616

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

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

647
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
413,532✔
648
                d->mode = 0755;
344,610✔
649

650
        numa_policy_reset(&c->numa_policy);
68,922✔
651

652
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
68,922✔
653
}
68,922✔
654

655
void exec_context_done(ExecContext *c) {
58,703✔
656
        assert(c);
58,703✔
657

658
        c->environment = strv_free(c->environment);
58,703✔
659
        c->environment_files = strv_free(c->environment_files);
58,703✔
660
        c->pass_environment = strv_free(c->pass_environment);
58,703✔
661
        c->unset_environment = strv_free(c->unset_environment);
58,703✔
662

663
        rlimit_free_all(c->rlimit);
58,703✔
664

665
        for (size_t l = 0; l < 3; l++) {
234,812✔
666
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
176,109✔
667
                c->stdio_file[l] = mfree(c->stdio_file[l]);
176,109✔
668
        }
669

670
        c->working_directory = mfree(c->working_directory);
58,703✔
671
        c->root_directory = mfree(c->root_directory);
58,703✔
672
        c->root_image = mfree(c->root_image);
58,703✔
673
        c->root_image_options = mount_options_free_all(c->root_image_options);
58,703✔
674
        iovec_done(&c->root_hash);
58,703✔
675
        c->root_hash_path = mfree(c->root_hash_path);
58,703✔
676
        iovec_done(&c->root_hash_sig);
58,703✔
677
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
58,703✔
678
        c->root_verity = mfree(c->root_verity);
58,703✔
679
        c->root_mstack = mfree(c->root_mstack);
58,703✔
680
        c->tty_path = mfree(c->tty_path);
58,703✔
681
        c->syslog_identifier = mfree(c->syslog_identifier);
58,703✔
682
        c->user = mfree(c->user);
58,703✔
683
        c->group = mfree(c->group);
58,703✔
684

685
        c->supplementary_groups = strv_free(c->supplementary_groups);
58,703✔
686

687
        c->pam_name = mfree(c->pam_name);
58,703✔
688

689
        c->read_only_paths = strv_free(c->read_only_paths);
58,703✔
690
        c->read_write_paths = strv_free(c->read_write_paths);
58,703✔
691
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
58,703✔
692
        c->exec_paths = strv_free(c->exec_paths);
58,703✔
693
        c->no_exec_paths = strv_free(c->no_exec_paths);
58,703✔
694
        c->exec_search_path = strv_free(c->exec_search_path);
58,703✔
695

696
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
58,703✔
697
        c->bind_mounts = NULL;
58,703✔
698
        c->n_bind_mounts = 0;
58,703✔
699
        mount_image_free_many(c->mount_images, c->n_mount_images);
58,703✔
700
        c->mount_images = NULL;
58,703✔
701
        c->n_mount_images = 0;
58,703✔
702
        mount_image_free_many(c->extension_images, c->n_extension_images);
58,703✔
703
        c->extension_images = NULL;
58,703✔
704
        c->n_extension_images = 0;
58,703✔
705
        c->extension_directories = strv_free(c->extension_directories);
58,703✔
706
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
58,703✔
707
        c->temporary_filesystems = NULL;
58,703✔
708
        c->n_temporary_filesystems = 0;
58,703✔
709

710
        cpu_set_done(&c->cpu_set);
58,703✔
711
        numa_policy_reset(&c->numa_policy);
58,703✔
712

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

718
        c->restrict_filesystems = set_free(c->restrict_filesystems);
58,703✔
719

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

725
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
352,218✔
726
                exec_directory_done(d);
293,515✔
727

728
        c->log_level_max = -1;
58,703✔
729

730
        exec_context_free_log_extra_fields(c);
58,703✔
731
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
58,703✔
732
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
58,703✔
733

734
        c->log_ratelimit = (RateLimit) {};
58,703✔
735

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

739
        c->user_namespace_path = mfree(c->user_namespace_path);
58,703✔
740
        c->network_namespace_path = mfree(c->network_namespace_path);
58,703✔
741
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
58,703✔
742

743
        c->log_namespace = mfree(c->log_namespace);
58,703✔
744

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

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

753
        c->private_hostname = mfree(c->private_hostname);
58,703✔
754
}
58,703✔
755

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

759
        if (!runtime_prefix)
6,583✔
760
                return 0;
761

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

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

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

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

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

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

790
        return 0;
791
}
792

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

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

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

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

807
        return 0;
808
}
809

810
void exec_command_done(ExecCommand *c) {
124,293✔
811
        assert(c);
124,293✔
812

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

817
void exec_command_done_array(ExecCommand *c, size_t n) {
35,122✔
818
        FOREACH_ARRAY(i, c, n)
140,486✔
819
                exec_command_done(i);
105,364✔
820
}
35,122✔
821

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

826
        exec_command_done(c);
18,895✔
827
        return mfree(c);
18,895✔
828
}
829

830
ExecCommand* exec_command_free_list(ExecCommand *c) {
165,390✔
831
        ExecCommand *i;
165,390✔
832

833
        while ((i = LIST_POP(command, c)))
184,285✔
834
                exec_command_free(i);
18,895✔
835

836
        return NULL;
165,390✔
837
}
838

839
void exec_command_free_array(ExecCommand **c, size_t n) {
23,547✔
840
        FOREACH_ARRAY(i, c, n)
188,913✔
841
                *i = exec_command_free_list(*i);
165,366✔
842
}
23,547✔
843

844
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
8,607✔
845
        FOREACH_ARRAY(i, c, n)
34,427✔
846
                exec_status_reset(&i->exec_status);
25,820✔
847
}
8,607✔
848

849
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
5,700✔
850
        FOREACH_ARRAY(i, c, n)
40,665✔
851
                LIST_FOREACH(command, z, *i)
37,391✔
852
                        exec_status_reset(&z->exec_status);
2,426✔
853
}
5,700✔
854

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

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

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

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

869
        switch (fd_index) {
39,885✔
870

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

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

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

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

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

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

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

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

898
        assert(c);
2,517✔
899
        assert(ret);
2,517✔
900

901
        STRV_FOREACH(i, c->environment_files) {
2,520✔
902
                _cleanup_strv_free_ char **paths = NULL;
3✔
903
                bool ignore = false;
3✔
904
                char *fn = *i;
3✔
905

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

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

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

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

928
                STRV_FOREACH(path, paths) {
2✔
929
                        _cleanup_strv_free_ char **p = NULL;
1✔
930

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

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

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

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

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

960
        *ret = TAKE_PTR(v);
2,517✔
961

962
        return 0;
2,517✔
963
}
964

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

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

971
        tty = skip_dev_prefix(tty);
257✔
972

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

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

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

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

987
        return ec->tty_reset ||
57,799✔
988
                ec->tty_vhangup ||
28,778✔
989
                ec->tty_vt_disallocate ||
28,778✔
990
                exec_input_is_terminal(ec->std_input) ||
28,778✔
991
                ec->std_output == EXEC_OUTPUT_TTY ||
57,799✔
992
                ec->std_error == EXEC_OUTPUT_TTY;
28,729✔
993
}
994

995
bool exec_context_may_touch_console(const ExecContext *ec) {
27,318✔
996

997
        return exec_context_may_touch_tty(ec) &&
27,575✔
998
               tty_may_match_dev_console(exec_context_tty_path(ec));
257✔
999
}
1000

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

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

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

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

1020
static void strv_fprintf(FILE *f, char **l) {
2✔
1021
        assert(f);
2✔
1022

1023
        STRV_FOREACH(g, l)
6✔
1024
                fprintf(f, " %s", *g);
4✔
1025
}
2✔
1026

1027
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
3,392✔
1028
        assert(f);
3,392✔
1029
        assert(prefix);
3,392✔
1030
        assert(name);
3,392✔
1031

1032
        if (!strv_isempty(strv)) {
3,392✔
1033
                fprintf(f, "%s%s:", prefix, name);
2✔
1034
                strv_fprintf(f, strv);
2✔
1035
                fputs("\n", f);
2✔
1036
        }
1037
}
3,392✔
1038

1039
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1040
        assert(p);
×
1041
        assert(f);
×
1042

1043
        prefix = strempty(prefix);
×
1044

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

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

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

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

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

1084
        assert(c);
424✔
1085
        assert(f);
424✔
1086

1087
        prefix = strempty(prefix);
424✔
1088

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

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

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

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

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

1168
        if (c->root_image_options) {
424✔
1169
                _cleanup_free_ char *opts_str = NULL;
×
1170

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

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

1182
        if (c->root_hash_path)
424✔
1183
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1184

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

1193
        if (c->root_hash_sig_path)
424✔
1194
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1195

1196
        if (c->root_verity)
424✔
1197
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1198

1199
        if (c->root_mstack)
424✔
1200
                fprintf(f, "%sRootMStack: %s\n", prefix, c->root_mstack);
×
1201

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

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

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

1211
        STRV_FOREACH(e, c->unset_environment)
439✔
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));
424✔
1215

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

1219
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
2,143✔
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));
424✔
1233

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

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

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

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

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

1254
        if (c->ioprio_is_set) {
424✔
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) {
424✔
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) {
424✔
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))) {
424✔
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)
424✔
1294
                fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
1✔
1295

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

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

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

1326
        if (c->tty_path)
424✔
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,
424✔
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;
400✔
1353

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

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

1363
        if (c->log_level_max >= 0) {
424✔
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)
424✔
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)
424✔
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)) {
424✔
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) {
428✔
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)
424✔
1397
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1398

1399
        if (c->secure_bits) {
424✔
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) {
424✔
1408
                _cleanup_free_ char *str = NULL;
424✔
1409

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

1415
        if (c->capability_ambient_set != 0) {
424✔
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)
424✔
1424
                fprintf(f, "%sUser: %s\n", prefix, c->user);
7✔
1425
        if (c->group)
424✔
1426
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1427

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

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

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

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

1442
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
428✔
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)
424✔
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)
424✔
1457
                fprintf(f,
2✔
1458
                        "%sUtmpIdentifier: %s\n",
1459
                        prefix, c->utmp_id);
1460

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

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

1471
        if (c->smack_process_label)
424✔
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)
424✔
1477
                fprintf(f,
1✔
1478
                        "%sPersonality: %s\n",
1479
                        prefix, strna(personality_to_string(c->personality)));
1480

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

1485
        if (c->syscall_filter) {
424✔
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,404✔
1498
                                _cleanup_free_ char *name = NULL;
9,380✔
1499
                                const char *errno_name = NULL;
9,380✔
1500
                                int num = PTR_TO_INT(val);
9,380✔
1501

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

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

1510
                                if (num >= 0) {
9,380✔
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) {
424✔
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)) {
424✔
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)) {
424✔
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)
424✔
1555
                fprintf(f,
×
1556
                        "%sUserNamespacePath: %s\n",
1557
                        prefix, c->user_namespace_path);
1558

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

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

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

1577
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
424✔
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) {
424✔
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);
424✔
1605
}
424✔
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) {
6,082✔
1623
        int p;
6,082✔
1624

1625
        assert(c);
6,082✔
1626

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

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

1634
        return ioprio_normalize(p);
6,068✔
1635
}
1636

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

1640
        /* Explicit setting wins */
1641
        if (c->mount_apivfs >= 0)
39,988✔
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,869✔
1646
                return true;
227✔
1647

1648
        return false;
1649
}
1650

1651
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
32,407✔
1652
        assert(c);
32,407✔
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)
32,407✔
1657
                return true;
1658

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

1662
        if (exec_context_get_effective_mount_apivfs(c))
32,397✔
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)
32,248✔
1667
                return true;
×
1668

1669
        return false;
1670
}
1671

1672
void exec_context_free_log_extra_fields(ExecContext *c) {
58,705✔
1673
        assert(c);
58,705✔
1674

1675
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
59,221✔
1676
                free(field->iov_base);
516✔
1677

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

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

1688
        assert(c);
1,703✔
1689

1690
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1691
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,703✔
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,703✔
1697
                return;
1698

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

1703
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
35✔
1704
        if (fd < 0)
35✔
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)
35✔
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))
35✔
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);
35✔
1721
        if (r < 0)
35✔
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);
35✔
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) {
3,025✔
1784
        ExecCleanMask mask = 0;
3,025✔
1785

1786
        assert(c);
3,025✔
1787
        assert(ret);
3,025✔
1788

1789
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
18,150✔
1790
                if (c->directories[t].n_items > 0)
15,125✔
1791
                        mask |= 1U << t;
311✔
1792

1793
        *ret = mask;
3,025✔
1794
        return 0;
3,025✔
1795
}
1796

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

1800
        assert(c);
3,041✔
1801

1802
        if (c->oom_score_adjust_set)
3,041✔
1803
                return c->oom_score_adjust;
678✔
1804

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

1809
        return n;
2,363✔
1810
}
1811

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

1817
        assert(c);
3,041✔
1818

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

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

1831
        return n;
3,041✔
1832
}
1833

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

1837
        assert(c);
3,041✔
1838

1839
        if (c->nice_set)
3,041✔
1840
                return c->nice;
8✔
1841

1842
        errno = 0;
3,033✔
1843
        n = getpriority(PRIO_PROCESS, 0);
3,033✔
1844
        if (errno > 0) {
3,033✔
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) {
3,041✔
1853
        int n;
3,041✔
1854

1855
        assert(c);
3,041✔
1856

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

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

1864
        return n < 0 ? SCHED_OTHER : n;
3,041✔
1865
}
1866

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

1871
        assert(c);
3,041✔
1872

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

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

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

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

1886
        assert(c);
3,041✔
1887

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

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

1895
        return (uint64_t) MAX(r, 0);
3,041✔
1896
}
1897

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

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

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

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

1910
        assert(c);
3,041✔
1911

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

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

1923
                if (c->syscall_allow_list && num >= 0)
11,423✔
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,423✔
1928
                if (!name)
11,423✔
1929
                        continue;
×
1930

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

1942
                if (strv_consume(&l, s) < 0)
11,423✔
1943
                        return NULL;
1944
        }
1945

1946
        strv_sort(l);
3,041✔
1947
#endif
1948

1949
        return l ? TAKE_PTR(l) : strv_new(NULL);
3,041✔
1950
}
1951

1952
char** exec_context_get_syscall_archs(const ExecContext *c) {
3,041✔
1953
        _cleanup_strv_free_ char **l = NULL;
3,041✔
1954

1955
        assert(c);
3,041✔
1956

1957
#if HAVE_SECCOMP
1958
        void *id;
3,041✔
1959
        SET_FOREACH(id, c->syscall_archs) {
3,074✔
1960
                const char *name;
33✔
1961

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

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

1970
        strv_sort(l);
3,041✔
1971
#endif
1972

1973
        return l ? TAKE_PTR(l) : strv_new(NULL);
3,041✔
1974
}
1975

1976
char** exec_context_get_syscall_log(const ExecContext *c) {
3,041✔
1977
        _cleanup_strv_free_ char **l = NULL;
3,041✔
1978

1979
        assert(c);
3,041✔
1980

1981
#if HAVE_SECCOMP
1982
        if (dlopen_libseccomp() < 0)
3,041✔
1983
                return strv_new(NULL);
×
1984

1985
        void *id, *val;
3,041✔
1986
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
3,041✔
1987
                char *name = NULL;
×
1988

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

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

1997
        strv_sort(l);
3,041✔
1998
#endif
1999

2000
        return l ? TAKE_PTR(l) : strv_new(NULL);
3,041✔
2001
}
2002

2003
char** exec_context_get_address_families(const ExecContext *c) {
3,041✔
2004
        _cleanup_strv_free_ char **l = NULL;
3,041✔
2005
        void *af;
3,041✔
2006

2007
        assert(c);
3,041✔
2008

2009
        SET_FOREACH(af, c->address_families) {
3,147✔
2010
                const char *name;
106✔
2011

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

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

2020
        strv_sort(l);
3,041✔
2021

2022
        return l ? TAKE_PTR(l) : strv_new(NULL);
3,041✔
2023
}
2024

2025
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
3,041✔
2026
        assert(c);
3,041✔
2027

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

2033
        return strv_sort(l);
3,041✔
2034
#else
2035
        return strv_new(NULL);
2036
#endif
2037
}
2038

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

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

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

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

2052
bool exec_context_with_rootfs(const ExecContext *c) {
75,907✔
2053
        assert(c);
75,907✔
2054

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

2057
        return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd || c->root_mstack;
75,907✔
2058
}
2059

2060
bool exec_context_with_rootfs_strict(const ExecContext *c) {
1,407✔
2061
        assert(c);
1,407✔
2062

2063
        /* just like exec_context_with_rootfs(), but doesn't suppress a root directory of "/", i.e. returns
2064
         * true in more cases: when a root directory is explicitly configured, even if it's our usual
2065
         * root. */
2066

2067
        return c->root_directory || c->root_image || c->root_directory_as_fd || c->root_mstack;
1,407✔
2068
}
2069

2070
int exec_context_has_vpicked_extensions(const ExecContext *context) {
15,226✔
2071
        int r;
15,226✔
2072

2073
        assert(context);
15,226✔
2074

2075
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
15,282✔
2076
                r = path_uses_vpick(mi->source);
56✔
2077
                if (r != 0)
56✔
2078
                        return r;
2079
        }
2080
        STRV_FOREACH(ed, context->extension_directories) {
15,227✔
2081
                r = path_uses_vpick(*ed);
1✔
2082
                if (r != 0)
1✔
2083
                        return r;
2084
        }
2085

2086
        return 0;
2087
}
2088

2089
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
5,170✔
2090
        assert(s);
5,170✔
2091

2092
        *s = (ExecStatus) {
5,170✔
2093
                .pid = pid,
2094
        };
2095

2096
        if (ts)
5,170✔
2097
                s->start_timestamp = *ts;
5,170✔
2098
        else
2099
                dual_timestamp_now(&s->start_timestamp);
×
2100
}
5,170✔
2101

2102
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,333✔
2103
        assert(s);
2,333✔
2104

2105
        if (s->pid != pid)
2,333✔
2106
                *s = (ExecStatus) {
4✔
2107
                        .pid = pid,
2108
                };
2109

2110
        dual_timestamp_now(&s->exit_timestamp);
2,333✔
2111

2112
        s->code = code;
2,333✔
2113
        s->status = status;
2,333✔
2114

2115
        if (context && context->utmp_id)
2,333✔
2116
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2117
}
2,333✔
2118

2119
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
8,598✔
2120
        assert(s);
8,598✔
2121
        assert(ucred);
8,598✔
2122
        assert(ts);
8,598✔
2123

2124
        if (ucred->pid != s->pid)
8,598✔
2125
                *s = (ExecStatus) {
7✔
2126
                        .pid = ucred->pid,
2127
                };
2128

2129
        s->handoff_timestamp = *ts;
8,598✔
2130
}
8,598✔
2131

2132
void exec_status_reset(ExecStatus *s) {
30,401✔
2133
        assert(s);
30,401✔
2134

2135
        *s = (ExecStatus) {};
30,401✔
2136
}
30,401✔
2137

2138
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
225✔
2139
        assert(s);
225✔
2140
        assert(f);
225✔
2141

2142
        if (s->pid <= 0)
225✔
2143
                return;
2144

2145
        prefix = strempty(prefix);
41✔
2146

2147
        fprintf(f,
41✔
2148
                "%sPID: "PID_FMT"\n",
2149
                prefix, s->pid);
2150

2151
        if (dual_timestamp_is_set(&s->start_timestamp))
41✔
2152
                fprintf(f,
41✔
2153
                        "%sStart Timestamp: %s\n",
2154
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
41✔
2155

2156
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
41✔
2157
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
40✔
2158
                fprintf(f,
40✔
2159
                        "%sHandoff Timestamp: %s since start\n",
2160
                        prefix,
2161
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
80✔
2162
        else
2163
                fprintf(f,
1✔
2164
                        "%sHandoff Timestamp: %s\n",
2165
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
1✔
2166

2167
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
41✔
2168

2169
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
19✔
2170
                        fprintf(f,
19✔
2171
                                "%sExit Timestamp: %s since handoff\n",
2172
                                prefix,
2173
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
38✔
2174
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2175
                        fprintf(f,
×
2176
                                "%sExit Timestamp: %s since start\n",
2177
                                prefix,
2178
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2179
                else
2180
                        fprintf(f,
×
2181
                                "%sExit Timestamp: %s\n",
2182
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2183

2184
                fprintf(f,
19✔
2185
                        "%sExit Code: %s\n"
2186
                        "%sExit Status: %i\n",
2187
                        prefix, sigchld_code_to_string(s->code),
19✔
2188
                        prefix, s->status);
19✔
2189
        }
2190
}
2191

2192
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
225✔
2193
        _cleanup_free_ char *cmd = NULL;
450✔
2194
        const char *prefix2;
225✔
2195

2196
        assert(c);
225✔
2197
        assert(f);
225✔
2198

2199
        prefix = strempty(prefix);
225✔
2200
        prefix2 = strjoina(prefix, "\t");
1,125✔
2201

2202
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
225✔
2203

2204
        fprintf(f,
225✔
2205
                "%sCommand Line: %s\n",
2206
                prefix, strnull(cmd));
2207

2208
        exec_status_dump(&c->exec_status, f, prefix2);
225✔
2209
}
225✔
2210

2211
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
220✔
2212
        assert(f);
220✔
2213

2214
        prefix = strempty(prefix);
220✔
2215

2216
        LIST_FOREACH(command, i, c)
444✔
2217
                exec_command_dump(i, f, prefix);
224✔
2218
}
220✔
2219

2220
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
18,895✔
2221
        ExecCommand *end;
18,895✔
2222

2223
        assert(l);
18,895✔
2224
        assert(e);
18,895✔
2225

2226
        if (*l) {
18,895✔
2227
                /* It's kind of important, that we keep the order here */
2228
                end = LIST_FIND_TAIL(command, *l);
374✔
2229
                LIST_INSERT_AFTER(command, *l, end, e);
137✔
2230
        } else
2231
                *l = e;
18,758✔
2232
}
18,895✔
2233

2234
int exec_command_set(ExecCommand *c, const char *path, ...) {
220✔
2235
        va_list ap;
220✔
2236
        char **l, *p;
220✔
2237

2238
        assert(c);
220✔
2239
        assert(path);
220✔
2240

2241
        va_start(ap, path);
220✔
2242
        l = strv_new_ap(path, ap);
220✔
2243
        va_end(ap);
220✔
2244

2245
        if (!l)
220✔
2246
                return -ENOMEM;
220✔
2247

2248
        p = strdup(path);
220✔
2249
        if (!p) {
220✔
2250
                strv_free(l);
×
2251
                return -ENOMEM;
×
2252
        }
2253

2254
        free_and_replace(c->path, p);
220✔
2255

2256
        return strv_free_and_replace(c->argv, l);
220✔
2257
}
2258

2259
int exec_command_append(ExecCommand *c, const char *path, ...) {
326✔
2260
        char **l;
326✔
2261
        va_list ap;
326✔
2262
        int r;
326✔
2263

2264
        assert(c);
326✔
2265
        assert(path);
326✔
2266

2267
        va_start(ap, path);
326✔
2268
        l = strv_new_ap(path, ap);
326✔
2269
        va_end(ap);
326✔
2270

2271
        if (!l)
326✔
2272
                return -ENOMEM;
326✔
2273

2274
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates= */ false);
326✔
2275
        if (r < 0)
326✔
2276
                return r;
×
2277

2278
        return 0;
2279
}
2280

2281
static char *destroy_tree(char *path) {
292✔
2282
        if (!path)
292✔
2283
                return NULL;
2284

2285
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
65✔
2286
                log_debug("Spawning process to nuke '%s'", path);
65✔
2287

2288
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
65✔
2289
        }
2290

2291
        return mfree(path);
65✔
2292
}
2293

2294
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
142,812✔
2295
        assert(rt);
142,812✔
2296

2297
        if (rt->manager)
142,812✔
2298
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
175✔
2299

2300
        rt->id = mfree(rt->id);
142,812✔
2301
        rt->tmp_dir = mfree(rt->tmp_dir);
142,812✔
2302
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
142,812✔
2303
        safe_close_pair(rt->userns_storage_socket);
142,812✔
2304
        safe_close_pair(rt->netns_storage_socket);
142,812✔
2305
        safe_close_pair(rt->ipcns_storage_socket);
142,812✔
2306
}
142,812✔
2307

2308
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
142,778✔
2309
        if (!rt)
142,778✔
2310
                return NULL;
2311

2312
        exec_shared_runtime_done(rt);
142,778✔
2313
        return mfree(rt);
142,778✔
2314
}
2315

2316
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
190✔
2317
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
145,908✔
2318

2319
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
66✔
2320
        if (!rt)
66✔
2321
                return NULL;
2322

2323
        assert(rt->n_ref > 0);
51✔
2324
        rt->n_ref--;
51✔
2325

2326
        if (rt->n_ref > 0)
51✔
2327
                return NULL;
2328

2329
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
51✔
2330
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
51✔
2331

2332
        return exec_shared_runtime_free(rt);
51✔
2333
}
2334

2335
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
142,778✔
2336
        _cleanup_free_ char *id_copy = NULL;
285,556✔
2337
        ExecSharedRuntime *n;
142,778✔
2338

2339
        assert(ret);
142,778✔
2340

2341
        id_copy = strdup(id);
142,778✔
2342
        if (!id_copy)
142,778✔
2343
                return -ENOMEM;
2344

2345
        n = new(ExecSharedRuntime, 1);
142,778✔
2346
        if (!n)
142,778✔
2347
                return -ENOMEM;
2348

2349
        *n = (ExecSharedRuntime) {
142,778✔
2350
                .id = TAKE_PTR(id_copy),
142,778✔
2351
                .userns_storage_socket = EBADF_PAIR,
2352
                .netns_storage_socket = EBADF_PAIR,
2353
                .ipcns_storage_socket = EBADF_PAIR,
2354
        };
2355

2356
        *ret = n;
142,778✔
2357
        return 0;
142,778✔
2358
}
2359

2360
static int exec_shared_runtime_add(
175✔
2361
                Manager *m,
2362
                const char *id,
2363
                char **tmp_dir,
2364
                char **var_tmp_dir,
2365
                int userns_storage_socket[2],
2366
                int netns_storage_socket[2],
2367
                int ipcns_storage_socket[2],
2368
                ExecSharedRuntime **ret) {
2369

2370
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
175✔
2371
        int r;
175✔
2372

2373
        assert(m);
175✔
2374
        assert(id);
175✔
2375

2376
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2377

2378
        r = exec_shared_runtime_allocate(&rt, id);
175✔
2379
        if (r < 0)
175✔
2380
                return r;
2381

2382
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
175✔
2383
        if (r < 0)
175✔
2384
                return r;
2385

2386
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
175✔
2387
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
175✔
2388

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

2394
        if (netns_storage_socket) {
175✔
2395
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
175✔
2396
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
175✔
2397
        }
2398

2399
        if (ipcns_storage_socket) {
175✔
2400
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
175✔
2401
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
175✔
2402
        }
2403

2404
        rt->manager = m;
175✔
2405

2406
        if (ret)
175✔
2407
                *ret = rt;
75✔
2408
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2409
        TAKE_PTR(rt);
175✔
2410
        return 0;
175✔
2411
}
2412

2413
static int exec_shared_runtime_make(
8,014✔
2414
                Manager *m,
2415
                const ExecContext *c,
2416
                const char *id,
2417
                ExecSharedRuntime **ret) {
2418

2419
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
8,014✔
2420
        _cleanup_close_pair_ int userns_storage_socket[2] = EBADF_PAIR, netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
24,042✔
2421
        int r;
8,014✔
2422

2423
        assert(m);
8,014✔
2424
        assert(c);
8,014✔
2425
        assert(id);
8,014✔
2426

2427
        /* It is not necessary to create ExecSharedRuntime object. */
2428
        if (!c->user_namespace_path && !exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) &&
8,014✔
2429
            c->private_tmp != PRIVATE_TMP_CONNECTED && c->private_var_tmp != PRIVATE_TMP_CONNECTED) {
8,005✔
2430
                *ret = NULL;
7,939✔
2431
                return 0;
7,939✔
2432
        }
2433

2434
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
107✔
2435
            !prefixed_path_strv_contains(c->inaccessible_paths, "/tmp")) {
32✔
2436

2437
                r = setup_tmp_dir_one(id, "/tmp", &tmp_dir);
32✔
2438
                if (r < 0)
32✔
2439
                        return r;
2440
        }
2441

2442
        if (c->private_var_tmp == PRIVATE_TMP_CONNECTED &&
144✔
2443
            !prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") &&
138✔
2444
            !prefixed_path_strv_contains(c->inaccessible_paths, "/var")) {
69✔
2445

2446
                r = setup_tmp_dir_one(id, "/var/tmp", &var_tmp_dir);
69✔
2447
                if (r < 0)
69✔
2448
                        return r;
2449
        }
2450

2451
        if (c->user_namespace_path)
75✔
2452
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, userns_storage_socket) < 0)
×
2453
                        return -errno;
×
2454

2455
        if (exec_needs_network_namespace(c))
75✔
2456
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
8✔
2457
                        return -errno;
×
2458

2459
        if (exec_needs_ipc_namespace(c))
75✔
2460
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
2461
                        return -errno;
×
2462

2463
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_storage_socket, netns_storage_socket, ipcns_storage_socket, ret);
75✔
2464
        if (r < 0)
75✔
2465
                return r;
×
2466

2467
        return 1;
2468
}
2469

2470
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
8,114✔
2471
        ExecSharedRuntime *rt;
8,114✔
2472
        int r;
8,114✔
2473

2474
        assert(m);
8,114✔
2475
        assert(id);
8,114✔
2476
        assert(ret);
8,114✔
2477

2478
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
8,114✔
2479
        if (rt)
8,114✔
2480
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2481
                goto ref;
100✔
2482

2483
        if (!create) {
8,014✔
2484
                *ret = NULL;
×
2485
                return 0;
×
2486
        }
2487

2488
        /* If not found, then create a new object. */
2489
        r = exec_shared_runtime_make(m, c, id, &rt);
8,014✔
2490
        if (r < 0)
8,014✔
2491
                return r;
2492
        if (r == 0) {
8,014✔
2493
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2494
                *ret = NULL;
7,939✔
2495
                return 0;
7,939✔
2496
        }
2497

2498
ref:
75✔
2499
        /* increment reference counter. */
2500
        rt->n_ref++;
175✔
2501
        *ret = rt;
175✔
2502
        return 1;
175✔
2503
}
2504

2505
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
104✔
2506
        ExecSharedRuntime *rt;
104✔
2507

2508
        assert(m);
104✔
2509
        assert(f);
104✔
2510
        assert(fds);
104✔
2511

2512
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
228✔
2513
                fprintf(f, "exec-runtime=%s", rt->id);
124✔
2514

2515
                if (rt->tmp_dir)
124✔
2516
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
62✔
2517

2518
                if (rt->var_tmp_dir)
124✔
2519
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
124✔
2520

2521
                if (rt->userns_storage_socket[0] >= 0) {
124✔
2522
                        int copy;
×
2523

2524
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[0]);
×
2525
                        if (copy < 0)
×
2526
                                return copy;
×
2527

2528
                        fprintf(f, " userns-socket-0=%i", copy);
×
2529
                }
2530

2531
                if (rt->userns_storage_socket[1] >= 0) {
124✔
2532
                        int copy;
×
2533

2534
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[1]);
×
2535
                        if (copy < 0)
×
2536
                                return copy;
2537

2538
                        fprintf(f, " userns-socket-1=%i", copy);
×
2539
                }
2540

2541
                if (rt->netns_storage_socket[0] >= 0) {
124✔
2542
                        int copy;
4✔
2543

2544
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
4✔
2545
                        if (copy < 0)
4✔
2546
                                return copy;
2547

2548
                        fprintf(f, " netns-socket-0=%i", copy);
4✔
2549
                }
2550

2551
                if (rt->netns_storage_socket[1] >= 0) {
124✔
2552
                        int copy;
4✔
2553

2554
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
4✔
2555
                        if (copy < 0)
4✔
2556
                                return copy;
2557

2558
                        fprintf(f, " netns-socket-1=%i", copy);
4✔
2559
                }
2560

2561
                if (rt->ipcns_storage_socket[0] >= 0) {
124✔
2562
                        int copy;
×
2563

2564
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2565
                        if (copy < 0)
×
2566
                                return copy;
2567

2568
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2569
                }
2570

2571
                if (rt->ipcns_storage_socket[1] >= 0) {
124✔
2572
                        int copy;
×
2573

2574
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2575
                        if (copy < 0)
×
2576
                                return copy;
2577

2578
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2579
                }
2580

2581
                fputc('\n', f);
124✔
2582
        }
2583

2584
        return 0;
104✔
2585
}
2586

2587
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
145,733✔
2588
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
145,733✔
2589
        ExecSharedRuntime *rt = NULL;
145,733✔
2590
        int r;
145,733✔
2591

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

2597
        assert(u);
145,733✔
2598
        assert(key);
145,733✔
2599
        assert(value);
145,733✔
2600

2601
        /* Manager manages ExecSharedRuntime objects by the unit id.
2602
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2603
        if (isempty(u->id)) {
145,733✔
2604
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
2605
                return 0;
×
2606
        }
2607

2608
        if (u->manager) {
145,733✔
2609
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
145,733✔
2610
                        return log_oom();
×
2611

2612
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
145,733✔
2613
        }
2614
        if (!rt) {
145,733✔
2615
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
142,603✔
2616
                        return log_oom();
×
2617

2618
                rt = rt_create;
142,603✔
2619
        }
2620

2621
        if (streq(key, "tmp-dir")) {
145,733✔
2622
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2623
                        return -ENOMEM;
2624

2625
        } else if (streq(key, "var-tmp-dir")) {
145,733✔
2626
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2627
                        return -ENOMEM;
2628

2629
        } else if (streq(key, "netns-socket-0")) {
145,733✔
2630

2631
                safe_close(rt->netns_storage_socket[0]);
×
2632
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2633
                if (rt->netns_storage_socket[0] < 0)
×
2634
                        return 0;
2635

2636
        } else if (streq(key, "netns-socket-1")) {
145,733✔
2637

2638
                safe_close(rt->netns_storage_socket[1]);
×
2639
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2640
                if (rt->netns_storage_socket[1] < 0)
×
2641
                        return 0;
2642
        } else
2643
                return 0;
2644

2645
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
2646
        if (rt_create && u->manager) {
×
2647
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
2648
                if (r < 0) {
×
2649
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
2650
                        return 0;
×
2651
                }
2652

2653
                rt_create->manager = u->manager;
×
2654

2655
                /* Avoid cleanup */
2656
                TAKE_PTR(rt_create);
×
2657
        }
2658

2659
        return 1;
2660
}
2661

2662
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
100✔
2663
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
100✔
2664
        char *id = NULL;
100✔
2665
        int r, userns_fdpair[] = {-1, -1}, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
100✔
2666
        const char *p, *v = ASSERT_PTR(value);
100✔
2667
        size_t n;
100✔
2668

2669
        assert(m);
100✔
2670
        assert(fds);
100✔
2671

2672
        n = strcspn(v, " ");
100✔
2673
        id = strndupa_safe(v, n);
100✔
2674
        if (v[n] != ' ')
100✔
2675
                goto finalize;
×
2676
        p = v + n + 1;
100✔
2677

2678
        v = startswith(p, "tmp-dir=");
100✔
2679
        if (v) {
100✔
2680
                n = strcspn(v, " ");
50✔
2681
                tmp_dir = strndup(v, n);
50✔
2682
                if (!tmp_dir)
50✔
2683
                        return log_oom();
×
2684
                if (v[n] != ' ')
50✔
2685
                        goto finalize;
×
2686
                p = v + n + 1;
50✔
2687
        }
2688

2689
        v = startswith(p, "var-tmp-dir=");
100✔
2690
        if (v) {
100✔
2691
                n = strcspn(v, " ");
100✔
2692
                var_tmp_dir = strndup(v, n);
100✔
2693
                if (!var_tmp_dir)
100✔
2694
                        return log_oom();
×
2695
                if (v[n] != ' ')
100✔
2696
                        goto finalize;
98✔
2697
                p = v + n + 1;
2✔
2698
        }
2699

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

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

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

2715
        v = startswith(p, "userns-socket-1=");
2✔
2716
        if (v) {
2✔
2717
                char *buf;
×
2718

2719
                n = strcspn(v, " ");
×
2720
                buf = strndupa_safe(v, n);
×
2721

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

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

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

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

2745
        v = startswith(p, "netns-socket-1=");
2✔
2746
        if (v) {
2✔
2747
                char *buf;
2✔
2748

2749
                n = strcspn(v, " ");
2✔
2750
                buf = strndupa_safe(v, n);
2✔
2751

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

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

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

2767
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2768
                if (ipcns_fdpair[0] < 0)
×
2769
                        return ipcns_fdpair[0];
2770
                if (v[n] != ' ')
×
2771
                        goto finalize;
×
2772
                p = v + n + 1;
×
2773
        }
2774

2775
        v = startswith(p, "ipcns-socket-1=");
×
2776
        if (v) {
×
2777
                char *buf;
×
2778

2779
                n = strcspn(v, " ");
×
2780
                buf = strndupa_safe(v, n);
×
2781

2782
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2783
                if (ipcns_fdpair[1] < 0)
×
2784
                        return ipcns_fdpair[1];
2785
        }
2786

2787
finalize:
×
2788
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_fdpair, netns_fdpair, ipcns_fdpair, NULL);
100✔
2789
        if (r < 0)
100✔
2790
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2791
        return 0;
2792
}
2793

2794
void exec_shared_runtime_vacuum(Manager *m) {
1,632✔
2795
        ExecSharedRuntime *rt;
1,632✔
2796

2797
        assert(m);
1,632✔
2798

2799
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2800

2801
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,732✔
2802
                if (rt->n_ref > 0)
100✔
2803
                        continue;
100✔
2804

2805
                (void) exec_shared_runtime_free(rt);
×
2806
        }
2807
}
1,632✔
2808

2809
int exec_runtime_make(
8,114✔
2810
                const Unit *unit,
2811
                const ExecContext *context,
2812
                ExecSharedRuntime *shared,
2813
                DynamicCreds *creds,
2814
                ExecRuntime **ret) {
2815
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
8,114✔
2816
        _cleanup_free_ char *ephemeral = NULL;
8,114✔
2817
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
8,114✔
2818
        int r;
8,114✔
2819

2820
        assert(unit);
8,114✔
2821
        assert(context);
8,114✔
2822
        assert(ret);
8,114✔
2823

2824
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
8,114✔
2825
                *ret = NULL;
7,924✔
2826
                return 0;
7,924✔
2827
        }
2828

2829
        if (exec_needs_ephemeral(context)) {
190✔
2830
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2831
                if (r < 0)
×
2832
                        return r;
2833

2834
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2835
                if (r < 0)
×
2836
                        return r;
2837

2838
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2839
                        return -errno;
×
2840
        }
2841

2842
        rt = new(ExecRuntime, 1);
190✔
2843
        if (!rt)
190✔
2844
                return -ENOMEM;
2845

2846
        *rt = (ExecRuntime) {
190✔
2847
                .shared = shared,
2848
                .dynamic_creds = creds,
2849
                .ephemeral_copy = TAKE_PTR(ephemeral),
190✔
2850
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
190✔
2851
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
190✔
2852
        };
2853

2854
        *ret = TAKE_PTR(rt);
190✔
2855
        return 1;
190✔
2856
}
2857

2858
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
58,735✔
2859
        if (!rt)
58,735✔
2860
                return NULL;
2861

2862
        exec_shared_runtime_unref(rt->shared);
190✔
2863
        dynamic_creds_unref(rt->dynamic_creds);
190✔
2864

2865
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
190✔
2866

2867
        safe_close_pair(rt->ephemeral_storage_socket);
190✔
2868
        return mfree(rt);
190✔
2869
}
2870

2871
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
6,646✔
2872
        if (!rt)
6,646✔
2873
                return NULL;
2874

2875
        rt->shared = exec_shared_runtime_destroy(rt->shared);
66✔
2876
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
66✔
2877
        return exec_runtime_free(rt);
66✔
2878
}
2879

2880
void exec_runtime_clear(ExecRuntime *rt) {
34✔
2881
        if (!rt)
34✔
2882
                return;
2883

2884
        safe_close_pair(rt->ephemeral_storage_socket);
34✔
2885
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
34✔
2886
}
2887

2888
void exec_params_shallow_clear(ExecParameters *p) {
2,551✔
2889
        if (!p)
2,551✔
2890
                return;
2891

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

2895
        p->environment = strv_free(p->environment);
2,551✔
2896
        p->fd_names = strv_free(p->fd_names);
2,551✔
2897
        p->files_env = strv_free(p->files_env);
2,551✔
2898
        p->fds = mfree(p->fds);
2,551✔
2899
        p->root_directory_fd = safe_close(p->root_directory_fd);
2,551✔
2900
        p->exec_fd = safe_close(p->exec_fd);
2,551✔
2901
        p->user_lookup_fd = -EBADF;
2,551✔
2902
        p->bpf_restrict_fs_map_fd = -EBADF;
2,551✔
2903
        p->unit_id = mfree(p->unit_id);
2,551✔
2904
        p->invocation_id = SD_ID128_NULL;
2,551✔
2905
        p->invocation_id_string[0] = '\0';
2,551✔
2906
        p->confirm_spawn = mfree(p->confirm_spawn);
2,551✔
2907
}
2908

2909
void exec_params_deep_clear(ExecParameters *p) {
34✔
2910
        if (!p)
34✔
2911
                return;
2912

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

2917
        close_many_unset(p->fds, p->n_socket_fds + p->n_stashed_fds);
34✔
2918

2919
        p->cgroup_path = mfree(p->cgroup_path);
34✔
2920

2921
        if (p->prefix) {
34✔
2922
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
34✔
2923
                p->prefix = mfree(p->prefix);
34✔
2924
        }
2925

2926
        p->received_credentials_directory = mfree(p->received_credentials_directory);
34✔
2927
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
34✔
2928

2929
        if (p->idle_pipe) {
34✔
UNCOV
2930
                close_many_and_free(p->idle_pipe, 4);
×
UNCOV
2931
                p->idle_pipe = NULL;
×
2932
        }
2933

2934
        p->stdin_fd = safe_close(p->stdin_fd);
34✔
2935
        p->stdout_fd = safe_close(p->stdout_fd);
34✔
2936
        p->stderr_fd = safe_close(p->stderr_fd);
34✔
2937

2938
        p->notify_socket = mfree(p->notify_socket);
34✔
2939

2940
        open_file_free_many(&p->open_files);
34✔
2941

2942
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
34✔
2943

2944
        exec_params_shallow_clear(p);
34✔
2945
}
2946

2947
void exec_directory_done(ExecDirectory *d) {
293,515✔
2948
        if (!d)
293,515✔
2949
                return;
2950

2951
        FOREACH_ARRAY(i, d->items, d->n_items) {
295,262✔
2952
                free(i->path);
1,747✔
2953
                strv_free(i->symlinks);
1,747✔
2954
        }
2955

2956
        d->items = mfree(d->items);
293,515✔
2957
        d->n_items = 0;
293,515✔
2958
        d->mode = 0755;
293,515✔
2959
}
2960

2961
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
4,736✔
2962
        assert(d);
4,736✔
2963
        assert(path);
4,736✔
2964

2965
        FOREACH_ARRAY(i, d->items, d->n_items)
7,190✔
2966
                if (path_equal(i->path, path))
2,469✔
2967
                        return i;
2968

2969
        return NULL;
2970
}
2971

2972
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
4,736✔
2973
        _cleanup_strv_free_ char **s = NULL;
×
2974
        _cleanup_free_ char *p = NULL;
4,736✔
2975
        ExecDirectoryItem *existing;
4,736✔
2976
        int r;
4,736✔
2977

2978
        assert(d);
4,736✔
2979
        assert(path);
4,736✔
2980

2981
        existing = exec_directory_find(d, path);
4,736✔
2982
        if (existing) {
4,736✔
2983
                r = strv_extend(&existing->symlinks, symlink);
15✔
2984
                if (r < 0)
15✔
2985
                        return r;
2986

2987
                existing->flags |= flags;
15✔
2988

2989
                return 0; /* existing item is updated */
15✔
2990
        }
2991

2992
        p = strdup(path);
4,721✔
2993
        if (!p)
4,721✔
2994
                return -ENOMEM;
2995

2996
        if (symlink) {
4,721✔
2997
                s = strv_new(symlink);
6✔
2998
                if (!s)
6✔
2999
                        return -ENOMEM;
3000
        }
3001

3002
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
4,721✔
3003
                return -ENOMEM;
3004

3005
        d->items[d->n_items++] = (ExecDirectoryItem) {
4,721✔
3006
                .path = TAKE_PTR(p),
4,721✔
3007
                .symlinks = TAKE_PTR(s),
4,721✔
3008
                .flags = flags,
3009
        };
3010

3011
        return 1; /* new item is added */
4,721✔
3012
}
3013

3014
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
939✔
3015
        assert(a);
939✔
3016
        assert(b);
939✔
3017

3018
        return path_compare(a->path, b->path);
939✔
3019
}
3020

3021
void exec_directory_sort(ExecDirectory *d) {
161,261✔
3022
        assert(d);
161,261✔
3023

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

3029
        if (d->n_items <= 1)
161,261✔
3030
                return;
3031

3032
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
167✔
3033

3034
        for (size_t i = 1; i < d->n_items; i++)
730✔
3035
                for (size_t j = 0; j < i; j++)
1,878✔
3036
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,330✔
3037
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
3038
                                break;
15✔
3039
                        }
3040
}
3041

3042
ExecCleanMask exec_clean_mask_from_string(const char *s) {
4✔
3043
        ExecDirectoryType t;
4✔
3044

3045
        assert(s);
4✔
3046

3047
        if (streq(s, "all"))
4✔
3048
                return EXEC_CLEAN_ALL;
3049
        if (streq(s, "fdstore"))
3✔
3050
                return EXEC_CLEAN_FDSTORE;
3051

3052
        t = exec_resource_type_from_string(s);
2✔
3053
        if (t < 0)
2✔
3054
                return (ExecCleanMask) t;
3055

3056
        return 1U << t;
2✔
3057
}
3058

3059
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
3060
        [EXEC_INPUT_NULL]      = "null",
3061
        [EXEC_INPUT_TTY]       = "tty",
3062
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
3063
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
3064
        [EXEC_INPUT_SOCKET]    = "socket",
3065
        [EXEC_INPUT_NAMED_FD]  = "fd",
3066
        [EXEC_INPUT_DATA]      = "data",
3067
        [EXEC_INPUT_FILE]      = "file",
3068
};
3069

3070
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,856✔
3071

3072
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
3073
        [EXEC_OUTPUT_INHERIT]             = "inherit",
3074
        [EXEC_OUTPUT_NULL]                = "null",
3075
        [EXEC_OUTPUT_TTY]                 = "tty",
3076
        [EXEC_OUTPUT_KMSG]                = "kmsg",
3077
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
3078
        [EXEC_OUTPUT_JOURNAL]             = "journal",
3079
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
3080
        [EXEC_OUTPUT_SOCKET]              = "socket",
3081
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
3082
        [EXEC_OUTPUT_FILE]                = "file",
3083
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
3084
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
3085
};
3086

3087
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
33,960✔
3088

3089
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
3090
        [EXEC_UTMP_INIT]  = "init",
3091
        [EXEC_UTMP_LOGIN] = "login",
3092
        [EXEC_UTMP_USER]  = "user",
3093
};
3094

3095
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,864✔
3096

3097
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
3098
        [EXEC_PRESERVE_NO]      = "no",
3099
        [EXEC_PRESERVE_YES]     = "yes",
3100
        [EXEC_PRESERVE_RESTART] = "restart",
3101
};
3102

3103
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
19,863✔
3104

3105
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
3106
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3107
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
3108
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
3109
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
3110
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
3111
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
3112
};
3113

3114
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3115

3116
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3117
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
3118
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
3119
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
3120
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
3121
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
3122
};
3123

3124
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3125

3126
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
3127
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
3128
 * directories, specifically .timer units with their timestamp touch file. */
3129
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3130
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
3131
        [EXEC_DIRECTORY_STATE]         = "state",
3132
        [EXEC_DIRECTORY_CACHE]         = "cache",
3133
        [EXEC_DIRECTORY_LOGS]          = "logs",
3134
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
3135
};
3136

3137
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
317✔
3138

3139
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3140
        [EXEC_KEYRING_INHERIT] = "inherit",
3141
        [EXEC_KEYRING_PRIVATE] = "private",
3142
        [EXEC_KEYRING_SHARED]  = "shared",
3143
};
3144

3145
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
16,434✔
3146

3147
static const char* const memory_thp_table[_MEMORY_THP_MAX] = {
3148
        [MEMORY_THP_INHERIT] = "inherit",
3149
        [MEMORY_THP_DISABLE] = "disable",
3150
        [MEMORY_THP_MADVISE] = "madvise",
3151
        [MEMORY_THP_SYSTEM]  = "system",
3152
};
3153

3154
DEFINE_STRING_TABLE_LOOKUP(memory_thp, MemoryTHP);
16,237✔
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