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

systemd / systemd / 18958539692

30 Oct 2025 09:15PM UTC coverage: 72.046% (-0.2%) from 72.245%
18958539692

push

github

web-flow
importd: port export-tar code to use the one systemd-dissect already uses (#39405)

Split out of #38728.

(Testcase is part of that PR)

92 of 135 new or added lines in 5 files covered. (68.15%)

4530 existing lines in 57 files now uncovered.

304067 of 422048 relevant lines covered (72.05%)

1172093.27 hits per line

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

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

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

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

68
const char* exec_context_tty_path(const ExecContext *context) {
14,560✔
69
        assert(context);
14,560✔
70

71
        if (context->stdio_as_fds)
14,560✔
72
                return NULL;
73

74
        if (context->tty_path)
13,836✔
75
                return context->tty_path;
689✔
76

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

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

86
        unsigned rows, cols;
1,431✔
87
        int r;
1,431✔
88

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

93
        if (!isatty_safe(output_fd))
1,431✔
94
                return 0;
1,431✔
95

96
        if (!tty_path)
865✔
97
                tty_path = exec_context_tty_path(context);
435✔
98

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

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

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

122
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
865✔
123
}
124

125
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *parameters, sd_id128_t invocation_id) {
13,259✔
126
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
26,518✔
127
        int fd, r;
13,259✔
128

129
        assert(context);
13,259✔
130

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

136
        const char *path = exec_context_tty_path(context);
13,259✔
137

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

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

158
        if (context->tty_reset)
451✔
159
                (void) terminal_reset_defensive(
392✔
160
                                fd,
161
                                TERMINAL_RESET_SWITCH_TO_TEXT |
162
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
389✔
163

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

168
        if (!sd_id128_is_null(invocation_id) && exec_context_shall_ansi_seq_reset(context)) {
866✔
169
                sd_id128_t context_id;
22✔
170

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

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

185
        if (context->tty_vhangup)
451✔
186
                (void) terminal_vhangup_fd(fd);
171✔
187

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

192
        if (context->tty_vt_disallocate && path)
451✔
193
                (void) vt_disallocate(path);
90✔
194
}
195

196
bool exec_needs_network_namespace(const ExecContext *context) {
58,527✔
197
        assert(context);
58,527✔
198

199
        return context->private_network || context->network_namespace_path;
58,527✔
200
}
201

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

206
bool exec_needs_ipc_namespace(const ExecContext *context) {
52,927✔
207
        assert(context);
52,927✔
208

209
        return context->private_ipc || context->ipc_namespace_path;
52,927✔
210
}
211

212
static bool needs_cgroup_namespace(ProtectControlGroups i) {
136,977✔
213
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
136,977✔
214
}
215

216
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
84,040✔
217
        assert(context);
84,040✔
218

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

232
bool exec_needs_cgroup_namespace(const ExecContext *context) {
52,937✔
233
        assert(context);
52,937✔
234

235
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
52,937✔
236
}
237

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

241
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
27,158✔
242
}
243

244
bool exec_is_cgroup_mount_read_only(const ExecContext *context) {
1,973✔
245
        assert(context);
1,973✔
246

247
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
1,973✔
248
}
249

250
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
75,323✔
251
        assert(context);
75,323✔
252

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

257
        return context->private_pids != PRIVATE_PIDS_NO && namespace_type_supported(NAMESPACE_PID);
67,786✔
258
}
259

260
bool exec_needs_mount_namespace(
32,598✔
261
                const ExecContext *context,
262
                const ExecParameters *params,
263
                const ExecRuntime *runtime) {
264

265
        assert(context);
32,598✔
266

267
        if (context->root_image)
32,598✔
268
                return true;
269

270
        if (!strv_isempty(context->read_write_paths) ||
32,538✔
271
            !strv_isempty(context->read_only_paths) ||
30,426✔
272
            !strv_isempty(context->inaccessible_paths) ||
30,419✔
273
            !strv_isempty(context->exec_paths) ||
30,401✔
274
            !strv_isempty(context->no_exec_paths))
30,401✔
275
                return true;
276

277
        if (context->n_bind_mounts > 0)
30,401✔
278
                return true;
279

280
        if (context->n_temporary_filesystems > 0)
30,338✔
281
                return true;
282

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

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

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

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

295
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
30,126✔
296
                return true;
297

298
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
29,325✔
299
                return true;
300

301
        if (context->private_devices ||
28,840✔
302
            context->private_mounts > 0 ||
28,472✔
303
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,007✔
304
            context->protect_system != PROTECT_SYSTEM_NO ||
27,985✔
305
            context->protect_home != PROTECT_HOME_NO ||
27,985✔
306
            context->protect_kernel_tunables ||
27,985✔
307
            context->protect_kernel_modules ||
27,985✔
308
            context->protect_kernel_logs ||
51,761✔
309
            exec_needs_cgroup_mount(context) ||
25,879✔
310
            context->protect_proc != PROTECT_PROC_DEFAULT ||
25,861✔
311
            context->proc_subset != PROC_SUBSET_ALL ||
25,801✔
312
            context->private_bpf != PRIVATE_BPF_NO ||
51,572✔
313
            exec_needs_ipc_namespace(context) ||
51,572✔
314
            exec_needs_pid_namespace(context, params))
25,786✔
315
                return true;
3,094✔
316

317
        if (context->root_directory) {
25,746✔
318
                if (exec_context_get_effective_mount_apivfs(context))
5✔
319
                        return true;
320

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

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

330
        if (context->dynamic_user &&
25,741✔
331
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
332
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
333
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
334
                return true;
335

336
        if (exec_context_get_effective_bind_log_sockets(context))
25,741✔
337
                return true;
338

339
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
154,102✔
340
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
133,442✔
341
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,075✔
342
                                return true;
343

344
        return false;
345
}
346

347
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
3,829✔
348
        assert(context);
3,829✔
349
        assert(params);
3,829✔
350

351
        if (!params->notify_socket)
3,829✔
352
                return NULL;
353

354
        if (!needs_sandboxing)
3,156✔
355
                return NULL;
356

357
        if (!context->root_directory && !context->root_image)
3,156✔
358
                return NULL;
359

360
        if (!exec_context_get_effective_mount_apivfs(context))
×
361
                return NULL;
362

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

366
        return "/run/host/notify";
367
}
368

369
int exec_log_level_max_with_exec_params(const ExecContext *context, const ExecParameters *params) {
23,734✔
370
        assert(params);
23,734✔
371

372
        if (params->debug_invocation)
23,734✔
373
                return LOG_DEBUG;
374

375
        return exec_log_level_max(context);
23,730✔
376
}
377

378
int exec_log_level_max(const ExecContext *context) {
23,776✔
379
        assert(context);
23,776✔
380
        return context->log_level_max < 0 ? log_get_max_level() : context->log_level_max;
23,776✔
381
}
382

383
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
11,207✔
384
        assert(context);
11,207✔
385

386
        if (!context->dynamic_user)
11,207✔
387
                return false;
388

389
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
104✔
390
                return false;
391

392
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
98✔
393
                return false;
14✔
394

395
        return true;
396
}
397

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

403
int exec_params_get_cgroup_path(
12,567✔
404
                const ExecParameters *params,
405
                const CGroupContext *c,
406
                const char *prefix,
407
                char **ret) {
408

409
        const char *subgroup = NULL;
12,567✔
410
        char *p;
12,567✔
411

412
        assert(params);
12,567✔
413
        assert(c);
12,567✔
414
        assert(ret);
12,567✔
415

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

425
        /* Keep this in sync with exec_params_needs_control_subcgroup(). */
426
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
12,567✔
427
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
697✔
428
                        subgroup = ".control";
429
                else
430
                        subgroup = c->delegate_subgroup;
656✔
431
        }
432

433
        if (subgroup)
656✔
434
                p = path_join(prefix, subgroup);
697✔
435
        else
436
                p = strdup(strempty(prefix));
11,878✔
437
        if (!p)
12,567✔
438
                return -ENOMEM;
439

440
        *ret = p;
12,567✔
441
        return !!subgroup;
12,567✔
442
}
443

444
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
1,268✔
445
        assert(c);
1,268✔
446

447
        return c->cpu_affinity_from_numa;
1,268✔
448
}
449

450
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
2,115✔
451
        assert(unit);
2,115✔
452
        assert(msg);
2,115✔
453
        assert(executable);
2,115✔
454

455
        if (!DEBUG_LOGGING)
2,115✔
456
                return;
2,115✔
457

458
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
4,230✔
459

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

466
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
467

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

477
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL;
2,115✔
478
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
479
        _cleanup_fclose_ FILE *f = NULL;
2,115✔
480
        int r;
2,115✔
481

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

494
        LOG_CONTEXT_PUSH_UNIT(unit);
4,230✔
495

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

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

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

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

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

525
                cgtarget = subcgroup_path;
1✔
526
        } else
527
                cgtarget = params->cgroup_path;
2,114✔
528

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

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

540
        fdset = fdset_new();
2,115✔
541
        if (!fdset)
2,115✔
542
                return log_oom();
×
543

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

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

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

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

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

567
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,115✔
568
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,115✔
569

570
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,115✔
571
        dual_timestamp start_timestamp;
2,115✔
572

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

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

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

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

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

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

614
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,115✔
615

616
        *ret = TAKE_PIDREF(pidref);
2,115✔
617
        return 0;
2,115✔
618
}
619

620
void exec_context_init(ExecContext *c) {
60,090✔
621
        assert(c);
60,090✔
622

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

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

653
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
360,540✔
654
                d->mode = 0755;
300,450✔
655

656
        numa_policy_reset(&c->numa_policy);
60,090✔
657

658
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
60,090✔
659
}
60,090✔
660

661
void exec_context_done(ExecContext *c) {
48,255✔
662
        assert(c);
48,255✔
663

664
        c->environment = strv_free(c->environment);
48,255✔
665
        c->environment_files = strv_free(c->environment_files);
48,255✔
666
        c->pass_environment = strv_free(c->pass_environment);
48,255✔
667
        c->unset_environment = strv_free(c->unset_environment);
48,255✔
668

669
        rlimit_free_all(c->rlimit);
48,255✔
670

671
        for (size_t l = 0; l < 3; l++) {
193,020✔
672
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
144,765✔
673
                c->stdio_file[l] = mfree(c->stdio_file[l]);
144,765✔
674
        }
675

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

694
        c->supplementary_groups = strv_free(c->supplementary_groups);
48,255✔
695

696
        c->pam_name = mfree(c->pam_name);
48,255✔
697

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

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

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

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

721
        c->restrict_filesystems = set_free(c->restrict_filesystems);
48,255✔
722

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

728
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
289,530✔
729
                exec_directory_done(d);
241,275✔
730

731
        c->log_level_max = -1;
48,255✔
732

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

737
        c->log_ratelimit = (RateLimit) {};
48,255✔
738

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

742
        c->network_namespace_path = mfree(c->network_namespace_path);
48,255✔
743
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
48,255✔
744

745
        c->log_namespace = mfree(c->log_namespace);
48,255✔
746

747
        c->load_credentials = hashmap_free(c->load_credentials);
48,255✔
748
        c->set_credentials = hashmap_free(c->set_credentials);
48,255✔
749
        c->import_credentials = ordered_set_free(c->import_credentials);
48,255✔
750

751
        c->root_image_policy = image_policy_free(c->root_image_policy);
48,255✔
752
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
48,255✔
753
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
48,255✔
754

755
        c->private_hostname = mfree(c->private_hostname);
48,255✔
756
}
48,255✔
757

758
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
5,734✔
759
        assert(c);
5,734✔
760

761
        if (!runtime_prefix)
5,734✔
762
                return 0;
763

764
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
5,735✔
765
                _cleanup_free_ char *p = NULL;
1✔
766

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

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

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

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

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

792
        return 0;
793
}
794

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

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

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

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

809
        return 0;
810
}
811

812
void exec_command_done(ExecCommand *c) {
102,294✔
813
        assert(c);
102,294✔
814

815
        c->path = mfree(c->path);
102,294✔
816
        c->argv = strv_free(c->argv);
102,294✔
817
}
102,294✔
818

819
void exec_command_done_array(ExecCommand *c, size_t n) {
28,703✔
820
        FOREACH_ARRAY(i, c, n)
114,810✔
821
                exec_command_done(i);
86,107✔
822
}
28,703✔
823

824
ExecCommand* exec_command_free(ExecCommand *c) {
16,156✔
825
        if (!c)
16,156✔
826
                return NULL;
827

828
        exec_command_done(c);
16,156✔
829
        return mfree(c);
16,156✔
830
}
831

832
ExecCommand* exec_command_free_list(ExecCommand *c) {
124,334✔
833
        ExecCommand *i;
124,334✔
834

835
        while ((i = LIST_POP(command, c)))
140,490✔
836
                exec_command_free(i);
16,156✔
837

838
        return NULL;
124,334✔
839
}
840

841
void exec_command_free_array(ExecCommand **c, size_t n) {
19,521✔
842
        FOREACH_ARRAY(i, c, n)
143,840✔
843
                *i = exec_command_free_list(*i);
124,319✔
844
}
19,521✔
845

846
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
7,364✔
847
        FOREACH_ARRAY(i, c, n)
29,455✔
848
                exec_status_reset(&i->exec_status);
22,091✔
849
}
7,364✔
850

851
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
4,956✔
852
        FOREACH_ARRAY(i, c, n)
33,356✔
853
                LIST_FOREACH(command, z, *i)
30,450✔
854
                        exec_status_reset(&z->exec_status);
2,050✔
855
}
4,956✔
856

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

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

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

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

871
        switch (fd_index) {
39,405✔
872

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

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

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

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

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

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

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

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

900
        assert(c);
2,115✔
901
        assert(ret);
2,115✔
902

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

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

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

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

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

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

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

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

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

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

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

962
        *ret = TAKE_PTR(v);
2,115✔
963

964
        return 0;
2,115✔
965
}
966

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

970
        if (!tty)
226✔
971
                return true;
972

973
        tty = skip_dev_prefix(tty);
226✔
974

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

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

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

986
static bool exec_context_may_touch_tty(const ExecContext *ec) {
23,440✔
987
        assert(ec);
23,440✔
988

989
        return ec->tty_reset ||
46,667✔
990
                ec->tty_vhangup ||
23,227✔
991
                ec->tty_vt_disallocate ||
23,227✔
992
                exec_input_is_terminal(ec->std_input) ||
23,227✔
993
                ec->std_output == EXEC_OUTPUT_TTY ||
46,667✔
994
                ec->std_error == EXEC_OUTPUT_TTY;
23,178✔
995
}
996

997
bool exec_context_may_touch_console(const ExecContext *ec) {
22,047✔
998

999
        return exec_context_may_touch_tty(ec) &&
22,273✔
1000
               tty_may_match_dev_console(exec_context_tty_path(ec));
226✔
1001
}
1002

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

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

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

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

1022
static void strv_fprintf(FILE *f, char **l) {
×
1023
        assert(f);
×
1024

1025
        STRV_FOREACH(g, l)
×
1026
                fprintf(f, " %s", *g);
×
1027
}
×
1028

1029
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
1,776✔
1030
        assert(f);
1,776✔
1031
        assert(prefix);
1,776✔
1032
        assert(name);
1,776✔
1033

1034
        if (!strv_isempty(strv)) {
1,776✔
1035
                fprintf(f, "%s%s:", prefix, name);
×
1036
                strv_fprintf(f, strv);
×
1037
                fputs("\n", f);
×
1038
        }
1039
}
1,776✔
1040

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

1045
        prefix = strempty(prefix);
×
1046

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

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

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

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

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

1086
        assert(c);
222✔
1087
        assert(f);
222✔
1088

1089
        prefix = strempty(prefix);
222✔
1090

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

1149
        if (c->private_bpf == PRIVATE_BPF_YES) {
222✔
UNCOV
1150
                _cleanup_free_ char
×
UNCOV
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));
×
UNCOV
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)
222✔
1163
                fprintf(f, "%sSetLoginEnvironment: %s\n", prefix, yes_no(c->set_login_environment > 0));
2✔
1164

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

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

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

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

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

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

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

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

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

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

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

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

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

1219
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
1,120✔
1220
                        fprintf(f,
10✔
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)" : "");
10✔
1226

1227
                        STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
10✔
UNCOV
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));
222✔
1233

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

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

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

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

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

1254
        if (c->ioprio_is_set) {
222✔
UNCOV
1255
                _cleanup_free_ char *class_str = NULL;
×
1256

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

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

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

1267
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
UNCOV
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",
UNCOV
1274
                        prefix, c->cpu_sched_priority,
×
UNCOV
1275
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1276
        }
1277

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

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

1285
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
222✔
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)
222✔
1294
                fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
1✔
1295

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

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

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

1326
        if (c->tty_path)
222✔
UNCOV
1327
                fprintf(f,
×
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,
UNCOV
1335
                        prefix, yes_no(c->tty_reset),
×
UNCOV
1336
                        prefix, yes_no(c->tty_vhangup),
×
1337
                        prefix, yes_no(c->tty_vt_disallocate),
×
1338
                        prefix, c->tty_rows,
×
1339
                        prefix, c->tty_cols);
×
1340

1341
        if (IN_SET(c->std_output,
222✔
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,
17✔
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;
205✔
1353

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

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

1363
        if (c->log_level_max >= 0) {
222✔
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)
222✔
UNCOV
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)
222✔
UNCOV
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)) {
222✔
UNCOV
1380
                fprintf(f, "%sLogFilterPatterns:", prefix);
×
1381

1382
                char *pattern;
×
UNCOV
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) {
226✔
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)
222✔
UNCOV
1397
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1398

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

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

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

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

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

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

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

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

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

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

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

1442
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
226✔
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)
222✔
UNCOV
1451
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1452
                        tmpfs->path,
1453
                        isempty(tmpfs->options) ? "" : ":",
×
UNCOV
1454
                        strempty(tmpfs->options));
×
1455

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

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

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

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

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

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

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

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

1502
                                if (first)
4,268✔
1503
                                        first = false;
1504
                                else
1505
                                        fputc(' ', f);
4,257✔
1506

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

1510
                                if (num >= 0) {
4,268✔
UNCOV
1511
                                        errno_name = seccomp_errno_or_action_to_string(num);
×
UNCOV
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);
11✔
1522
        }
1523

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

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

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

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

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

1554
        if (c->network_namespace_path)
222✔
UNCOV
1555
                fprintf(f,
×
1556
                        "%sNetworkNamespacePath: %s\n",
1557
                        prefix, c->network_namespace_path);
1558

1559
        if (c->syscall_errno > 0) {
222✔
1560
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
221✔
1561

1562
#if HAVE_SECCOMP
1563
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
221✔
1564
                if (errno_name)
221✔
1565
                        fputs(errno_name, f);
221✔
1566
                else
UNCOV
1567
                        fprintf(f, "%d", c->syscall_errno);
×
1568
#endif
1569
                fputc('\n', f);
221✔
1570
        }
1571

1572
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
222✔
UNCOV
1573
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
UNCOV
1574
                        mount->ignore_enoent ? "-": "",
×
1575
                        mount->source,
1576
                        mount->destination);
UNCOV
1577
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
UNCOV
1578
                        fprintf(f, ":%s:%s",
×
1579
                                partition_designator_to_string(o->partition_designator),
1580
                                strempty(o->options));
×
UNCOV
1581
                fprintf(f, "\n");
×
1582
        }
1583

1584
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
222✔
UNCOV
1585
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
UNCOV
1586
                        mount->ignore_enoent ? "-": "",
×
1587
                        mount->source);
1588
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
UNCOV
1589
                        fprintf(f, ":%s:%s",
×
1590
                                partition_designator_to_string(o->partition_designator),
1591
                                strempty(o->options));
×
UNCOV
1592
                fprintf(f, "\n");
×
1593
        }
1594

1595
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
222✔
1596
}
222✔
1597

UNCOV
1598
bool exec_context_maintains_privileges(const ExecContext *c) {
×
UNCOV
1599
        assert(c);
×
1600

1601
        /* Returns true if the process forked off would run under
1602
         * an unchanged UID or as root. */
1603

UNCOV
1604
        if (!c->user)
×
1605
                return true;
1606

UNCOV
1607
        if (STR_IN_SET(c->user, "root", "0"))
×
UNCOV
1608
                return true;
×
1609

1610
        return false;
×
1611
}
1612

1613
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,536✔
1614
        int p;
2,536✔
1615

1616
        assert(c);
2,536✔
1617

1618
        if (c->ioprio_is_set)
2,536✔
1619
                return c->ioprio;
14✔
1620

1621
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,522✔
1622
        if (p < 0)
2,522✔
1623
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1624

1625
        return ioprio_normalize(p);
2,522✔
1626
}
1627

1628
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
34,632✔
1629
        assert(c);
34,632✔
1630

1631
        /* Explicit setting wins */
1632
        if (c->mount_apivfs >= 0)
34,632✔
1633
                return c->mount_apivfs > 0;
107✔
1634

1635
        /* Default to "yes" if root directory or image are specified */
1636
        if (exec_context_with_rootfs(c))
34,525✔
1637
                return true;
134✔
1638

1639
        return false;
1640
}
1641

1642
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
29,203✔
1643
        assert(c);
29,203✔
1644

1645
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1646
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1647
        if (c->log_namespace)
29,203✔
1648
                return true;
1649

1650
        if (c->bind_log_sockets >= 0)
29,195✔
1651
                return c->bind_log_sockets > 0;
2✔
1652

1653
        if (exec_context_get_effective_mount_apivfs(c))
29,193✔
1654
                return true;
1655

1656
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1657
        if (exec_context_with_rootfs(c) && c->private_devices)
29,093✔
UNCOV
1658
                return true;
×
1659

1660
        return false;
1661
}
1662

1663
void exec_context_free_log_extra_fields(ExecContext *c) {
48,257✔
1664
        assert(c);
48,257✔
1665

1666
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
48,262✔
1667
                free(field->iov_base);
5✔
1668

1669
        c->log_extra_fields = mfree(c->log_extra_fields);
48,257✔
1670
        c->n_log_extra_fields = 0;
48,257✔
1671
}
48,257✔
1672

1673
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,393✔
1674
        _cleanup_close_ int fd = -EBADF;
1,393✔
1675
        const char *path;
1,393✔
1676
        struct stat st;
1,393✔
1677
        int r;
1,393✔
1678

1679
        assert(c);
1,393✔
1680

1681
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1682
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,393✔
1683

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

1690
        path = exec_context_tty_path(c);
36✔
1691
        if (!path)
36✔
1692
                return;
1693

1694
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
36✔
1695
        if (fd < 0)
36✔
UNCOV
1696
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1697
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1698
                                             path);
1699

1700
        if (fstat(fd, &st) < 0)
36✔
UNCOV
1701
                return (void) log_warning_errno(errno, "Failed to stat TTY '%s', ignoring: %m", path);
×
1702

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

1711
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
36✔
1712
        if (r < 0)
36✔
1713
                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);
36✔
1714
}
1715

UNCOV
1716
int exec_context_get_clean_directories(
×
1717
                ExecContext *c,
1718
                char **prefix,
1719
                ExecCleanMask mask,
1720
                char ***ret) {
1721

UNCOV
1722
        _cleanup_strv_free_ char **l = NULL;
×
UNCOV
1723
        int r;
×
1724

1725
        assert(c);
×
UNCOV
1726
        assert(prefix);
×
1727
        assert(ret);
×
1728

1729
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
UNCOV
1730
                if (!BIT_SET(mask, t))
×
1731
                        continue;
×
1732

1733
                if (!prefix[t])
×
UNCOV
1734
                        continue;
×
1735

1736
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
×
UNCOV
1737
                        char *j;
×
1738

1739
                        j = path_join(prefix[t], i->path);
×
UNCOV
1740
                        if (!j)
×
1741
                                return -ENOMEM;
1742

UNCOV
1743
                        r = strv_consume(&l, j);
×
UNCOV
1744
                        if (r < 0)
×
1745
                                return r;
1746

1747
                        /* Also remove private directories unconditionally. */
UNCOV
1748
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
×
UNCOV
1749
                                j = path_join(prefix[t], "private", i->path);
×
1750
                                if (!j)
×
1751
                                        return -ENOMEM;
1752

UNCOV
1753
                                r = strv_consume(&l, j);
×
UNCOV
1754
                                if (r < 0)
×
1755
                                        return r;
1756
                        }
1757

UNCOV
1758
                        STRV_FOREACH(symlink, i->symlinks) {
×
UNCOV
1759
                                j = path_join(prefix[t], *symlink);
×
1760
                                if (!j)
×
1761
                                        return -ENOMEM;
1762

UNCOV
1763
                                r = strv_consume(&l, j);
×
UNCOV
1764
                                if (r < 0)
×
1765
                                        return r;
1766
                        }
1767
                }
1768
        }
1769

UNCOV
1770
        *ret = TAKE_PTR(l);
×
UNCOV
1771
        return 0;
×
1772
}
1773

1774
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,252✔
1775
        ExecCleanMask mask = 0;
1,252✔
1776

1777
        assert(c);
1,252✔
1778
        assert(ret);
1,252✔
1779

1780
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
7,512✔
1781
                if (c->directories[t].n_items > 0)
6,260✔
1782
                        mask |= 1U << t;
270✔
1783

1784
        *ret = mask;
1,252✔
1785
        return 0;
1,252✔
1786
}
1787

1788
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,268✔
1789
        int n = 0, r;
1,268✔
1790

1791
        assert(c);
1,268✔
1792

1793
        if (c->oom_score_adjust_set)
1,268✔
1794
                return c->oom_score_adjust;
476✔
1795

1796
        r = get_oom_score_adjust(&n);
792✔
1797
        if (r < 0)
792✔
UNCOV
1798
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1799

1800
        return n;
792✔
1801
}
1802

1803
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1,268✔
1804
        _cleanup_free_ char *t = NULL;
1,268✔
1805
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1,268✔
1806
        int r;
1,268✔
1807

1808
        assert(c);
1,268✔
1809

1810
        if (c->coredump_filter_set)
1,268✔
UNCOV
1811
                return c->coredump_filter;
×
1812

1813
        r = read_one_line_file("/proc/self/coredump_filter", &t);
1,268✔
1814
        if (r < 0)
1,268✔
UNCOV
1815
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1816
        else {
1817
                r = safe_atoux64(t, &n);
1,268✔
1818
                if (r < 0)
1,268✔
UNCOV
1819
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1820
        }
1821

1822
        return n;
1,268✔
1823
}
1824

1825
int exec_context_get_nice(const ExecContext *c) {
1,268✔
1826
        int n;
1,268✔
1827

1828
        assert(c);
1,268✔
1829

1830
        if (c->nice_set)
1,268✔
1831
                return c->nice;
4✔
1832

1833
        errno = 0;
1,264✔
1834
        n = getpriority(PRIO_PROCESS, 0);
1,264✔
1835
        if (errno > 0) {
1,264✔
UNCOV
1836
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1837
                n = 0;
1838
        }
1839

1840
        return n;
1841
}
1842

1843
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,268✔
1844
        int n;
1,268✔
1845

1846
        assert(c);
1,268✔
1847

1848
        if (c->cpu_sched_set)
1,268✔
UNCOV
1849
                return c->cpu_sched_policy;
×
1850

1851
        n = sched_getscheduler(0);
1,268✔
1852
        if (n < 0)
1,268✔
UNCOV
1853
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1854

1855
        return n < 0 ? SCHED_OTHER : n;
1,268✔
1856
}
1857

1858
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,268✔
1859
        struct sched_param p = {};
1,268✔
1860
        int r;
1,268✔
1861

1862
        assert(c);
1,268✔
1863

1864
        if (c->cpu_sched_set)
1,268✔
UNCOV
1865
                return c->cpu_sched_priority;
×
1866

1867
        r = sched_getparam(0, &p);
1,268✔
1868
        if (r < 0)
1,268✔
UNCOV
1869
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1870

1871
        return r >= 0 ? p.sched_priority : 0;
1,268✔
1872
}
1873

1874
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,268✔
1875
        int r;
1,268✔
1876

1877
        assert(c);
1,268✔
1878

1879
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,268✔
1880
                return c->timer_slack_nsec;
1881

1882
        r = prctl(PR_GET_TIMERSLACK);
1,268✔
1883
        if (r < 0)
1,268✔
UNCOV
1884
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1885

1886
        return (uint64_t) MAX(r, 0);
1,268✔
1887
}
1888

1889
bool exec_context_get_set_login_environment(const ExecContext *c) {
11,062✔
1890
        assert(c);
11,062✔
1891

1892
        if (c->set_login_environment >= 0)
11,062✔
UNCOV
1893
                return c->set_login_environment;
×
1894

1895
        return c->user || c->dynamic_user || c->pam_name;
19,930✔
1896
}
1897

1898
char** exec_context_get_syscall_filter(const ExecContext *c) {
1,268✔
1899
        _cleanup_strv_free_ char **l = NULL;
1,268✔
1900

1901
        assert(c);
1,268✔
1902

1903
#if HAVE_SECCOMP
1904
        if (dlopen_libseccomp() < 0)
1,268✔
UNCOV
1905
                return strv_new(NULL);
×
1906

1907
        void *id, *val;
1,268✔
1908
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
12,194✔
1909
                _cleanup_free_ char *name = NULL;
10,926✔
1910
                const char *e = NULL;
10,926✔
1911
                char *s;
10,926✔
1912
                int num = PTR_TO_INT(val);
10,926✔
1913

1914
                if (c->syscall_allow_list && num >= 0)
10,926✔
1915
                        /* syscall with num >= 0 in allow-list is denied. */
UNCOV
1916
                        continue;
×
1917

1918
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
10,926✔
1919
                if (!name)
10,926✔
UNCOV
1920
                        continue;
×
1921

1922
                if (num >= 0) {
10,926✔
UNCOV
1923
                        e = seccomp_errno_or_action_to_string(num);
×
UNCOV
1924
                        if (e) {
×
1925
                                s = strjoin(name, ":", e);
×
1926
                                if (!s)
×
1927
                                        return NULL;
1928
                        } else {
UNCOV
1929
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1930
                                        return NULL;
1931
                        }
1932
                } else
1933
                        s = TAKE_PTR(name);
10,926✔
1934

1935
                if (strv_consume(&l, s) < 0)
10,926✔
1936
                        return NULL;
1937
        }
1938

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

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

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

1948
        assert(c);
1,268✔
1949

1950
#if HAVE_SECCOMP
1951
        void *id;
1,268✔
1952
        SET_FOREACH(id, c->syscall_archs) {
1,300✔
1953
                const char *name;
32✔
1954

1955
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
32✔
1956
                if (!name)
32✔
UNCOV
1957
                        continue;
×
1958

1959
                if (strv_extend(&l, name) < 0)
32✔
UNCOV
1960
                        return NULL;
×
1961
        }
1962

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

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

1969
char** exec_context_get_syscall_log(const ExecContext *c) {
1,268✔
1970
        _cleanup_strv_free_ char **l = NULL;
1,268✔
1971

1972
        assert(c);
1,268✔
1973

1974
#if HAVE_SECCOMP
1975
        if (dlopen_libseccomp() < 0)
1,268✔
UNCOV
1976
                return strv_new(NULL);
×
1977

1978
        void *id, *val;
1,268✔
1979
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,268✔
UNCOV
1980
                char *name = NULL;
×
1981

1982
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
UNCOV
1983
                if (!name)
×
1984
                        continue;
×
1985

1986
                if (strv_consume(&l, name) < 0)
×
UNCOV
1987
                        return NULL;
×
1988
        }
1989

1990
        strv_sort(l);
1,268✔
1991
#endif
1992

1993
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,268✔
1994
}
1995

1996
char** exec_context_get_address_families(const ExecContext *c) {
1,268✔
1997
        _cleanup_strv_free_ char **l = NULL;
1,268✔
1998
        void *af;
1,268✔
1999

2000
        assert(c);
1,268✔
2001

2002
        SET_FOREACH(af, c->address_families) {
1,369✔
2003
                const char *name;
101✔
2004

2005
                name = af_to_name(PTR_TO_INT(af));
101✔
2006
                if (!name)
101✔
UNCOV
2007
                        continue;
×
2008

2009
                if (strv_extend(&l, name) < 0)
101✔
UNCOV
2010
                        return NULL;
×
2011
        }
2012

2013
        strv_sort(l);
1,268✔
2014

2015
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,268✔
2016
}
2017

2018
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1,268✔
2019
        assert(c);
1,268✔
2020

2021
#if HAVE_LIBBPF
2022
        char **l = set_get_strv(c->restrict_filesystems);
1,268✔
2023
        if (!l)
1,268✔
2024
                return NULL;
2025

2026
        return strv_sort(l);
1,268✔
2027
#else
2028
        return strv_new(NULL);
2029
#endif
2030
}
2031

2032
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
13,015✔
2033
        assert(c);
13,015✔
2034

2035
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
13,015✔
2036
}
2037

2038
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
14,262✔
2039
        assert(c);
14,262✔
2040

2041
        return c->restrict_filesystems_allow_list ||
14,262✔
2042
          !set_isempty(c->restrict_filesystems);
14,262✔
2043
}
2044

2045
bool exec_context_with_rootfs(const ExecContext *c) {
63,887✔
2046
        assert(c);
63,887✔
2047

2048
        /* Checks if RootDirectory= or RootImage= are used */
2049

2050
        return !empty_or_root(c->root_directory) || c->root_image;
63,887✔
2051
}
2052

2053
int exec_context_has_vpicked_extensions(const ExecContext *context) {
4✔
2054
        int r;
4✔
2055

2056
        assert(context);
4✔
2057

2058
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
4✔
UNCOV
2059
                r = path_uses_vpick(mi->source);
×
UNCOV
2060
                if (r != 0)
×
2061
                        return r;
2062
        }
2063
        STRV_FOREACH(ed, context->extension_directories) {
4✔
UNCOV
2064
                r = path_uses_vpick(*ed);
×
UNCOV
2065
                if (r != 0)
×
2066
                        return r;
2067
        }
2068

2069
        return 0;
2070
}
2071

2072
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
4,199✔
2073
        assert(s);
4,199✔
2074

2075
        *s = (ExecStatus) {
4,199✔
2076
                .pid = pid,
2077
        };
2078

2079
        if (ts)
4,199✔
2080
                s->start_timestamp = *ts;
4,199✔
2081
        else
UNCOV
2082
                dual_timestamp_now(&s->start_timestamp);
×
2083
}
4,199✔
2084

2085
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
1,946✔
2086
        assert(s);
1,946✔
2087

2088
        if (s->pid != pid)
1,946✔
2089
                *s = (ExecStatus) {
6✔
2090
                        .pid = pid,
2091
                };
2092

2093
        dual_timestamp_now(&s->exit_timestamp);
1,946✔
2094

2095
        s->code = code;
1,946✔
2096
        s->status = status;
1,946✔
2097

2098
        if (context && context->utmp_id)
1,946✔
2099
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
15✔
2100
}
1,946✔
2101

2102
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
7,300✔
2103
        assert(s);
7,300✔
2104
        assert(ucred);
7,300✔
2105
        assert(ts);
7,300✔
2106

2107
        if (ucred->pid != s->pid)
7,300✔
2108
                *s = (ExecStatus) {
7✔
2109
                        .pid = ucred->pid,
2110
                };
2111

2112
        s->handoff_timestamp = *ts;
7,300✔
2113
}
7,300✔
2114

2115
void exec_status_reset(ExecStatus *s) {
25,951✔
2116
        assert(s);
25,951✔
2117

2118
        *s = (ExecStatus) {};
25,951✔
2119
}
25,951✔
2120

2121
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
98✔
2122
        assert(s);
98✔
2123
        assert(f);
98✔
2124

2125
        if (s->pid <= 0)
98✔
2126
                return;
2127

2128
        prefix = strempty(prefix);
10✔
2129

2130
        fprintf(f,
10✔
2131
                "%sPID: "PID_FMT"\n",
2132
                prefix, s->pid);
2133

2134
        if (dual_timestamp_is_set(&s->start_timestamp))
10✔
2135
                fprintf(f,
10✔
2136
                        "%sStart Timestamp: %s\n",
2137
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
10✔
2138

2139
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
10✔
2140
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
10✔
2141
                fprintf(f,
10✔
2142
                        "%sHandoff Timestamp: %s since start\n",
2143
                        prefix,
2144
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
20✔
2145
        else
UNCOV
2146
                fprintf(f,
×
2147
                        "%sHandoff Timestamp: %s\n",
2148
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
×
2149

2150
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2151

UNCOV
2152
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
×
UNCOV
2153
                        fprintf(f,
×
2154
                                "%sExit Timestamp: %s since handoff\n",
2155
                                prefix,
UNCOV
2156
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
×
UNCOV
2157
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2158
                        fprintf(f,
×
2159
                                "%sExit Timestamp: %s since start\n",
2160
                                prefix,
UNCOV
2161
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2162
                else
2163
                        fprintf(f,
×
2164
                                "%sExit Timestamp: %s\n",
2165
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2166

2167
                fprintf(f,
×
2168
                        "%sExit Code: %s\n"
2169
                        "%sExit Status: %i\n",
UNCOV
2170
                        prefix, sigchld_code_to_string(s->code),
×
UNCOV
2171
                        prefix, s->status);
×
2172
        }
2173
}
2174

2175
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
98✔
2176
        _cleanup_free_ char *cmd = NULL;
196✔
2177
        const char *prefix2;
98✔
2178

2179
        assert(c);
98✔
2180
        assert(f);
98✔
2181

2182
        prefix = strempty(prefix);
98✔
2183
        prefix2 = strjoina(prefix, "\t");
490✔
2184

2185
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
98✔
2186

2187
        fprintf(f,
98✔
2188
                "%sCommand Line: %s\n",
2189
                prefix, strnull(cmd));
2190

2191
        exec_status_dump(&c->exec_status, f, prefix2);
98✔
2192
}
98✔
2193

2194
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
95✔
2195
        assert(f);
95✔
2196

2197
        prefix = strempty(prefix);
95✔
2198

2199
        LIST_FOREACH(command, i, c)
193✔
2200
                exec_command_dump(i, f, prefix);
98✔
2201
}
95✔
2202

2203
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
16,156✔
2204
        ExecCommand *end;
16,156✔
2205

2206
        assert(l);
16,156✔
2207
        assert(e);
16,156✔
2208

2209
        if (*l) {
16,156✔
2210
                /* It's kind of important, that we keep the order here */
2211
                end = LIST_FIND_TAIL(command, *l);
363✔
2212
                LIST_INSERT_AFTER(command, *l, end, e);
126✔
2213
        } else
2214
                *l = e;
16,030✔
2215
}
16,156✔
2216

2217
int exec_command_set(ExecCommand *c, const char *path, ...) {
188✔
2218
        va_list ap;
188✔
2219
        char **l, *p;
188✔
2220

2221
        assert(c);
188✔
2222
        assert(path);
188✔
2223

2224
        va_start(ap, path);
188✔
2225
        l = strv_new_ap(path, ap);
188✔
2226
        va_end(ap);
188✔
2227

2228
        if (!l)
188✔
2229
                return -ENOMEM;
188✔
2230

2231
        p = strdup(path);
188✔
2232
        if (!p) {
188✔
UNCOV
2233
                strv_free(l);
×
UNCOV
2234
                return -ENOMEM;
×
2235
        }
2236

2237
        free_and_replace(c->path, p);
188✔
2238

2239
        return strv_free_and_replace(c->argv, l);
188✔
2240
}
2241

2242
int exec_command_append(ExecCommand *c, const char *path, ...) {
277✔
2243
        char **l;
277✔
2244
        va_list ap;
277✔
2245
        int r;
277✔
2246

2247
        assert(c);
277✔
2248
        assert(path);
277✔
2249

2250
        va_start(ap, path);
277✔
2251
        l = strv_new_ap(path, ap);
277✔
2252
        va_end(ap);
277✔
2253

2254
        if (!l)
277✔
2255
                return -ENOMEM;
277✔
2256

2257
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
277✔
2258
        if (r < 0)
277✔
UNCOV
2259
                return r;
×
2260

2261
        return 0;
2262
}
2263

2264
static char *destroy_tree(char *path) {
251✔
2265
        if (!path)
251✔
2266
                return NULL;
2267

2268
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
88✔
2269
                log_debug("Spawning process to nuke '%s'", path);
88✔
2270

2271
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
88✔
2272
        }
2273

2274
        return mfree(path);
88✔
2275
}
2276

2277
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
95,003✔
2278
        assert(rt);
95,003✔
2279

2280
        if (rt->manager)
95,003✔
2281
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
150✔
2282

2283
        rt->id = mfree(rt->id);
95,003✔
2284
        rt->tmp_dir = mfree(rt->tmp_dir);
95,003✔
2285
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
95,003✔
2286
        safe_close_pair(rt->netns_storage_socket);
95,003✔
2287
        safe_close_pair(rt->ipcns_storage_socket);
95,003✔
2288
}
95,003✔
2289

2290
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
94,972✔
2291
        if (!rt)
94,972✔
2292
                return NULL;
2293

2294
        exec_shared_runtime_done(rt);
94,972✔
2295
        return mfree(rt);
94,972✔
2296
}
2297

2298
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
151✔
2299
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
97,365✔
2300

2301
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
51✔
2302
        if (!rt)
51✔
2303
                return NULL;
2304

2305
        assert(rt->n_ref > 0);
50✔
2306
        rt->n_ref--;
50✔
2307

2308
        if (rt->n_ref > 0)
50✔
2309
                return NULL;
2310

2311
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
50✔
2312
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
50✔
2313

2314
        return exec_shared_runtime_free(rt);
50✔
2315
}
2316

2317
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
94,972✔
2318
        _cleanup_free_ char *id_copy = NULL;
189,944✔
2319
        ExecSharedRuntime *n;
94,972✔
2320

2321
        assert(ret);
94,972✔
2322

2323
        id_copy = strdup(id);
94,972✔
2324
        if (!id_copy)
94,972✔
2325
                return -ENOMEM;
2326

2327
        n = new(ExecSharedRuntime, 1);
94,972✔
2328
        if (!n)
94,972✔
2329
                return -ENOMEM;
2330

2331
        *n = (ExecSharedRuntime) {
94,972✔
2332
                .id = TAKE_PTR(id_copy),
94,972✔
2333
                .netns_storage_socket = EBADF_PAIR,
2334
                .ipcns_storage_socket = EBADF_PAIR,
2335
        };
2336

2337
        *ret = n;
94,972✔
2338
        return 0;
94,972✔
2339
}
2340

2341
static int exec_shared_runtime_add(
150✔
2342
                Manager *m,
2343
                const char *id,
2344
                char **tmp_dir,
2345
                char **var_tmp_dir,
2346
                int netns_storage_socket[2],
2347
                int ipcns_storage_socket[2],
2348
                ExecSharedRuntime **ret) {
2349

2350
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
150✔
2351
        int r;
150✔
2352

2353
        assert(m);
150✔
2354
        assert(id);
150✔
2355

2356
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2357

2358
        r = exec_shared_runtime_allocate(&rt, id);
150✔
2359
        if (r < 0)
150✔
2360
                return r;
2361

2362
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
150✔
2363
        if (r < 0)
150✔
2364
                return r;
2365

2366
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
150✔
2367
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
150✔
2368
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
150✔
2369

2370
        if (netns_storage_socket) {
150✔
2371
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
150✔
2372
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
150✔
2373
        }
2374

2375
        if (ipcns_storage_socket) {
150✔
2376
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
150✔
2377
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
150✔
2378
        }
2379

2380
        rt->manager = m;
150✔
2381

2382
        if (ret)
150✔
2383
                *ret = rt;
71✔
2384
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2385
        TAKE_PTR(rt);
150✔
2386
        return 0;
150✔
2387
}
2388

2389
static int exec_shared_runtime_make(
5,448✔
2390
                Manager *m,
2391
                const ExecContext *c,
2392
                const char *id,
2393
                ExecSharedRuntime **ret) {
2394

2395
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
5,448✔
2396
        _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
10,896✔
2397
        int r;
5,448✔
2398

2399
        assert(m);
5,448✔
2400
        assert(c);
5,448✔
2401
        assert(id);
5,448✔
2402

2403
        /* It is not necessary to create ExecSharedRuntime object. */
2404
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
5,448✔
2405
                *ret = NULL;
5,377✔
2406
                return 0;
5,377✔
2407
        }
2408

2409
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
136✔
2410
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
65✔
UNCOV
2411
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
UNCOV
2412
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2413
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
65✔
2414
                if (r < 0)
65✔
2415
                        return r;
2416
        }
2417

2418
        if (exec_needs_network_namespace(c))
71✔
2419
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
7✔
UNCOV
2420
                        return -errno;
×
2421

2422
        if (exec_needs_ipc_namespace(c))
71✔
2423
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
UNCOV
2424
                        return -errno;
×
2425

2426
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
71✔
2427
        if (r < 0)
71✔
UNCOV
2428
                return r;
×
2429

2430
        return 1;
2431
}
2432

2433
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
5,527✔
2434
        ExecSharedRuntime *rt;
5,527✔
2435
        int r;
5,527✔
2436

2437
        assert(m);
5,527✔
2438
        assert(id);
5,527✔
2439
        assert(ret);
5,527✔
2440

2441
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
5,527✔
2442
        if (rt)
5,527✔
2443
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2444
                goto ref;
79✔
2445

2446
        if (!create) {
5,448✔
UNCOV
2447
                *ret = NULL;
×
UNCOV
2448
                return 0;
×
2449
        }
2450

2451
        /* If not found, then create a new object. */
2452
        r = exec_shared_runtime_make(m, c, id, &rt);
5,448✔
2453
        if (r < 0)
5,448✔
2454
                return r;
2455
        if (r == 0) {
5,448✔
2456
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2457
                *ret = NULL;
5,377✔
2458
                return 0;
5,377✔
2459
        }
2460

2461
ref:
71✔
2462
        /* increment reference counter. */
2463
        rt->n_ref++;
150✔
2464
        *ret = rt;
150✔
2465
        return 1;
150✔
2466
}
2467

2468
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
63✔
2469
        ExecSharedRuntime *rt;
63✔
2470

2471
        assert(m);
63✔
2472
        assert(f);
63✔
2473
        assert(fds);
63✔
2474

2475
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
163✔
2476
                fprintf(f, "exec-runtime=%s", rt->id);
100✔
2477

2478
                if (rt->tmp_dir)
100✔
2479
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
100✔
2480

2481
                if (rt->var_tmp_dir)
100✔
2482
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
100✔
2483

2484
                if (rt->netns_storage_socket[0] >= 0) {
100✔
2485
                        int copy;
2✔
2486

2487
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2✔
2488
                        if (copy < 0)
2✔
UNCOV
2489
                                return copy;
×
2490

2491
                        fprintf(f, " netns-socket-0=%i", copy);
2✔
2492
                }
2493

2494
                if (rt->netns_storage_socket[1] >= 0) {
100✔
2495
                        int copy;
2✔
2496

2497
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2✔
2498
                        if (copy < 0)
2✔
2499
                                return copy;
2500

2501
                        fprintf(f, " netns-socket-1=%i", copy);
2✔
2502
                }
2503

2504
                if (rt->ipcns_storage_socket[0] >= 0) {
100✔
UNCOV
2505
                        int copy;
×
2506

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

UNCOV
2511
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2512
                }
2513

2514
                if (rt->ipcns_storage_socket[1] >= 0) {
100✔
UNCOV
2515
                        int copy;
×
2516

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

UNCOV
2521
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2522
                }
2523

2524
                fputc('\n', f);
100✔
2525
        }
2526

2527
        return 0;
63✔
2528
}
2529

2530
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
97,215✔
2531
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
97,215✔
2532
        ExecSharedRuntime *rt = NULL;
97,215✔
2533
        int r;
97,215✔
2534

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

2540
        assert(u);
97,215✔
2541
        assert(key);
97,215✔
2542
        assert(value);
97,215✔
2543

2544
        /* Manager manages ExecSharedRuntime objects by the unit id.
2545
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2546
        if (isempty(u->id)) {
97,215✔
UNCOV
2547
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
UNCOV
2548
                return 0;
×
2549
        }
2550

2551
        if (u->manager) {
97,215✔
2552
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
97,215✔
UNCOV
2553
                        return log_oom();
×
2554

2555
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
97,215✔
2556
        }
2557
        if (!rt) {
97,215✔
2558
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
94,822✔
UNCOV
2559
                        return log_oom();
×
2560

2561
                rt = rt_create;
94,822✔
2562
        }
2563

2564
        if (streq(key, "tmp-dir")) {
97,215✔
UNCOV
2565
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2566
                        return -ENOMEM;
2567

2568
        } else if (streq(key, "var-tmp-dir")) {
97,215✔
UNCOV
2569
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2570
                        return -ENOMEM;
2571

2572
        } else if (streq(key, "netns-socket-0")) {
97,215✔
2573

UNCOV
2574
                safe_close(rt->netns_storage_socket[0]);
×
UNCOV
2575
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2576
                if (rt->netns_storage_socket[0] < 0)
×
2577
                        return 0;
2578

2579
        } else if (streq(key, "netns-socket-1")) {
97,215✔
2580

UNCOV
2581
                safe_close(rt->netns_storage_socket[1]);
×
UNCOV
2582
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2583
                if (rt->netns_storage_socket[1] < 0)
×
2584
                        return 0;
2585
        } else
2586
                return 0;
2587

2588
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
UNCOV
2589
        if (rt_create && u->manager) {
×
UNCOV
2590
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
2591
                if (r < 0) {
×
2592
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
2593
                        return 0;
×
2594
                }
2595

UNCOV
2596
                rt_create->manager = u->manager;
×
2597

2598
                /* Avoid cleanup */
UNCOV
2599
                TAKE_PTR(rt_create);
×
2600
        }
2601

2602
        return 1;
2603
}
2604

2605
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
79✔
2606
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
79✔
2607
        char *id = NULL;
79✔
2608
        int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
79✔
2609
        const char *p, *v = ASSERT_PTR(value);
79✔
2610
        size_t n;
79✔
2611

2612
        assert(m);
79✔
2613
        assert(fds);
79✔
2614

2615
        n = strcspn(v, " ");
79✔
2616
        id = strndupa_safe(v, n);
79✔
2617
        if (v[n] != ' ')
79✔
UNCOV
2618
                goto finalize;
×
2619
        p = v + n + 1;
79✔
2620

2621
        v = startswith(p, "tmp-dir=");
79✔
2622
        if (v) {
79✔
2623
                n = strcspn(v, " ");
79✔
2624
                tmp_dir = strndup(v, n);
79✔
2625
                if (!tmp_dir)
79✔
UNCOV
2626
                        return log_oom();
×
2627
                if (v[n] != ' ')
79✔
2628
                        goto finalize;
×
2629
                p = v + n + 1;
79✔
2630
        }
2631

2632
        v = startswith(p, "var-tmp-dir=");
79✔
2633
        if (v) {
79✔
2634
                n = strcspn(v, " ");
79✔
2635
                var_tmp_dir = strndup(v, n);
79✔
2636
                if (!var_tmp_dir)
79✔
UNCOV
2637
                        return log_oom();
×
2638
                if (v[n] != ' ')
79✔
2639
                        goto finalize;
78✔
2640
                p = v + n + 1;
1✔
2641
        }
2642

2643
        v = startswith(p, "netns-socket-0=");
1✔
2644
        if (v) {
1✔
2645
                char *buf;
1✔
2646

2647
                n = strcspn(v, " ");
1✔
2648
                buf = strndupa_safe(v, n);
1✔
2649

2650
                netns_fdpair[0] = deserialize_fd(fds, buf);
1✔
2651
                if (netns_fdpair[0] < 0)
1✔
2652
                        return netns_fdpair[0];
2653
                if (v[n] != ' ')
1✔
UNCOV
2654
                        goto finalize;
×
2655
                p = v + n + 1;
1✔
2656
        }
2657

2658
        v = startswith(p, "netns-socket-1=");
1✔
2659
        if (v) {
1✔
2660
                char *buf;
1✔
2661

2662
                n = strcspn(v, " ");
1✔
2663
                buf = strndupa_safe(v, n);
1✔
2664

2665
                netns_fdpair[1] = deserialize_fd(fds, buf);
1✔
2666
                if (netns_fdpair[1] < 0)
1✔
2667
                        return netns_fdpair[1];
2668
                if (v[n] != ' ')
1✔
2669
                        goto finalize;
1✔
UNCOV
2670
                p = v + n + 1;
×
2671
        }
2672

UNCOV
2673
        v = startswith(p, "ipcns-socket-0=");
×
UNCOV
2674
        if (v) {
×
2675
                char *buf;
×
2676

2677
                n = strcspn(v, " ");
×
UNCOV
2678
                buf = strndupa_safe(v, n);
×
2679

2680
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
UNCOV
2681
                if (ipcns_fdpair[0] < 0)
×
2682
                        return ipcns_fdpair[0];
2683
                if (v[n] != ' ')
×
UNCOV
2684
                        goto finalize;
×
2685
                p = v + n + 1;
×
2686
        }
2687

UNCOV
2688
        v = startswith(p, "ipcns-socket-1=");
×
UNCOV
2689
        if (v) {
×
2690
                char *buf;
×
2691

2692
                n = strcspn(v, " ");
×
UNCOV
2693
                buf = strndupa_safe(v, n);
×
2694

2695
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
UNCOV
2696
                if (ipcns_fdpair[1] < 0)
×
2697
                        return ipcns_fdpair[1];
2698
        }
2699

UNCOV
2700
finalize:
×
2701
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
79✔
2702
        if (r < 0)
79✔
UNCOV
2703
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2704
        return 0;
2705
}
2706

2707
void exec_shared_runtime_vacuum(Manager *m) {
1,500✔
2708
        ExecSharedRuntime *rt;
1,500✔
2709

2710
        assert(m);
1,500✔
2711

2712
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2713

2714
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,579✔
2715
                if (rt->n_ref > 0)
79✔
2716
                        continue;
79✔
2717

UNCOV
2718
                (void) exec_shared_runtime_free(rt);
×
2719
        }
2720
}
1,500✔
2721

2722
int exec_runtime_make(
5,527✔
2723
                const Unit *unit,
2724
                const ExecContext *context,
2725
                ExecSharedRuntime *shared,
2726
                DynamicCreds *creds,
2727
                ExecRuntime **ret) {
2728
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
5,527✔
2729
        _cleanup_free_ char *ephemeral = NULL;
5,527✔
2730
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
5,527✔
2731
        int r;
5,527✔
2732

2733
        assert(unit);
5,527✔
2734
        assert(context);
5,527✔
2735
        assert(ret);
5,527✔
2736

2737
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
5,527✔
2738
                *ret = NULL;
5,376✔
2739
                return 0;
5,376✔
2740
        }
2741

2742
        if (exec_needs_ephemeral(context)) {
151✔
UNCOV
2743
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
UNCOV
2744
                if (r < 0)
×
2745
                        return r;
2746

UNCOV
2747
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
UNCOV
2748
                if (r < 0)
×
2749
                        return r;
2750

UNCOV
2751
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
UNCOV
2752
                        return -errno;
×
2753
        }
2754

2755
        rt = new(ExecRuntime, 1);
151✔
2756
        if (!rt)
151✔
2757
                return -ENOMEM;
2758

2759
        *rt = (ExecRuntime) {
151✔
2760
                .shared = shared,
2761
                .dynamic_creds = creds,
2762
                .ephemeral_copy = TAKE_PTR(ephemeral),
151✔
2763
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
151✔
2764
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
151✔
2765
        };
2766

2767
        *ret = TAKE_PTR(rt);
151✔
2768
        return 1;
151✔
2769
}
2770

2771
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
48,275✔
2772
        if (!rt)
48,275✔
2773
                return NULL;
2774

2775
        exec_shared_runtime_unref(rt->shared);
151✔
2776
        dynamic_creds_unref(rt->dynamic_creds);
151✔
2777

2778
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
151✔
2779

2780
        safe_close_pair(rt->ephemeral_storage_socket);
151✔
2781
        return mfree(rt);
151✔
2782
}
2783

2784
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
5,795✔
2785
        if (!rt)
5,795✔
2786
                return NULL;
2787

2788
        rt->shared = exec_shared_runtime_destroy(rt->shared);
51✔
2789
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
51✔
2790
        return exec_runtime_free(rt);
51✔
2791
}
2792

2793
void exec_runtime_clear(ExecRuntime *rt) {
31✔
2794
        if (!rt)
31✔
2795
                return;
2796

2797
        safe_close_pair(rt->ephemeral_storage_socket);
31✔
2798
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
31✔
2799
}
2800

2801
void exec_params_shallow_clear(ExecParameters *p) {
2,146✔
2802
        if (!p)
2,146✔
2803
                return;
2804

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

2808
        p->environment = strv_free(p->environment);
2,146✔
2809
        p->fd_names = strv_free(p->fd_names);
2,146✔
2810
        p->files_env = strv_free(p->files_env);
2,146✔
2811
        p->fds = mfree(p->fds);
2,146✔
2812
        p->exec_fd = safe_close(p->exec_fd);
2,146✔
2813
        p->user_lookup_fd = -EBADF;
2,146✔
2814
        p->bpf_restrict_fs_map_fd = -EBADF;
2,146✔
2815
        p->unit_id = mfree(p->unit_id);
2,146✔
2816
        p->invocation_id = SD_ID128_NULL;
2,146✔
2817
        p->invocation_id_string[0] = '\0';
2,146✔
2818
        p->confirm_spawn = mfree(p->confirm_spawn);
2,146✔
2819
}
2820

2821
void exec_params_deep_clear(ExecParameters *p) {
31✔
2822
        if (!p)
31✔
2823
                return;
2824

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

2829
        close_many_unset(p->fds, p->n_socket_fds + p->n_stashed_fds);
31✔
2830

2831
        p->cgroup_path = mfree(p->cgroup_path);
31✔
2832

2833
        if (p->prefix) {
31✔
2834
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
31✔
2835
                p->prefix = mfree(p->prefix);
31✔
2836
        }
2837

2838
        p->received_credentials_directory = mfree(p->received_credentials_directory);
31✔
2839
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
31✔
2840

2841
        if (p->idle_pipe) {
31✔
UNCOV
2842
                close_many_and_free(p->idle_pipe, 4);
×
UNCOV
2843
                p->idle_pipe = NULL;
×
2844
        }
2845

2846
        p->stdin_fd = safe_close(p->stdin_fd);
31✔
2847
        p->stdout_fd = safe_close(p->stdout_fd);
31✔
2848
        p->stderr_fd = safe_close(p->stderr_fd);
31✔
2849

2850
        p->notify_socket = mfree(p->notify_socket);
31✔
2851

2852
        open_file_free_many(&p->open_files);
31✔
2853

2854
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
31✔
2855

2856
        exec_params_shallow_clear(p);
31✔
2857
}
2858

2859
void exec_directory_done(ExecDirectory *d) {
241,275✔
2860
        if (!d)
241,275✔
2861
                return;
2862

2863
        FOREACH_ARRAY(i, d->items, d->n_items) {
242,784✔
2864
                free(i->path);
1,509✔
2865
                strv_free(i->symlinks);
1,509✔
2866
        }
2867

2868
        d->items = mfree(d->items);
241,275✔
2869
        d->n_items = 0;
241,275✔
2870
        d->mode = 0755;
241,275✔
2871
}
2872

2873
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
5,167✔
2874
        assert(d);
5,167✔
2875
        assert(path);
5,167✔
2876

2877
        FOREACH_ARRAY(i, d->items, d->n_items)
7,501✔
2878
                if (path_equal(i->path, path))
2,349✔
2879
                        return i;
2880

2881
        return NULL;
2882
}
2883

2884
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
5,167✔
UNCOV
2885
        _cleanup_strv_free_ char **s = NULL;
×
2886
        _cleanup_free_ char *p = NULL;
5,167✔
2887
        ExecDirectoryItem *existing;
5,167✔
2888
        int r;
5,167✔
2889

2890
        assert(d);
5,167✔
2891
        assert(path);
5,167✔
2892

2893
        existing = exec_directory_find(d, path);
5,167✔
2894
        if (existing) {
5,167✔
2895
                r = strv_extend(&existing->symlinks, symlink);
15✔
2896
                if (r < 0)
15✔
2897
                        return r;
2898

2899
                existing->flags |= flags;
15✔
2900

2901
                return 0; /* existing item is updated */
15✔
2902
        }
2903

2904
        p = strdup(path);
5,152✔
2905
        if (!p)
5,152✔
2906
                return -ENOMEM;
2907

2908
        if (symlink) {
5,152✔
2909
                s = strv_new(symlink);
6✔
2910
                if (!s)
6✔
2911
                        return -ENOMEM;
2912
        }
2913

2914
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
5,152✔
2915
                return -ENOMEM;
2916

2917
        d->items[d->n_items++] = (ExecDirectoryItem) {
5,152✔
2918
                .path = TAKE_PTR(p),
5,152✔
2919
                .symlinks = TAKE_PTR(s),
5,152✔
2920
                .flags = flags,
2921
        };
2922

2923
        return 1; /* new item is added */
5,152✔
2924
}
2925

2926
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
862✔
2927
        assert(a);
862✔
2928
        assert(b);
862✔
2929

2930
        return path_compare(a->path, b->path);
862✔
2931
}
2932

2933
void exec_directory_sort(ExecDirectory *d) {
132,556✔
2934
        assert(d);
132,556✔
2935

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

2941
        if (d->n_items <= 1)
132,556✔
2942
                return;
2943

2944
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
156✔
2945

2946
        for (size_t i = 1; i < d->n_items; i++)
675✔
2947
                for (size_t j = 0; j < i; j++)
1,724✔
2948
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,220✔
2949
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
2950
                                break;
15✔
2951
                        }
2952
}
2953

UNCOV
2954
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
UNCOV
2955
        ExecDirectoryType t;
×
2956

2957
        assert(s);
×
2958

2959
        if (streq(s, "all"))
×
2960
                return EXEC_CLEAN_ALL;
2961
        if (streq(s, "fdstore"))
×
2962
                return EXEC_CLEAN_FDSTORE;
2963

UNCOV
2964
        t = exec_resource_type_from_string(s);
×
UNCOV
2965
        if (t < 0)
×
2966
                return (ExecCleanMask) t;
2967

UNCOV
2968
        return 1U << t;
×
2969
}
2970

2971
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
2972
        [EXEC_INPUT_NULL]      = "null",
2973
        [EXEC_INPUT_TTY]       = "tty",
2974
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
2975
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
2976
        [EXEC_INPUT_SOCKET]    = "socket",
2977
        [EXEC_INPUT_NAMED_FD]  = "fd",
2978
        [EXEC_INPUT_DATA]      = "data",
2979
        [EXEC_INPUT_FILE]      = "file",
2980
};
2981

2982
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,009✔
2983

2984
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
2985
        [EXEC_OUTPUT_INHERIT]             = "inherit",
2986
        [EXEC_OUTPUT_NULL]                = "null",
2987
        [EXEC_OUTPUT_TTY]                 = "tty",
2988
        [EXEC_OUTPUT_KMSG]                = "kmsg",
2989
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
2990
        [EXEC_OUTPUT_JOURNAL]             = "journal",
2991
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
2992
        [EXEC_OUTPUT_SOCKET]              = "socket",
2993
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
2994
        [EXEC_OUTPUT_FILE]                = "file",
2995
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
2996
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
2997
};
2998

2999
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
32,232✔
3000

3001
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
3002
        [EXEC_UTMP_INIT]  = "init",
3003
        [EXEC_UTMP_LOGIN] = "login",
3004
        [EXEC_UTMP_USER]  = "user",
3005
};
3006

3007
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,302✔
3008

3009
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
3010
        [EXEC_PRESERVE_NO]      = "no",
3011
        [EXEC_PRESERVE_YES]     = "yes",
3012
        [EXEC_PRESERVE_RESTART] = "restart",
3013
};
3014

3015
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
17,276✔
3016

3017
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
3018
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3019
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
3020
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
3021
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
3022
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
3023
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
3024
};
3025

3026
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3027

3028
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3029
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
3030
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
3031
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
3032
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
3033
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
3034
};
3035

UNCOV
3036
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3037

3038
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
3039
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
3040
 * directories, specifically .timer units with their timestamp touch file. */
3041
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3042
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
3043
        [EXEC_DIRECTORY_STATE]         = "state",
3044
        [EXEC_DIRECTORY_CACHE]         = "cache",
3045
        [EXEC_DIRECTORY_LOGS]          = "logs",
3046
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
3047
};
3048

3049
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
274✔
3050

3051
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3052
        [EXEC_KEYRING_INHERIT] = "inherit",
3053
        [EXEC_KEYRING_PRIVATE] = "private",
3054
        [EXEC_KEYRING_SHARED]  = "shared",
3055
};
3056

3057
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
15,640✔
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