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

systemd / systemd / 13533485051

25 Feb 2025 10:20PM UTC coverage: 71.818% (+0.04%) from 71.774%
13533485051

push

github

web-flow
make integritysetup/veritysetup more alike cryptsetup when it comes to remote operation (#36501)

Let's address some asymmetries here.

0 of 12 new or added lines in 1 file covered. (0.0%)

96 existing lines in 30 files now uncovered.

294319 of 409813 relevant lines covered (71.82%)

716680.45 hits per line

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

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

3
#include <errno.h>
4
#include <fcntl.h>
5
#include <poll.h>
6
#include <sys/file.h>
7
#include <sys/mman.h>
8
#include <sys/personality.h>
9
#include <sys/prctl.h>
10
#include <sys/shm.h>
11
#include <sys/types.h>
12
#include <sys/un.h>
13
#include <unistd.h>
14
#include <utmpx.h>
15

16
#include <linux/fs.h> /* Must be included after <sys/mount.h> */
17

18
#include "sd-messages.h"
19

20
#include "af-list.h"
21
#include "alloc-util.h"
22
#include "async.h"
23
#include "bitfield.h"
24
#include "cap-list.h"
25
#include "capability-util.h"
26
#include "cgroup-setup.h"
27
#include "constants.h"
28
#include "cpu-set-util.h"
29
#include "env-file.h"
30
#include "env-util.h"
31
#include "errno-list.h"
32
#include "escape.h"
33
#include "exec-credential.h"
34
#include "execute.h"
35
#include "execute-serialize.h"
36
#include "exit-status.h"
37
#include "fd-util.h"
38
#include "fileio.h"
39
#include "format-util.h"
40
#include "glob-util.h"
41
#include "hexdecoct.h"
42
#include "ioprio-util.h"
43
#include "lock-util.h"
44
#include "log.h"
45
#include "macro.h"
46
#include "manager.h"
47
#include "manager-dump.h"
48
#include "memory-util.h"
49
#include "missing_fs.h"
50
#include "missing_prctl.h"
51
#include "mkdir-label.h"
52
#include "namespace.h"
53
#include "parse-util.h"
54
#include "path-util.h"
55
#include "process-util.h"
56
#include "rlimit-util.h"
57
#include "rm-rf.h"
58
#include "seccomp-util.h"
59
#include "securebits-util.h"
60
#include "selinux-util.h"
61
#include "serialize.h"
62
#include "sort-util.h"
63
#include "special.h"
64
#include "stat-util.h"
65
#include "string-table.h"
66
#include "string-util.h"
67
#include "strv.h"
68
#include "syslog-util.h"
69
#include "terminal-util.h"
70
#include "tmpfile-util.h"
71
#include "umask-util.h"
72
#include "unit-serialize.h"
73
#include "user-util.h"
74
#include "utmp-wtmp.h"
75

76
static bool is_terminal_input(ExecInput i) {
43,245✔
77
        return IN_SET(i,
43,245✔
78
                      EXEC_INPUT_TTY,
79
                      EXEC_INPUT_TTY_FORCE,
80
                      EXEC_INPUT_TTY_FAIL);
81
}
82

83
static bool is_terminal_output(ExecOutput o) {
85,430✔
84
        return IN_SET(o,
85,430✔
85
                      EXEC_OUTPUT_TTY,
86
                      EXEC_OUTPUT_KMSG_AND_CONSOLE,
87
                      EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
88
}
89

90
const char* exec_context_tty_path(const ExecContext *context) {
17,640✔
91
        assert(context);
17,640✔
92

93
        if (context->stdio_as_fds)
17,640✔
94
                return NULL;
95

96
        if (context->tty_path)
17,082✔
97
                return context->tty_path;
681✔
98

99
        return "/dev/console";
100
}
101

102
int exec_context_apply_tty_size(
1,745✔
103
                const ExecContext *context,
104
                int input_fd,
105
                int output_fd,
106
                const char *tty_path) {
107

108
        unsigned rows, cols;
1,745✔
109
        int r;
1,745✔
110

111
        assert(context);
1,745✔
112
        assert(input_fd >= 0);
1,745✔
113
        assert(output_fd >= 0);
1,745✔
114

115
        if (!isatty_safe(output_fd))
1,745✔
116
                return 0;
1,745✔
117

118
        if (!tty_path)
1,085✔
119
                tty_path = exec_context_tty_path(context);
419✔
120

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

125
        /* Fill in data from kernel command line if anything is unspecified */
126
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
1,085✔
127
                (void) proc_cmdline_tty_size(
1,053✔
128
                                tty_path,
129
                                rows == UINT_MAX ? &rows : NULL,
130
                                cols == UINT_MAX ? &cols : NULL);
131

132
        /* If we got nothing so far and we are talking to a physical device, and the TTY reset logic is on,
133
         * then let's query dimensions from the ANSI driver. */
134
        if (rows == UINT_MAX && cols == UINT_MAX &&
1,085✔
135
            context->tty_reset &&
404✔
136
            terminal_is_pty_fd(output_fd) == 0 &&
372✔
137
            isatty_safe(input_fd)) {
181✔
138
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
181✔
139
                if (r < 0)
181✔
140
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
181✔
141
        }
142

143
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
1,085✔
144
}
145

146
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p) {
15,986✔
147
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
31,972✔
148
        int fd, r;
15,986✔
149

150
        assert(context);
15,986✔
151

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

157
        const char *path = exec_context_tty_path(context);
15,986✔
158

159
        if (p && p->stdout_fd >= 0 && isatty_safe(p->stdout_fd))
15,986✔
160
                fd = p->stdout_fd;
16✔
161
        else if (path && (context->tty_path || is_terminal_input(context->std_input) ||
15,970✔
162
                        is_terminal_output(context->std_output) || is_terminal_output(context->std_error))) {
15,099✔
163
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
666✔
164
                if (fd < 0)
666✔
UNCOV
165
                        return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path);
×
166
        } else
167
                return;   /* nothing to do */
168

169
        /* Take a synchronization lock for the duration of the setup that we do here.
170
         * systemd-vconsole-setup.service also takes the lock to avoid being interrupted. We open a new fd
171
         * that will be closed automatically, and operate on it for convenience. */
172
        lock_fd = lock_dev_console();
682✔
173
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
682✔
174
                log_debug_errno(lock_fd, "No privileges to lock /dev/console, proceeding without lock: %m");
×
175
        else if (ERRNO_IS_NEG_DEVICE_ABSENT(lock_fd))
682✔
176
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
177
        else if (lock_fd < 0)
682✔
178
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
179

180
        if (context->tty_reset)
682✔
181
                (void) terminal_reset_defensive(fd, /* switch_to_text= */ true);
173✔
182

183
        r = exec_context_apply_tty_size(context, fd, fd, path);
682✔
184
        if (r < 0)
682✔
185
                log_debug_errno(r, "Failed to configure TTY dimensions, ignoring: %m");
×
186

187
        if (context->tty_vhangup)
682✔
188
                (void) terminal_vhangup_fd(fd);
168✔
189

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

194
        if (context->tty_vt_disallocate && path)
682✔
195
                (void) vt_disallocate(path);
92✔
196
}
197

198
bool exec_needs_network_namespace(const ExecContext *context) {
36,140✔
199
        assert(context);
36,140✔
200

201
        return context->private_network || context->network_namespace_path;
36,140✔
202
}
203

204
static bool exec_needs_ephemeral(const ExecContext *context) {
7,715✔
205
        return (context->root_image || context->root_directory) && context->root_ephemeral;
7,715✔
206
}
207

208
bool exec_needs_ipc_namespace(const ExecContext *context) {
32,358✔
209
        assert(context);
32,358✔
210

211
        return context->private_ipc || context->ipc_namespace_path;
32,358✔
212
}
213

214
static bool can_apply_cgroup_namespace(const ExecContext *context, const ExecParameters *params) {
38✔
215
        return cg_all_unified() > 0 && ns_type_supported(NAMESPACE_CGROUP);
38✔
216
}
217

218
static bool needs_cgroup_namespace(ProtectControlGroups i) {
65,157✔
219
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
65,157✔
220
}
221

222
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context, const ExecParameters *params) {
40,956✔
223
        assert(context);
40,956✔
224

225
        /* If cgroup namespace is configured via ProtectControlGroups=private or strict but we can't actually
226
         * use cgroup namespace, either from not having unified hierarchy or kernel support, we ignore the
227
         * setting and do not unshare the namespace. ProtectControlGroups=private and strict get downgraded
228
         * to no and yes respectively. This ensures that strict always gets a read-only mount of /sys/fs/cgroup.
229
         *
230
         * TODO: Remove fallback once cgroupv1 support is removed in v258. */
231
        if (needs_cgroup_namespace(context->protect_control_groups) && !can_apply_cgroup_namespace(context, params)) {
40,956✔
232
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_PRIVATE)
×
233
                        return PROTECT_CONTROL_GROUPS_NO;
234
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_STRICT)
×
235
                        return PROTECT_CONTROL_GROUPS_YES;
236
        }
237
        return context->protect_control_groups;
40,956✔
238
}
239

240
bool exec_needs_cgroup_namespace(const ExecContext *context, const ExecParameters *params) {
24,201✔
241
        assert(context);
24,201✔
242

243
        return needs_cgroup_namespace(exec_get_protect_control_groups(context, params));
24,201✔
244
}
245

246
bool exec_needs_cgroup_mount(const ExecContext *context, const ExecParameters *params) {
11,655✔
247
        assert(context);
11,655✔
248

249
        return exec_get_protect_control_groups(context, params) != PROTECT_CONTROL_GROUPS_NO;
11,655✔
250
}
251

252
bool exec_is_cgroup_mount_read_only(const ExecContext *context, const ExecParameters *params) {
2,550✔
253
        assert(context);
2,550✔
254

255
        return IN_SET(exec_get_protect_control_groups(context, params), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
2,550✔
256
}
257

258
bool exec_needs_pid_namespace(const ExecContext *context) {
53,980✔
259
        assert(context);
53,980✔
260

261
        return context->private_pids != PRIVATE_PIDS_NO && ns_type_supported(NAMESPACE_PID);
53,980✔
262
}
263

264
bool exec_needs_mount_namespace(
14,420✔
265
                const ExecContext *context,
266
                const ExecParameters *params,
267
                const ExecRuntime *runtime) {
268

269
        assert(context);
14,420✔
270

271
        if (context->root_image)
14,420✔
272
                return true;
273

274
        if (!strv_isempty(context->read_write_paths) ||
14,402✔
275
            !strv_isempty(context->read_only_paths) ||
13,365✔
276
            !strv_isempty(context->inaccessible_paths) ||
13,354✔
277
            !strv_isempty(context->exec_paths) ||
13,351✔
278
            !strv_isempty(context->no_exec_paths))
13,351✔
279
                return true;
280

281
        if (context->n_bind_mounts > 0)
13,351✔
282
                return true;
283

284
        if (context->n_temporary_filesystems > 0)
13,311✔
285
                return true;
286

287
        if (context->n_mount_images > 0)
13,258✔
288
                return true;
289

290
        if (context->n_extension_images > 0)
13,250✔
291
                return true;
292

293
        if (!strv_isempty(context->extension_directories))
13,246✔
294
                return true;
295

296
        if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
13,244✔
297
                return true;
298

299
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
13,244✔
300
                return true;
301

302
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
12,630✔
303
                return true;
304

305
        if (context->private_devices ||
12,413✔
306
            context->private_mounts > 0 ||
12,049✔
307
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
11,872✔
308
            context->protect_system != PROTECT_SYSTEM_NO ||
11,871✔
309
            context->protect_home != PROTECT_HOME_NO ||
310
            context->protect_kernel_tunables ||
311
            context->protect_kernel_modules ||
11,871✔
312
            context->protect_kernel_logs ||
11,039✔
313
            exec_needs_cgroup_mount(context, params) ||
11,039✔
314
            context->protect_proc != PROTECT_PROC_DEFAULT ||
11,033✔
315
            context->proc_subset != PROC_SUBSET_ALL ||
10,996✔
316
            exec_needs_ipc_namespace(context) ||
21,992✔
317
            exec_needs_pid_namespace(context))
10,996✔
318
                return true;
1,452✔
319

320
        if (context->root_directory) {
10,961✔
321
                if (exec_context_get_effective_mount_apivfs(context))
2✔
322
                        return true;
323

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

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

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

339
        if (exec_context_get_effective_bind_log_sockets(context))
10,959✔
340
                return true;
341

342
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
65,618✔
343
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
57,031✔
344
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
2,370✔
345
                                return true;
346

347
        return false;
348
}
349

350
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
5,004✔
351
        assert(context);
5,004✔
352
        assert(params);
5,004✔
353

354
        if (!params->notify_socket)
5,004✔
355
                return NULL;
356

357
        if (!needs_sandboxing)
4,372✔
358
                return NULL;
359

360
        if (!context->root_directory && !context->root_image)
4,372✔
361
                return NULL;
362

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

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

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

372
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
12,952✔
373
        assert(context);
12,952✔
374

375
        if (!context->dynamic_user)
12,952✔
376
                return false;
377

378
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
98✔
379
                return false;
380

381
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
92✔
382
                return false;
14✔
383

384
        return true;
385
}
386

387
int exec_params_get_cgroup_path(
17,950✔
388
                const ExecParameters *params,
389
                const CGroupContext *c,
390
                char **ret) {
391

392
        const char *subgroup = NULL;
17,950✔
393
        char *p;
17,950✔
394

395
        assert(params);
17,950✔
396
        assert(ret);
17,950✔
397

398
        if (!params->cgroup_path)
17,950✔
399
                return -EINVAL;
400

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

410
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
17,950✔
411
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
739✔
412
                        subgroup = ".control";
413
                else
414
                        subgroup = c->delegate_subgroup;
739✔
415
        }
416

417
        if (subgroup)
739✔
418
                p = path_join(params->cgroup_path, subgroup);
739✔
419
        else
420
                p = strdup(params->cgroup_path);
17,211✔
421
        if (!p)
17,950✔
422
                return -ENOMEM;
423

424
        *ret = p;
17,950✔
425
        return !!subgroup;
17,950✔
426
}
427

428
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
1,137✔
429
        assert(c);
1,137✔
430

431
        return c->cpu_affinity_from_numa;
1,137✔
432
}
433

434
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
3,821✔
435
        assert(unit);
3,821✔
436
        assert(msg);
3,821✔
437
        assert(executable);
3,821✔
438

439
        if (!DEBUG_LOGGING)
3,821✔
440
                return;
3,821✔
441

442
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
7,642✔
443

444
        log_unit_struct(unit, LOG_DEBUG,
3,821✔
445
                        "EXECUTABLE=%s", executable,
446
                        LOG_UNIT_MESSAGE(unit, "%s: %s", msg, strnull(cmdline)),
447
                        LOG_UNIT_INVOCATION_ID(unit));
448
}
449

450
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
451

452
int exec_spawn(
3,821✔
453
                Unit *unit,
454
                ExecCommand *command,
455
                const ExecContext *context,
456
                ExecParameters *params,
457
                ExecRuntime *runtime,
458
                const CGroupContext *cgroup_context,
459
                PidRef *ret) {
460

461
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL, *executor_path = NULL;
3,821✔
462
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
463
        _cleanup_fclose_ FILE *f = NULL;
3,821✔
464
        int r;
3,821✔
465

466
        assert(unit);
3,821✔
467
        assert(unit->manager);
3,821✔
468
        assert(unit->manager->executor_fd >= 0);
3,821✔
469
        assert(command);
3,821✔
470
        assert(context);
3,821✔
471
        assert(params);
3,821✔
472
        assert(!params->fds || FLAGS_SET(params->flags, EXEC_PASS_FDS));
3,821✔
473
        assert(params->fds || (params->n_socket_fds + params->n_storage_fds + params->n_extra_fds == 0));
3,821✔
474
        assert(!params->files_env); /* We fill this field, ensure it comes NULL-initialized to us */
3,821✔
475
        assert(ret);
3,821✔
476

477
        LOG_CONTEXT_PUSH_UNIT(unit);
7,642✔
478

479
        r = exec_context_load_environment(unit, context, &params->files_env);
3,821✔
480
        if (r < 0)
3,821✔
481
                return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
×
482

483
        /* We won't know the real executable path until we create the mount namespace in the child, but we
484
           want to log from the parent, so we use the possibly inaccurate path here. */
485
        log_command_line(unit, "About to execute", command->path, command->argv);
3,821✔
486

487
        if (params->cgroup_path) {
3,821✔
488
                r = exec_params_get_cgroup_path(params, cgroup_context, &subcgroup_path);
3,821✔
489
                if (r < 0)
3,821✔
490
                        return log_unit_error_errno(unit, r, "Failed to acquire subcgroup path: %m");
×
491
                if (r > 0) {
3,821✔
492
                        /* If there's a subcgroup, then let's create it here now (the main cgroup was already
493
                         * realized by the unit logic) */
494

495
                        r = cg_create(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path);
101✔
496
                        if (r < 0)
101✔
497
                                return log_unit_error_errno(unit, r, "Failed to create subcgroup '%s': %m", subcgroup_path);
×
498
                }
499
        }
500

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

508
        r = open_serialization_file("sd-executor-state", &f);
3,821✔
509
        if (r < 0)
3,821✔
510
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
511

512
        fdset = fdset_new();
3,821✔
513
        if (!fdset)
3,821✔
514
                return log_oom();
×
515

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

520
        r = finish_serialization_file(f);
3,821✔
521
        if (r < 0)
3,821✔
522
                return log_unit_error_errno(unit, r, "Failed to finish serialization stream: %m");
×
523

524
        r = fd_cloexec(fileno(f), false);
3,821✔
525
        if (r < 0)
3,821✔
526
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m");
×
527

528
        r = fdset_cloexec(fdset, false);
3,821✔
529
        if (r < 0)
3,821✔
530
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m");
×
531

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

539
        r = fd_get_path(unit->manager->executor_fd, &executor_path);
3,821✔
540
        if (r < 0)
3,821✔
541
                return log_unit_error_errno(unit, r, "Failed to get executor path from fd: %m");
×
542

543
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
3,821✔
544
        xsprintf(serialization_fd_number, "%i", fileno(f));
3,821✔
545

546
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
3,821✔
547
        dual_timestamp start_timestamp;
3,821✔
548

549
        /* Restore the original ambient capability set the manager was started with to pass it to
550
         * sd-executor. */
551
        r = capability_ambient_set_apply(unit->manager->saved_ambient_set, /* also_inherit= */ false);
3,821✔
552
        if (r < 0)
3,821✔
553
                return log_unit_error_errno(unit, r, "Failed to apply the starting ambient set: %m");
×
554

555
        /* Record the start timestamp before we fork so that it is guaranteed to be earlier than the
556
         * handoff timestamp. */
557
        dual_timestamp_now(&start_timestamp);
3,821✔
558

559
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
560
        r = posix_spawn_wrapper(
7,642✔
561
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
3,821✔
562
                        STRV_MAKE(executor_path,
3,821✔
563
                                  "--deserialize", serialization_fd_number,
564
                                  "--log-level", max_log_levels,
565
                                  "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))),
566
                        environ,
567
                        cg_unified() > 0 ? subcgroup_path : NULL,
568
                        &pidref);
569

570
        /* Drop the ambient set again, so no processes other than sd-executore spawned from the manager inherit it. */
571
        (void) capability_ambient_set_apply(0, /* also_inherit= */ false);
3,821✔
572

573
        if (r == -EUCLEAN && subcgroup_path)
3,821✔
574
                return log_unit_error_errno(unit, r,
×
575
                                            "Failed to spawn process into cgroup '%s', because the cgroup "
576
                                            "or one of its parents or siblings is in the threaded mode.",
577
                                            subcgroup_path);
578
        if (r < 0)
3,821✔
579
                return log_unit_error_errno(unit, r, "Failed to spawn executor: %m");
×
580
        /* We add the new process to the cgroup both in the child (so that we can be sure that no user code is ever
581
         * executed outside of the cgroup) and in the parent (so that we can be sure that when we kill the cgroup the
582
         * process will be killed too). */
583
        if (r == 0 && subcgroup_path)
3,821✔
584
                (void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path, pidref.pid);
×
585
        /* r > 0: Already in the right cgroup thanks to CLONE_INTO_CGROUP */
586

587
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
7,642✔
588
                       command->path, pidref.pid, r > 0 ? "via" : "without");
589

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

592
        *ret = TAKE_PIDREF(pidref);
3,821✔
593
        return 0;
3,821✔
594
}
595

596
void exec_context_init(ExecContext *c) {
63,300✔
597
        assert(c);
63,300✔
598

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

602
        *c = (ExecContext) {
63,300✔
603
                .umask = 0022,
604
                .ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO,
63,300✔
605
                .cpu_sched_policy = SCHED_OTHER,
606
                .syslog_priority = LOG_DAEMON|LOG_INFO,
607
                .syslog_level_prefix = true,
608
                .ignore_sigpipe = true,
609
                .timer_slack_nsec = NSEC_INFINITY,
610
                .personality = PERSONALITY_INVALID,
611
                .timeout_clean_usec = USEC_INFINITY,
612
                .capability_bounding_set = CAP_MASK_UNSET,
613
                .restrict_namespaces = NAMESPACE_FLAGS_INITIAL,
614
                .log_level_max = -1,
615
#if HAVE_SECCOMP
616
                .syscall_errno = SECCOMP_ERROR_NUMBER_KILL,
617
#endif
618
                .tty_rows = UINT_MAX,
619
                .tty_cols = UINT_MAX,
620
                .private_mounts = -1,
621
                .mount_apivfs = -1,
622
                .bind_log_sockets = -1,
623
                .memory_ksm = -1,
624
                .set_login_environment = -1,
625
        };
626

627
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
379,800✔
628
                d->mode = 0755;
316,500✔
629

630
        numa_policy_reset(&c->numa_policy);
63,300✔
631

632
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
63,300✔
633
}
63,300✔
634

635
void exec_context_done(ExecContext *c) {
49,850✔
636
        assert(c);
49,850✔
637

638
        c->environment = strv_free(c->environment);
49,850✔
639
        c->environment_files = strv_free(c->environment_files);
49,850✔
640
        c->pass_environment = strv_free(c->pass_environment);
49,850✔
641
        c->unset_environment = strv_free(c->unset_environment);
49,850✔
642

643
        rlimit_free_all(c->rlimit);
49,850✔
644

645
        for (size_t l = 0; l < 3; l++) {
199,400✔
646
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
149,550✔
647
                c->stdio_file[l] = mfree(c->stdio_file[l]);
149,550✔
648
        }
649

650
        c->working_directory = mfree(c->working_directory);
49,850✔
651
        c->root_directory = mfree(c->root_directory);
49,850✔
652
        c->root_image = mfree(c->root_image);
49,850✔
653
        c->root_image_options = mount_options_free_all(c->root_image_options);
49,850✔
654
        c->root_hash = mfree(c->root_hash);
49,850✔
655
        c->root_hash_size = 0;
49,850✔
656
        c->root_hash_path = mfree(c->root_hash_path);
49,850✔
657
        c->root_hash_sig = mfree(c->root_hash_sig);
49,850✔
658
        c->root_hash_sig_size = 0;
49,850✔
659
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
49,850✔
660
        c->root_verity = mfree(c->root_verity);
49,850✔
661
        c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
49,850✔
662
        c->extension_directories = strv_free(c->extension_directories);
49,850✔
663
        c->tty_path = mfree(c->tty_path);
49,850✔
664
        c->syslog_identifier = mfree(c->syslog_identifier);
49,850✔
665
        c->user = mfree(c->user);
49,850✔
666
        c->group = mfree(c->group);
49,850✔
667

668
        c->supplementary_groups = strv_free(c->supplementary_groups);
49,850✔
669

670
        c->pam_name = mfree(c->pam_name);
49,850✔
671

672
        c->read_only_paths = strv_free(c->read_only_paths);
49,850✔
673
        c->read_write_paths = strv_free(c->read_write_paths);
49,850✔
674
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
49,850✔
675
        c->exec_paths = strv_free(c->exec_paths);
49,850✔
676
        c->no_exec_paths = strv_free(c->no_exec_paths);
49,850✔
677
        c->exec_search_path = strv_free(c->exec_search_path);
49,850✔
678

679
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
49,850✔
680
        c->bind_mounts = NULL;
49,850✔
681
        c->n_bind_mounts = 0;
49,850✔
682
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
49,850✔
683
        c->temporary_filesystems = NULL;
49,850✔
684
        c->n_temporary_filesystems = 0;
49,850✔
685
        c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
49,850✔
686

687
        cpu_set_reset(&c->cpu_set);
49,850✔
688
        numa_policy_reset(&c->numa_policy);
49,850✔
689

690
        c->utmp_id = mfree(c->utmp_id);
49,850✔
691
        c->selinux_context = mfree(c->selinux_context);
49,850✔
692
        c->apparmor_profile = mfree(c->apparmor_profile);
49,850✔
693
        c->smack_process_label = mfree(c->smack_process_label);
49,850✔
694

695
        c->restrict_filesystems = set_free_free(c->restrict_filesystems);
49,850✔
696

697
        c->syscall_filter = hashmap_free(c->syscall_filter);
49,850✔
698
        c->syscall_archs = set_free(c->syscall_archs);
49,850✔
699
        c->syscall_log = hashmap_free(c->syscall_log);
49,850✔
700
        c->address_families = set_free(c->address_families);
49,850✔
701

702
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
299,100✔
703
                exec_directory_done(d);
249,250✔
704

705
        c->log_level_max = -1;
49,850✔
706

707
        exec_context_free_log_extra_fields(c);
49,850✔
708
        c->log_filter_allowed_patterns = set_free_free(c->log_filter_allowed_patterns);
49,850✔
709
        c->log_filter_denied_patterns = set_free_free(c->log_filter_denied_patterns);
49,850✔
710

711
        c->log_ratelimit = (RateLimit) {};
49,850✔
712

713
        c->stdin_data = mfree(c->stdin_data);
49,850✔
714
        c->stdin_data_size = 0;
49,850✔
715

716
        c->network_namespace_path = mfree(c->network_namespace_path);
49,850✔
717
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
49,850✔
718

719
        c->log_namespace = mfree(c->log_namespace);
49,850✔
720

721
        c->load_credentials = hashmap_free(c->load_credentials);
49,850✔
722
        c->set_credentials = hashmap_free(c->set_credentials);
49,850✔
723
        c->import_credentials = ordered_set_free(c->import_credentials);
49,850✔
724

725
        c->root_image_policy = image_policy_free(c->root_image_policy);
49,850✔
726
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
49,850✔
727
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
49,850✔
728

729
        c->private_hostname = mfree(c->private_hostname);
49,850✔
730
}
49,850✔
731

732
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
5,710✔
733
        assert(c);
5,710✔
734

735
        if (!runtime_prefix)
5,710✔
736
                return 0;
737

738
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
5,774✔
739
                _cleanup_free_ char *p = NULL;
64✔
740

741
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
64✔
742
                        p = path_join(runtime_prefix, "private", i->path);
×
743
                else
744
                        p = path_join(runtime_prefix, i->path);
64✔
745
                if (!p)
64✔
746
                        return -ENOMEM;
747

748
                /* We execute this synchronously, since we need to be sure this is gone when we start the
749
                 * service next. */
750
                (void) rm_rf(p, REMOVE_ROOT);
64✔
751

752
                STRV_FOREACH(symlink, i->symlinks) {
64✔
753
                        _cleanup_free_ char *symlink_abs = NULL;
×
754

755
                        if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
×
756
                                symlink_abs = path_join(runtime_prefix, "private", *symlink);
×
757
                        else
758
                                symlink_abs = path_join(runtime_prefix, *symlink);
×
759
                        if (!symlink_abs)
×
760
                                return -ENOMEM;
×
761

762
                        (void) unlink(symlink_abs);
×
763
                }
764
        }
765

766
        return 0;
767
}
768

769
int exec_context_destroy_mount_ns_dir(Unit *u) {
11,408✔
770
        _cleanup_free_ char *p = NULL;
11,408✔
771

772
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
11,408✔
773
                return 0;
774

775
        p = path_join("/run/systemd/propagate/", u->id);
5,038✔
776
        if (!p)
5,038✔
777
                return -ENOMEM;
778

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

783
        return 0;
784
}
785

786
void exec_command_done(ExecCommand *c) {
101,247✔
787
        assert(c);
101,247✔
788

789
        c->path = mfree(c->path);
101,247✔
790
        c->argv = strv_free(c->argv);
101,247✔
791
}
101,247✔
792

793
void exec_command_done_array(ExecCommand *c, size_t n) {
26,772✔
794
        FOREACH_ARRAY(i, c, n)
107,087✔
795
                exec_command_done(i);
80,315✔
796
}
26,772✔
797

798
ExecCommand* exec_command_free(ExecCommand *c) {
20,904✔
799
        if (!c)
20,904✔
800
                return NULL;
801

802
        exec_command_done(c);
20,904✔
803
        return mfree(c);
20,904✔
804
}
805

806
ExecCommand* exec_command_free_list(ExecCommand *c) {
149,951✔
807
        ExecCommand *i;
149,951✔
808

809
        while ((i = LIST_POP(command, c)))
170,855✔
810
                exec_command_free(i);
20,904✔
811

812
        return NULL;
149,951✔
813
}
814

815
void exec_command_free_array(ExecCommand **c, size_t n) {
23,050✔
816
        FOREACH_ARRAY(i, c, n)
172,986✔
817
                *i = exec_command_free_list(*i);
149,936✔
818
}
23,050✔
819

820
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
5,358✔
821
        FOREACH_ARRAY(i, c, n)
21,431✔
822
                exec_status_reset(&i->exec_status);
16,073✔
823
}
5,358✔
824

825
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
5,992✔
826
        FOREACH_ARRAY(i, c, n)
42,116✔
827
                LIST_FOREACH(command, z, *i)
39,570✔
828
                        exec_status_reset(&z->exec_status);
3,446✔
829
}
5,992✔
830

831
typedef struct InvalidEnvInfo {
832
        const Unit *unit;
833
        const char *path;
834
} InvalidEnvInfo;
835

836
static void invalid_env(const char *p, void *userdata) {
×
837
        InvalidEnvInfo *info = userdata;
×
838

839
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
840
}
×
841

842
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
43,848✔
843
        assert(c);
43,848✔
844

845
        switch (fd_index) {
43,848✔
846

847
        case STDIN_FILENO:
14,616✔
848
                if (c->std_input != EXEC_INPUT_NAMED_FD)
14,616✔
849
                        return NULL;
850

851
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
852

853
        case STDOUT_FILENO:
14,616✔
854
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
14,616✔
855
                        return NULL;
856

857
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
858

859
        case STDERR_FILENO:
14,616✔
860
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
14,616✔
861
                        return NULL;
862

863
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
864

865
        default:
866
                return NULL;
867
        }
868
}
869

870
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
3,821✔
871
        _cleanup_strv_free_ char **v = NULL;
3,821✔
872
        int r;
3,821✔
873

874
        assert(c);
3,821✔
875
        assert(ret);
3,821✔
876

877
        STRV_FOREACH(i, c->environment_files) {
3,823✔
878
                _cleanup_globfree_ glob_t pglob = {};
2✔
879
                bool ignore = false;
2✔
880
                char *fn = *i;
2✔
881

882
                if (fn[0] == '-') {
2✔
883
                        ignore = true;
1✔
884
                        fn++;
1✔
885
                }
886

887
                if (!path_is_absolute(fn)) {
2✔
888
                        if (ignore)
×
889
                                continue;
×
890
                        return -EINVAL;
891
                }
892

893
                /* Filename supports globbing, take all matching files */
894
                r = safe_glob(fn, 0, &pglob);
2✔
895
                if (r < 0) {
2✔
896
                        if (ignore)
1✔
897
                                continue;
1✔
898
                        return r;
899
                }
900

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

904
                FOREACH_ARRAY(path, pglob.gl_pathv, pglob.gl_pathc) {
2✔
905
                        _cleanup_strv_free_ char **p = NULL;
1✔
906

907
                        r = load_env_file(NULL, *path, &p);
1✔
908
                        if (r < 0) {
1✔
909
                                if (ignore)
×
910
                                        continue;
×
911
                                return r;
912
                        }
913

914
                        /* Log invalid environment variables with filename */
915
                        if (p) {
1✔
916
                                InvalidEnvInfo info = {
1✔
917
                                        .unit = unit,
918
                                        .path = *path,
1✔
919
                                };
920

921
                                p = strv_env_clean_with_callback(p, invalid_env, &info);
1✔
922
                        }
923

924
                        if (!v)
1✔
925
                                v = TAKE_PTR(p);
1✔
926
                        else {
927
                                char **m = strv_env_merge(v, p);
×
928
                                if (!m)
×
929
                                        return -ENOMEM;
×
930

931
                                strv_free_and_replace(v, m);
×
932
                        }
933
                }
934
        }
935

936
        *ret = TAKE_PTR(v);
3,821✔
937

938
        return 0;
3,821✔
939
}
940

941
static bool tty_may_match_dev_console(const char *tty) {
347✔
942
        _cleanup_free_ char *resolved = NULL;
347✔
943

944
        if (!tty)
347✔
945
                return true;
946

947
        tty = skip_dev_prefix(tty);
347✔
948

949
        /* trivial identity? */
950
        if (streq(tty, "console"))
347✔
951
                return true;
952

953
        if (resolve_dev_console(&resolved) < 0)
24✔
954
                return true; /* if we could not resolve, assume it may */
955

956
        /* "tty0" means the active VC, so it may be the same sometimes */
957
        return path_equal(resolved, tty) || (streq(resolved, "tty0") && tty_is_vc(tty));
24✔
958
}
959

960
static bool exec_context_may_touch_tty(const ExecContext *ec) {
28,069✔
961
        assert(ec);
28,069✔
962

963
        return ec->tty_reset ||
28,069✔
964
                ec->tty_vhangup ||
28,069✔
965
                ec->tty_vt_disallocate ||
27,937✔
966
                is_terminal_input(ec->std_input) ||
27,937✔
967
                is_terminal_output(ec->std_output) ||
55,916✔
968
                is_terminal_output(ec->std_error);
27,673✔
969
}
970

971
bool exec_context_may_touch_console(const ExecContext *ec) {
25,561✔
972

973
        return exec_context_may_touch_tty(ec) &&
25,908✔
974
               tty_may_match_dev_console(exec_context_tty_path(ec));
347✔
975
}
976

977
static void strv_fprintf(FILE *f, char **l) {
×
978
        assert(f);
×
979

980
        STRV_FOREACH(g, l)
×
981
                fprintf(f, " %s", *g);
×
982
}
×
983

984
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
1,792✔
985
        assert(f);
1,792✔
986
        assert(prefix);
1,792✔
987
        assert(name);
1,792✔
988

989
        if (!strv_isempty(strv)) {
1,792✔
990
                fprintf(f, "%s%s:", prefix, name);
×
991
                strv_fprintf(f, strv);
×
992
                fputs("\n", f);
×
993
        }
994
}
1,792✔
995

996
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
997
        assert(p);
×
998
        assert(f);
×
999

1000
        prefix = strempty(prefix);
×
1001

1002
        fprintf(f,
×
1003
                "%sRuntimeScope: %s\n"
1004
                "%sExecFlags: %u\n"
1005
                "%sSELinuxContextNetwork: %s\n"
1006
                "%sCgroupSupportedMask: %u\n"
1007
                "%sCgroupPath: %s\n"
1008
                "%sCrededentialsDirectory: %s\n"
1009
                "%sEncryptedCredentialsDirectory: %s\n"
1010
                "%sConfirmSpawn: %s\n"
1011
                "%sShallConfirmSpawn: %s\n"
1012
                "%sWatchdogUSec: " USEC_FMT "\n"
1013
                "%sNotifySocket: %s\n"
1014
                "%sDebugInvocation: %s\n"
1015
                "%sFallbackSmackProcessLabel: %s\n",
1016
                prefix, runtime_scope_to_string(p->runtime_scope),
×
1017
                prefix, p->flags,
×
1018
                prefix, yes_no(p->selinux_context_net),
×
1019
                prefix, p->cgroup_supported,
×
1020
                prefix, p->cgroup_path,
×
1021
                prefix, strempty(p->received_credentials_directory),
×
1022
                prefix, strempty(p->received_encrypted_credentials_directory),
×
1023
                prefix, strempty(p->confirm_spawn),
×
1024
                prefix, yes_no(p->shall_confirm_spawn),
×
1025
                prefix, p->watchdog_usec,
×
1026
                prefix, strempty(p->notify_socket),
×
1027
                prefix, yes_no(p->debug_invocation),
×
1028
                prefix, strempty(p->fallback_smack_process_label));
×
1029

1030
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1031
        strv_dump(f, prefix, "Environment", p->environment);
×
1032
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1033

1034
        LIST_FOREACH(open_files, file, p->open_files)
×
1035
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1036

1037
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1038
}
×
1039

1040
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
224✔
1041
        int r;
224✔
1042

1043
        assert(c);
224✔
1044
        assert(f);
224✔
1045

1046
        prefix = strempty(prefix);
224✔
1047

1048
        fprintf(f,
224✔
1049
                "%sUMask: %04o\n"
1050
                "%sWorkingDirectory: %s\n"
1051
                "%sRootDirectory: %s\n"
1052
                "%sRootEphemeral: %s\n"
1053
                "%sNonBlocking: %s\n"
1054
                "%sPrivateTmp: %s\n"
1055
                "%sPrivateDevices: %s\n"
1056
                "%sProtectKernelTunables: %s\n"
1057
                "%sProtectKernelModules: %s\n"
1058
                "%sProtectKernelLogs: %s\n"
1059
                "%sProtectClock: %s\n"
1060
                "%sProtectControlGroups: %s\n"
1061
                "%sPrivateNetwork: %s\n"
1062
                "%sPrivateUsers: %s\n"
1063
                "%sPrivatePIDs: %s\n"
1064
                "%sProtectHome: %s\n"
1065
                "%sProtectSystem: %s\n"
1066
                "%sMountAPIVFS: %s\n"
1067
                "%sBindLogSockets: %s\n"
1068
                "%sIgnoreSIGPIPE: %s\n"
1069
                "%sMemoryDenyWriteExecute: %s\n"
1070
                "%sRestrictRealtime: %s\n"
1071
                "%sRestrictSUIDSGID: %s\n"
1072
                "%sKeyringMode: %s\n"
1073
                "%sProtectHostname: %s%s%s\n"
1074
                "%sProtectProc: %s\n"
1075
                "%sProcSubset: %s\n",
1076
                prefix, c->umask,
224✔
1077
                prefix, empty_to_root(c->working_directory),
224✔
1078
                prefix, empty_to_root(c->root_directory),
224✔
1079
                prefix, yes_no(c->root_ephemeral),
224✔
1080
                prefix, yes_no(c->non_blocking),
224✔
1081
                prefix, private_tmp_to_string(c->private_tmp),
224✔
1082
                prefix, yes_no(c->private_devices),
224✔
1083
                prefix, yes_no(c->protect_kernel_tunables),
224✔
1084
                prefix, yes_no(c->protect_kernel_modules),
224✔
1085
                prefix, yes_no(c->protect_kernel_logs),
224✔
1086
                prefix, yes_no(c->protect_clock),
224✔
1087
                prefix, protect_control_groups_to_string(c->protect_control_groups),
224✔
1088
                prefix, yes_no(c->private_network),
224✔
1089
                prefix, private_users_to_string(c->private_users),
224✔
1090
                prefix, private_pids_to_string(c->private_pids),
224✔
1091
                prefix, protect_home_to_string(c->protect_home),
224✔
1092
                prefix, protect_system_to_string(c->protect_system),
224✔
1093
                prefix, yes_no(exec_context_get_effective_mount_apivfs(c)),
224✔
1094
                prefix, yes_no(exec_context_get_effective_bind_log_sockets(c)),
224✔
1095
                prefix, yes_no(c->ignore_sigpipe),
224✔
1096
                prefix, yes_no(c->memory_deny_write_execute),
224✔
1097
                prefix, yes_no(c->restrict_realtime),
224✔
1098
                prefix, yes_no(c->restrict_suid_sgid),
224✔
1099
                prefix, exec_keyring_mode_to_string(c->keyring_mode),
224✔
1100
                prefix, protect_hostname_to_string(c->protect_hostname), c->private_hostname ? ":" : "", strempty(c->private_hostname),
448✔
1101
                prefix, protect_proc_to_string(c->protect_proc),
224✔
1102
                prefix, proc_subset_to_string(c->proc_subset));
224✔
1103

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

1107
        if (c->root_image)
224✔
1108
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1109

1110
        if (c->root_image_options) {
224✔
1111
                fprintf(f, "%sRootImageOptions:", prefix);
×
1112
                LIST_FOREACH(mount_options, o, c->root_image_options)
×
1113
                        if (!isempty(o->options))
×
1114
                                fprintf(f, " %s:%s",
×
1115
                                        partition_designator_to_string(o->partition_designator),
1116
                                        o->options);
1117
                fprintf(f, "\n");
×
1118
        }
1119

1120
        if (c->root_hash) {
224✔
1121
                _cleanup_free_ char *encoded = NULL;
×
1122
                encoded = hexmem(c->root_hash, c->root_hash_size);
×
1123
                if (encoded)
×
1124
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1125
        }
1126

1127
        if (c->root_hash_path)
224✔
1128
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1129

1130
        if (c->root_hash_sig) {
224✔
1131
                _cleanup_free_ char *encoded = NULL;
×
1132
                ssize_t len;
×
1133
                len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded);
×
1134
                if (len)
×
1135
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1136
        }
1137

1138
        if (c->root_hash_sig_path)
224✔
1139
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1140

1141
        if (c->root_verity)
224✔
1142
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1143

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

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

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

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

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

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

1161
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
1,130✔
1162
                        fprintf(f,
10✔
1163
                                "%s%s: %s%s\n",
1164
                                prefix,
1165
                                exec_directory_type_to_string(dt),
1166
                                c->directories[dt].items[i].path,
1167
                                FLAGS_SET(c->directories[dt].items[i].flags, EXEC_DIRECTORY_READ_ONLY) ? " (ro)" : "");
10✔
1168

1169
                        STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
10✔
1170
                                fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
×
1171
                }
1172
        }
1173

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

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

1179
        if (c->nice_set)
224✔
1180
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
×
1181

1182
        if (c->oom_score_adjust_set)
224✔
1183
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
10✔
1184

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

1188
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
3,808✔
1189
                if (c->rlimit[i]) {
3,584✔
1190
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
20✔
1191
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1192
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
20✔
1193
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
20✔
1194
                }
1195

1196
        if (c->ioprio_set) {
224✔
1197
                _cleanup_free_ char *class_str = NULL;
×
1198

1199
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
×
1200
                if (r >= 0)
×
1201
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
×
1202

1203
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
×
1204
        }
1205

1206
        if (c->cpu_sched_set) {
224✔
1207
                _cleanup_free_ char *policy_str = NULL;
×
1208

1209
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1210
                if (r >= 0)
×
1211
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1212

1213
                fprintf(f,
×
1214
                        "%sCPUSchedulingPriority: %i\n"
1215
                        "%sCPUSchedulingResetOnFork: %s\n",
1216
                        prefix, c->cpu_sched_priority,
×
1217
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1218
        }
1219

1220
        if (c->cpu_set.set) {
224✔
1221
                _cleanup_free_ char *affinity = NULL;
×
1222

1223
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1224
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1225
        }
1226

1227
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
224✔
1228
                _cleanup_free_ char *nodes = NULL;
1✔
1229

1230
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1231
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1232
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1233
        }
1234

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

1238
        fprintf(f,
224✔
1239
                "%sStandardInput: %s\n"
1240
                "%sStandardOutput: %s\n"
1241
                "%sStandardError: %s\n",
1242
                prefix, exec_input_to_string(c->std_input),
224✔
1243
                prefix, exec_output_to_string(c->std_output),
224✔
1244
                prefix, exec_output_to_string(c->std_error));
224✔
1245

1246
        if (c->std_input == EXEC_INPUT_NAMED_FD)
224✔
1247
                fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
×
1248
        if (c->std_output == EXEC_OUTPUT_NAMED_FD)
224✔
1249
                fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
×
1250
        if (c->std_error == EXEC_OUTPUT_NAMED_FD)
224✔
1251
                fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
×
1252

1253
        if (c->std_input == EXEC_INPUT_FILE)
224✔
1254
                fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
×
1255
        if (c->std_output == EXEC_OUTPUT_FILE)
224✔
1256
                fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1257
        if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
224✔
1258
                fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1259
        if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1260
                fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1261
        if (c->std_error == EXEC_OUTPUT_FILE)
224✔
1262
                fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1263
        if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
224✔
1264
                fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1265
        if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1266
                fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1267

1268
        if (c->tty_path)
224✔
1269
                fprintf(f,
×
1270
                        "%sTTYPath: %s\n"
1271
                        "%sTTYReset: %s\n"
1272
                        "%sTTYVHangup: %s\n"
1273
                        "%sTTYVTDisallocate: %s\n"
1274
                        "%sTTYRows: %u\n"
1275
                        "%sTTYColumns: %u\n",
1276
                        prefix, c->tty_path,
1277
                        prefix, yes_no(c->tty_reset),
×
1278
                        prefix, yes_no(c->tty_vhangup),
×
1279
                        prefix, yes_no(c->tty_vt_disallocate),
×
1280
                        prefix, c->tty_rows,
×
1281
                        prefix, c->tty_cols);
×
1282

1283
        if (IN_SET(c->std_output,
224✔
1284
                   EXEC_OUTPUT_KMSG,
1285
                   EXEC_OUTPUT_JOURNAL,
1286
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1287
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1288
            IN_SET(c->std_error,
17✔
1289
                   EXEC_OUTPUT_KMSG,
1290
                   EXEC_OUTPUT_JOURNAL,
1291
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1292
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1293

1294
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
207✔
1295

1296
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
207✔
1297
                if (r >= 0)
207✔
1298
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
207✔
1299

1300
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
207✔
1301
                if (r >= 0)
207✔
1302
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
207✔
1303
        }
1304

1305
        if (c->log_level_max >= 0) {
224✔
1306
                _cleanup_free_ char *t = NULL;
1✔
1307

1308
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1309

1310
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1311
        }
1312

1313
        if (c->log_ratelimit.interval > 0)
224✔
1314
                fprintf(f,
×
1315
                        "%sLogRateLimitIntervalSec: %s\n",
1316
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1317

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

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

1324
                char *pattern;
×
1325
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1326
                        fprintf(f, " %s", pattern);
×
1327
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1328
                        fprintf(f, " ~%s", pattern);
×
1329
                fputc('\n', f);
×
1330
        }
1331

1332
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
228✔
1333
                fprintf(f, "%sLogExtraFields: ", prefix);
4✔
1334
                fwrite(field->iov_base, 1, field->iov_len, f);
4✔
1335
                fputc('\n', f);
4✔
1336
        }
1337

1338
        if (c->log_namespace)
224✔
1339
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1340

1341
        if (c->secure_bits) {
224✔
1342
                _cleanup_free_ char *str = NULL;
×
1343

1344
                r = secure_bits_to_string_alloc(c->secure_bits, &str);
×
1345
                if (r >= 0)
×
1346
                        fprintf(f, "%sSecure Bits: %s\n", prefix, str);
×
1347
        }
1348

1349
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
224✔
1350
                _cleanup_free_ char *str = NULL;
16✔
1351

1352
                r = capability_set_to_string(c->capability_bounding_set, &str);
16✔
1353
                if (r >= 0)
16✔
1354
                        fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
16✔
1355
        }
1356

1357
        if (c->capability_ambient_set != 0) {
224✔
1358
                _cleanup_free_ char *str = NULL;
×
1359

1360
                r = capability_set_to_string(c->capability_ambient_set, &str);
×
1361
                if (r >= 0)
×
1362
                        fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
×
1363
        }
1364

1365
        if (c->user)
224✔
1366
                fprintf(f, "%sUser: %s\n", prefix, c->user);
1✔
1367
        if (c->group)
224✔
1368
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1369

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

1372
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
224✔
1373

1374
        if (c->pam_name)
224✔
1375
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
×
1376

1377
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
224✔
1378
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
224✔
1379
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
224✔
1380
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
224✔
1381
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
224✔
1382
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
224✔
1383

1384
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
228✔
1385
                fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
4✔
1386
                        mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
4✔
1387
                        mount->ignore_enoent ? "-": "",
4✔
1388
                        mount->source,
1389
                        mount->destination,
1390
                        mount->recursive ? "rbind" : "norbind");
4✔
1391

1392
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
224✔
1393
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1394
                        tmpfs->path,
1395
                        isempty(tmpfs->options) ? "" : ":",
×
1396
                        strempty(tmpfs->options));
×
1397

1398
        if (c->utmp_id)
224✔
1399
                fprintf(f,
×
1400
                        "%sUtmpIdentifier: %s\n",
1401
                        prefix, c->utmp_id);
1402

1403
        if (c->selinux_context)
224✔
1404
                fprintf(f,
×
1405
                        "%sSELinuxContext: %s%s\n",
1406
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1407

1408
        if (c->apparmor_profile)
224✔
1409
                fprintf(f,
×
1410
                        "%sAppArmorProfile: %s%s\n",
1411
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1412

1413
        if (c->smack_process_label)
224✔
1414
                fprintf(f,
×
1415
                        "%sSmackProcessLabel: %s%s\n",
1416
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1417

1418
        if (c->personality != PERSONALITY_INVALID)
224✔
1419
                fprintf(f,
1✔
1420
                        "%sPersonality: %s\n",
1421
                        prefix, strna(personality_to_string(c->personality)));
1422

1423
        fprintf(f,
224✔
1424
                "%sLockPersonality: %s\n",
1425
                prefix, yes_no(c->lock_personality));
224✔
1426

1427
        if (c->syscall_filter) {
224✔
1428
                fprintf(f,
11✔
1429
                        "%sSystemCallFilter: ",
1430
                        prefix);
1431

1432
                if (!c->syscall_allow_list)
11✔
1433
                        fputc('~', f);
×
1434

1435
#if HAVE_SECCOMP
1436
                void *id, *val;
11✔
1437
                bool first = true;
11✔
1438
                HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
4,169✔
1439
                        _cleanup_free_ char *name = NULL;
4,158✔
1440
                        const char *errno_name = NULL;
4,158✔
1441
                        int num = PTR_TO_INT(val);
4,158✔
1442

1443
                        if (first)
4,158✔
1444
                                first = false;
1445
                        else
1446
                                fputc(' ', f);
4,147✔
1447

1448
                        name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
4,158✔
1449
                        fputs(strna(name), f);
4,158✔
1450

1451
                        if (num >= 0) {
4,158✔
1452
                                errno_name = seccomp_errno_or_action_to_string(num);
×
1453
                                if (errno_name)
×
1454
                                        fprintf(f, ":%s", errno_name);
×
1455
                                else
1456
                                        fprintf(f, ":%d", num);
×
1457
                        }
1458
                }
1459
#endif
1460

1461
                fputc('\n', f);
11✔
1462
        }
1463

1464
        if (c->syscall_archs) {
224✔
1465
                fprintf(f,
11✔
1466
                        "%sSystemCallArchitectures:",
1467
                        prefix);
1468

1469
#if HAVE_SECCOMP
1470
                void *id;
11✔
1471
                SET_FOREACH(id, c->syscall_archs)
22✔
1472
                        fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
11✔
1473
#endif
1474
                fputc('\n', f);
11✔
1475
        }
1476

1477
        if (exec_context_restrict_namespaces_set(c)) {
224✔
1478
                _cleanup_free_ char *s = NULL;
12✔
1479

1480
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
12✔
1481
                if (r >= 0)
12✔
1482
                        fprintf(f, "%sRestrictNamespaces: %s\n",
24✔
1483
                                prefix, strna(s));
1484
        }
1485

1486
#if HAVE_LIBBPF
1487
        if (exec_context_restrict_filesystems_set(c)) {
224✔
1488
                char *fs;
×
1489
                SET_FOREACH(fs, c->restrict_filesystems)
×
1490
                        fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
×
1491
        }
1492
#endif
1493

1494
        if (c->network_namespace_path)
224✔
1495
                fprintf(f,
×
1496
                        "%sNetworkNamespacePath: %s\n",
1497
                        prefix, c->network_namespace_path);
1498

1499
        if (c->syscall_errno > 0) {
224✔
1500
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
223✔
1501

1502
#if HAVE_SECCOMP
1503
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
223✔
1504
                if (errno_name)
223✔
1505
                        fputs(errno_name, f);
223✔
1506
                else
1507
                        fprintf(f, "%d", c->syscall_errno);
×
1508
#endif
1509
                fputc('\n', f);
223✔
1510
        }
1511

1512
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
224✔
1513
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1514
                        mount->ignore_enoent ? "-": "",
×
1515
                        mount->source,
1516
                        mount->destination);
1517
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1518
                        fprintf(f, ":%s:%s",
×
1519
                                partition_designator_to_string(o->partition_designator),
1520
                                strempty(o->options));
×
1521
                fprintf(f, "\n");
×
1522
        }
1523

1524
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
224✔
1525
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1526
                        mount->ignore_enoent ? "-": "",
×
1527
                        mount->source);
1528
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1529
                        fprintf(f, ":%s:%s",
×
1530
                                partition_designator_to_string(o->partition_designator),
1531
                                strempty(o->options));
×
1532
                fprintf(f, "\n");
×
1533
        }
1534

1535
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
224✔
1536
}
224✔
1537

1538
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1539
        assert(c);
×
1540

1541
        /* Returns true if the process forked off would run under
1542
         * an unchanged UID or as root. */
1543

1544
        if (!c->user)
×
1545
                return true;
1546

1547
        if (STR_IN_SET(c->user, "root", "0"))
×
1548
                return true;
×
1549

1550
        return false;
×
1551
}
1552

1553
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,274✔
1554
        int p;
2,274✔
1555

1556
        assert(c);
2,274✔
1557

1558
        if (c->ioprio_set)
2,274✔
1559
                return c->ioprio;
18✔
1560

1561
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,256✔
1562
        if (p < 0)
2,256✔
1563
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1564

1565
        return ioprio_normalize(p);
2,256✔
1566
}
1567

1568
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
21,327✔
1569
        assert(c);
21,327✔
1570

1571
        /* Explicit setting wins */
1572
        if (c->mount_apivfs >= 0)
21,327✔
1573
                return c->mount_apivfs > 0;
110✔
1574

1575
        /* Default to "yes" if root directory or image are specified */
1576
        if (exec_context_with_rootfs(c))
21,217✔
1577
                return true;
47✔
1578

1579
        return false;
1580
}
1581

1582
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
14,870✔
1583
        assert(c);
14,870✔
1584

1585
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1586
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1587
        if (c->log_namespace)
14,870✔
1588
                return true;
1589

1590
        if (c->bind_log_sockets >= 0)
14,866✔
1591
                return c->bind_log_sockets > 0;
2✔
1592

1593
        if (exec_context_get_effective_mount_apivfs(c))
14,864✔
1594
                return true;
1595

1596
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1597
        if (exec_context_with_rootfs(c) && c->private_devices)
14,805✔
1598
                return true;
×
1599

1600
        return false;
1601
}
1602

1603
void exec_context_free_log_extra_fields(ExecContext *c) {
49,852✔
1604
        assert(c);
49,852✔
1605

1606
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
49,857✔
1607
                free(field->iov_base);
5✔
1608

1609
        c->log_extra_fields = mfree(c->log_extra_fields);
49,852✔
1610
        c->n_log_extra_fields = 0;
49,852✔
1611
}
49,852✔
1612

1613
void exec_context_revert_tty(ExecContext *c) {
2,508✔
1614
        _cleanup_close_ int fd = -EBADF;
2,508✔
1615
        const char *path;
2,508✔
1616
        struct stat st;
2,508✔
1617
        int r;
2,508✔
1618

1619
        assert(c);
2,508✔
1620

1621
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1622
        exec_context_tty_reset(c, /* parameters= */ NULL);
2,508✔
1623

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

1630
        path = exec_context_tty_path(c);
49✔
1631
        if (!path)
49✔
1632
                return;
1633

1634
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
49✔
1635
        if (fd < 0)
49✔
1636
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1637
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1638
                                             path);
1639

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

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

1651
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
49✔
1652
        if (r < 0)
49✔
1653
                log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s to " UID_FMT ":" GID_FMT ", ignoring: %m", path, (uid_t) 0, (gid_t) TTY_GID);
49✔
1654
}
1655

1656
int exec_context_get_clean_directories(
×
1657
                ExecContext *c,
1658
                char **prefix,
1659
                ExecCleanMask mask,
1660
                char ***ret) {
1661

1662
        _cleanup_strv_free_ char **l = NULL;
×
1663
        int r;
×
1664

1665
        assert(c);
×
1666
        assert(prefix);
×
1667
        assert(ret);
×
1668

1669
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
1670
                if (!BIT_SET(mask, t))
×
1671
                        continue;
×
1672

1673
                if (!prefix[t])
×
1674
                        continue;
×
1675

1676
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
×
1677
                        char *j;
×
1678

1679
                        j = path_join(prefix[t], i->path);
×
1680
                        if (!j)
×
1681
                                return -ENOMEM;
1682

1683
                        r = strv_consume(&l, j);
×
1684
                        if (r < 0)
×
1685
                                return r;
1686

1687
                        /* Also remove private directories unconditionally. */
1688
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
×
1689
                                j = path_join(prefix[t], "private", i->path);
×
1690
                                if (!j)
×
1691
                                        return -ENOMEM;
1692

1693
                                r = strv_consume(&l, j);
×
1694
                                if (r < 0)
×
1695
                                        return r;
1696
                        }
1697

1698
                        STRV_FOREACH(symlink, i->symlinks) {
×
1699
                                j = path_join(prefix[t], *symlink);
×
1700
                                if (!j)
×
1701
                                        return -ENOMEM;
1702

1703
                                r = strv_consume(&l, j);
×
1704
                                if (r < 0)
×
1705
                                        return r;
1706
                        }
1707
                }
1708
        }
1709

1710
        *ret = TAKE_PTR(l);
×
1711
        return 0;
×
1712
}
1713

1714
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,117✔
1715
        ExecCleanMask mask = 0;
1,117✔
1716

1717
        assert(c);
1,117✔
1718
        assert(ret);
1,117✔
1719

1720
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
6,702✔
1721
                if (c->directories[t].n_items > 0)
5,585✔
1722
                        mask |= 1U << t;
331✔
1723

1724
        *ret = mask;
1,117✔
1725
        return 0;
1,117✔
1726
}
1727

1728
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,137✔
1729
        int n = 0, r;
1,137✔
1730

1731
        assert(c);
1,137✔
1732

1733
        if (c->oom_score_adjust_set)
1,137✔
1734
                return c->oom_score_adjust;
348✔
1735

1736
        r = get_oom_score_adjust(&n);
789✔
1737
        if (r < 0)
789✔
1738
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1739

1740
        return n;
789✔
1741
}
1742

1743
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1,137✔
1744
        _cleanup_free_ char *t = NULL;
1,137✔
1745
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1,137✔
1746
        int r;
1,137✔
1747

1748
        assert(c);
1,137✔
1749

1750
        if (c->coredump_filter_set)
1,137✔
1751
                return c->coredump_filter;
×
1752

1753
        r = read_one_line_file("/proc/self/coredump_filter", &t);
1,137✔
1754
        if (r < 0)
1,137✔
1755
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1756
        else {
1757
                r = safe_atoux64(t, &n);
1,137✔
1758
                if (r < 0)
1,137✔
1759
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1760
        }
1761

1762
        return n;
1,137✔
1763
}
1764

1765
int exec_context_get_nice(const ExecContext *c) {
1,137✔
1766
        int n;
1,137✔
1767

1768
        assert(c);
1,137✔
1769

1770
        if (c->nice_set)
1,137✔
1771
                return c->nice;
6✔
1772

1773
        errno = 0;
1,131✔
1774
        n = getpriority(PRIO_PROCESS, 0);
1,131✔
1775
        if (errno > 0) {
1,131✔
1776
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1777
                n = 0;
1778
        }
1779

1780
        return n;
1781
}
1782

1783
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,137✔
1784
        int n;
1,137✔
1785

1786
        assert(c);
1,137✔
1787

1788
        if (c->cpu_sched_set)
1,137✔
1789
                return c->cpu_sched_policy;
×
1790

1791
        n = sched_getscheduler(0);
1,137✔
1792
        if (n < 0)
1,137✔
1793
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1794

1795
        return n < 0 ? SCHED_OTHER : n;
1,137✔
1796
}
1797

1798
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,137✔
1799
        struct sched_param p = {};
1,137✔
1800
        int r;
1,137✔
1801

1802
        assert(c);
1,137✔
1803

1804
        if (c->cpu_sched_set)
1,137✔
1805
                return c->cpu_sched_priority;
×
1806

1807
        r = sched_getparam(0, &p);
1,137✔
1808
        if (r < 0)
1,137✔
1809
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1810

1811
        return r >= 0 ? p.sched_priority : 0;
1,137✔
1812
}
1813

1814
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,137✔
1815
        int r;
1,137✔
1816

1817
        assert(c);
1,137✔
1818

1819
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,137✔
1820
                return c->timer_slack_nsec;
1821

1822
        r = prctl(PR_GET_TIMERSLACK);
1,137✔
1823
        if (r < 0)
1,137✔
1824
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1825

1826
        return (uint64_t) MAX(r, 0);
1,137✔
1827
}
1828

1829
bool exec_context_get_set_login_environment(const ExecContext *c) {
12,219✔
1830
        assert(c);
12,219✔
1831

1832
        if (c->set_login_environment >= 0)
12,219✔
1833
                return c->set_login_environment;
×
1834

1835
        return c->user || c->dynamic_user || c->pam_name;
22,351✔
1836
}
1837

1838
char** exec_context_get_syscall_filter(const ExecContext *c) {
1,137✔
1839
        _cleanup_strv_free_ char **l = NULL;
1,137✔
1840

1841
        assert(c);
1,137✔
1842

1843
#if HAVE_SECCOMP
1844
        void *id, *val;
1,137✔
1845
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
18,653✔
1846
                _cleanup_free_ char *name = NULL;
17,516✔
1847
                const char *e = NULL;
17,516✔
1848
                char *s;
17,516✔
1849
                int num = PTR_TO_INT(val);
17,516✔
1850

1851
                if (c->syscall_allow_list && num >= 0)
17,516✔
1852
                        /* syscall with num >= 0 in allow-list is denied. */
1853
                        continue;
×
1854

1855
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
17,516✔
1856
                if (!name)
17,516✔
1857
                        continue;
×
1858

1859
                if (num >= 0) {
17,516✔
1860
                        e = seccomp_errno_or_action_to_string(num);
×
1861
                        if (e) {
×
1862
                                s = strjoin(name, ":", e);
×
1863
                                if (!s)
×
1864
                                        return NULL;
1865
                        } else {
1866
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1867
                                        return NULL;
1868
                        }
1869
                } else
1870
                        s = TAKE_PTR(name);
17,516✔
1871

1872
                if (strv_consume(&l, s) < 0)
17,516✔
1873
                        return NULL;
1874
        }
1875

1876
        strv_sort(l);
1,137✔
1877
#endif
1878

1879
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,137✔
1880
}
1881

1882
char** exec_context_get_syscall_archs(const ExecContext *c) {
1,137✔
1883
        _cleanup_strv_free_ char **l = NULL;
1,137✔
1884

1885
        assert(c);
1,137✔
1886

1887
#if HAVE_SECCOMP
1888
        void *id;
1,137✔
1889
        SET_FOREACH(id, c->syscall_archs) {
1,189✔
1890
                const char *name;
52✔
1891

1892
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
52✔
1893
                if (!name)
52✔
1894
                        continue;
×
1895

1896
                if (strv_extend(&l, name) < 0)
52✔
1897
                        return NULL;
×
1898
        }
1899

1900
        strv_sort(l);
1,137✔
1901
#endif
1902

1903
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,137✔
1904
}
1905

1906
char** exec_context_get_syscall_log(const ExecContext *c) {
1,137✔
1907
        _cleanup_strv_free_ char **l = NULL;
1,137✔
1908

1909
        assert(c);
1,137✔
1910

1911
#if HAVE_SECCOMP
1912
        void *id, *val;
1,137✔
1913
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,137✔
1914
                char *name = NULL;
×
1915

1916
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1917
                if (!name)
×
1918
                        continue;
×
1919

1920
                if (strv_consume(&l, name) < 0)
×
1921
                        return NULL;
×
1922
        }
1923

1924
        strv_sort(l);
1,137✔
1925
#endif
1926

1927
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,137✔
1928
}
1929

1930
char** exec_context_get_address_families(const ExecContext *c) {
1,137✔
1931
        _cleanup_strv_free_ char **l = NULL;
1,137✔
1932
        void *af;
1,137✔
1933

1934
        assert(c);
1,137✔
1935

1936
        SET_FOREACH(af, c->address_families) {
1,291✔
1937
                const char *name;
154✔
1938

1939
                name = af_to_name(PTR_TO_INT(af));
154✔
1940
                if (!name)
154✔
1941
                        continue;
×
1942

1943
                if (strv_extend(&l, name) < 0)
154✔
1944
                        return NULL;
×
1945
        }
1946

1947
        strv_sort(l);
1,137✔
1948

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

1952
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1,137✔
1953
        _cleanup_strv_free_ char **l = NULL;
1,137✔
1954

1955
        assert(c);
1,137✔
1956

1957
#if HAVE_LIBBPF
1958
        l = set_get_strv(c->restrict_filesystems);
1,137✔
1959
        if (!l)
1,137✔
1960
                return NULL;
1961

1962
        strv_sort(l);
1,137✔
1963
#endif
1964

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

1968
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
7,355✔
1969
        assert(s);
7,355✔
1970

1971
        *s = (ExecStatus) {
7,355✔
1972
                .pid = pid,
1973
        };
1974

1975
        if (ts)
7,355✔
1976
                s->start_timestamp = *ts;
7,355✔
1977
        else
1978
                dual_timestamp_now(&s->start_timestamp);
×
1979
}
7,355✔
1980

1981
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
3,558✔
1982
        assert(s);
3,558✔
1983

1984
        if (s->pid != pid)
3,558✔
1985
                *s = (ExecStatus) {
4✔
1986
                        .pid = pid,
1987
                };
1988

1989
        dual_timestamp_now(&s->exit_timestamp);
3,558✔
1990

1991
        s->code = code;
3,558✔
1992
        s->status = status;
3,558✔
1993

1994
        if (context && context->utmp_id)
3,558✔
1995
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
1996
}
3,558✔
1997

1998
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
13,388✔
1999
        assert(s);
13,388✔
2000
        assert(ucred);
13,388✔
2001
        assert(ts);
13,388✔
2002

2003
        if (ucred->pid != s->pid)
13,388✔
2004
                *s = (ExecStatus) {
7✔
2005
                        .pid = ucred->pid,
2006
                };
2007

2008
        s->handoff_timestamp = *ts;
13,388✔
2009
}
13,388✔
2010

2011
void exec_status_reset(ExecStatus *s) {
22,601✔
2012
        assert(s);
22,601✔
2013

2014
        *s = (ExecStatus) {};
22,601✔
2015
}
22,601✔
2016

2017
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
94✔
2018
        assert(s);
94✔
2019
        assert(f);
94✔
2020

2021
        if (s->pid <= 0)
94✔
2022
                return;
2023

2024
        prefix = strempty(prefix);
10✔
2025

2026
        fprintf(f,
10✔
2027
                "%sPID: "PID_FMT"\n",
2028
                prefix, s->pid);
2029

2030
        if (dual_timestamp_is_set(&s->start_timestamp))
10✔
2031
                fprintf(f,
10✔
2032
                        "%sStart Timestamp: %s\n",
2033
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
10✔
2034

2035
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
10✔
2036
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
10✔
2037
                fprintf(f,
10✔
2038
                        "%sHandoff Timestamp: %s since start\n",
2039
                        prefix,
2040
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
20✔
2041
        else
2042
                fprintf(f,
×
2043
                        "%sHandoff Timestamp: %s\n",
2044
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
×
2045

2046
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2047

2048
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
×
2049
                        fprintf(f,
×
2050
                                "%sExit Timestamp: %s since handoff\n",
2051
                                prefix,
2052
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
×
2053
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2054
                        fprintf(f,
×
2055
                                "%sExit Timestamp: %s since start\n",
2056
                                prefix,
2057
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2058
                else
2059
                        fprintf(f,
×
2060
                                "%sExit Timestamp: %s\n",
2061
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2062

2063
                fprintf(f,
×
2064
                        "%sExit Code: %s\n"
2065
                        "%sExit Status: %i\n",
2066
                        prefix, sigchld_code_to_string(s->code),
×
2067
                        prefix, s->status);
×
2068
        }
2069
}
2070

2071
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
94✔
2072
        _cleanup_free_ char *cmd = NULL;
188✔
2073
        const char *prefix2;
94✔
2074

2075
        assert(c);
94✔
2076
        assert(f);
94✔
2077

2078
        prefix = strempty(prefix);
94✔
2079
        prefix2 = strjoina(prefix, "\t");
470✔
2080

2081
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
94✔
2082

2083
        fprintf(f,
94✔
2084
                "%sCommand Line: %s\n",
2085
                prefix, strnull(cmd));
2086

2087
        exec_status_dump(&c->exec_status, f, prefix2);
94✔
2088
}
94✔
2089

2090
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
91✔
2091
        assert(f);
91✔
2092

2093
        prefix = strempty(prefix);
91✔
2094

2095
        LIST_FOREACH(command, i, c)
185✔
2096
                exec_command_dump(i, f, prefix);
94✔
2097
}
91✔
2098

2099
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
20,904✔
2100
        ExecCommand *end;
20,904✔
2101

2102
        assert(l);
20,904✔
2103
        assert(e);
20,904✔
2104

2105
        if (*l) {
20,904✔
2106
                /* It's kind of important, that we keep the order here */
2107
                end = LIST_FIND_TAIL(command, *l);
369✔
2108
                LIST_INSERT_AFTER(command, *l, end, e);
132✔
2109
        } else
2110
                *l = e;
20,772✔
2111
}
20,904✔
2112

2113
int exec_command_set(ExecCommand *c, const char *path, ...) {
570✔
2114
        va_list ap;
570✔
2115
        char **l, *p;
570✔
2116

2117
        assert(c);
570✔
2118
        assert(path);
570✔
2119

2120
        va_start(ap, path);
570✔
2121
        l = strv_new_ap(path, ap);
570✔
2122
        va_end(ap);
570✔
2123

2124
        if (!l)
570✔
2125
                return -ENOMEM;
570✔
2126

2127
        p = strdup(path);
570✔
2128
        if (!p) {
570✔
2129
                strv_free(l);
×
2130
                return -ENOMEM;
×
2131
        }
2132

2133
        free_and_replace(c->path, p);
570✔
2134

2135
        return strv_free_and_replace(c->argv, l);
570✔
2136
}
2137

2138
int exec_command_append(ExecCommand *c, const char *path, ...) {
991✔
2139
        char **l;
991✔
2140
        va_list ap;
991✔
2141
        int r;
991✔
2142

2143
        assert(c);
991✔
2144
        assert(path);
991✔
2145

2146
        va_start(ap, path);
991✔
2147
        l = strv_new_ap(path, ap);
991✔
2148
        va_end(ap);
991✔
2149

2150
        if (!l)
991✔
2151
                return -ENOMEM;
991✔
2152

2153
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
991✔
2154
        if (r < 0)
991✔
2155
                return r;
×
2156

2157
        return 0;
2158
}
2159

2160
static char *destroy_tree(char *path) {
421✔
2161
        if (!path)
421✔
2162
                return NULL;
2163

2164
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
186✔
2165
                log_debug("Spawning process to nuke '%s'", path);
186✔
2166

2167
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
186✔
2168
        }
2169

2170
        return mfree(path);
186✔
2171
}
2172

2173
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
128,986✔
2174
        assert(rt);
128,986✔
2175

2176
        if (rt->manager)
128,986✔
2177
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
220✔
2178

2179
        rt->id = mfree(rt->id);
128,986✔
2180
        rt->tmp_dir = mfree(rt->tmp_dir);
128,986✔
2181
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
128,986✔
2182
        safe_close_pair(rt->netns_storage_socket);
128,986✔
2183
        safe_close_pair(rt->ipcns_storage_socket);
128,986✔
2184
}
128,986✔
2185

2186
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
128,958✔
2187
        if (!rt)
128,958✔
2188
                return NULL;
2189

2190
        exec_shared_runtime_done(rt);
128,958✔
2191
        return mfree(rt);
128,958✔
2192
}
2193

2194
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
221✔
2195
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
131,944✔
2196

2197
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
101✔
2198
        if (!rt)
101✔
2199
                return NULL;
2200

2201
        assert(rt->n_ref > 0);
100✔
2202
        rt->n_ref--;
100✔
2203

2204
        if (rt->n_ref > 0)
100✔
2205
                return NULL;
2206

2207
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
100✔
2208
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
100✔
2209

2210
        return exec_shared_runtime_free(rt);
100✔
2211
}
2212

2213
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
128,958✔
2214
        _cleanup_free_ char *id_copy = NULL;
257,916✔
2215
        ExecSharedRuntime *n;
128,958✔
2216

2217
        assert(ret);
128,958✔
2218

2219
        id_copy = strdup(id);
128,958✔
2220
        if (!id_copy)
128,958✔
2221
                return -ENOMEM;
2222

2223
        n = new(ExecSharedRuntime, 1);
128,958✔
2224
        if (!n)
128,958✔
2225
                return -ENOMEM;
2226

2227
        *n = (ExecSharedRuntime) {
128,958✔
2228
                .id = TAKE_PTR(id_copy),
128,958✔
2229
                .netns_storage_socket = EBADF_PAIR,
2230
                .ipcns_storage_socket = EBADF_PAIR,
2231
        };
2232

2233
        *ret = n;
128,958✔
2234
        return 0;
128,958✔
2235
}
2236

2237
static int exec_shared_runtime_add(
220✔
2238
                Manager *m,
2239
                const char *id,
2240
                char **tmp_dir,
2241
                char **var_tmp_dir,
2242
                int netns_storage_socket[2],
2243
                int ipcns_storage_socket[2],
2244
                ExecSharedRuntime **ret) {
2245

2246
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
220✔
2247
        int r;
220✔
2248

2249
        assert(m);
220✔
2250
        assert(id);
220✔
2251

2252
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2253

2254
        r = exec_shared_runtime_allocate(&rt, id);
220✔
2255
        if (r < 0)
220✔
2256
                return r;
2257

2258
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
220✔
2259
        if (r < 0)
220✔
2260
                return r;
2261

2262
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
220✔
2263
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
220✔
2264
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
220✔
2265

2266
        if (netns_storage_socket) {
220✔
2267
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
220✔
2268
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
220✔
2269
        }
2270

2271
        if (ipcns_storage_socket) {
220✔
2272
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
220✔
2273
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
220✔
2274
        }
2275

2276
        rt->manager = m;
220✔
2277

2278
        if (ret)
220✔
2279
                *ret = rt;
121✔
2280
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2281
        TAKE_PTR(rt);
220✔
2282
        return 0;
220✔
2283
}
2284

2285
static int exec_shared_runtime_make(
7,616✔
2286
                Manager *m,
2287
                const ExecContext *c,
2288
                const char *id,
2289
                ExecSharedRuntime **ret) {
2290

2291
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
7,616✔
2292
        _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
15,232✔
2293
        int r;
7,616✔
2294

2295
        assert(m);
7,616✔
2296
        assert(c);
7,616✔
2297
        assert(id);
7,616✔
2298

2299
        /* It is not necessary to create ExecSharedRuntime object. */
2300
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
7,616✔
2301
                *ret = NULL;
7,495✔
2302
                return 0;
7,495✔
2303
        }
2304

2305
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
235✔
2306
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
114✔
2307
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
2308
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2309
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
114✔
2310
                if (r < 0)
114✔
2311
                        return r;
2312
        }
2313

2314
        if (exec_needs_network_namespace(c))
121✔
2315
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
7✔
2316
                        return -errno;
×
2317

2318
        if (exec_needs_ipc_namespace(c))
121✔
2319
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
1✔
2320
                        return -errno;
×
2321

2322
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
121✔
2323
        if (r < 0)
121✔
2324
                return r;
×
2325

2326
        return 1;
2327
}
2328

2329
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
7,715✔
2330
        ExecSharedRuntime *rt;
7,715✔
2331
        int r;
7,715✔
2332

2333
        assert(m);
7,715✔
2334
        assert(id);
7,715✔
2335
        assert(ret);
7,715✔
2336

2337
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
7,715✔
2338
        if (rt)
7,715✔
2339
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2340
                goto ref;
99✔
2341

2342
        if (!create) {
7,616✔
2343
                *ret = NULL;
×
2344
                return 0;
×
2345
        }
2346

2347
        /* If not found, then create a new object. */
2348
        r = exec_shared_runtime_make(m, c, id, &rt);
7,616✔
2349
        if (r < 0)
7,616✔
2350
                return r;
2351
        if (r == 0) {
7,616✔
2352
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2353
                *ret = NULL;
7,495✔
2354
                return 0;
7,495✔
2355
        }
2356

2357
ref:
121✔
2358
        /* increment reference counter. */
2359
        rt->n_ref++;
220✔
2360
        *ret = rt;
220✔
2361
        return 1;
220✔
2362
}
2363

2364
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
123✔
2365
        ExecSharedRuntime *rt;
123✔
2366

2367
        assert(m);
123✔
2368
        assert(f);
123✔
2369
        assert(fds);
123✔
2370

2371
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
243✔
2372
                fprintf(f, "exec-runtime=%s", rt->id);
120✔
2373

2374
                if (rt->tmp_dir)
120✔
2375
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
120✔
2376

2377
                if (rt->var_tmp_dir)
120✔
2378
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
120✔
2379

2380
                if (rt->netns_storage_socket[0] >= 0) {
120✔
2381
                        int copy;
2✔
2382

2383
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2✔
2384
                        if (copy < 0)
2✔
2385
                                return copy;
×
2386

2387
                        fprintf(f, " netns-socket-0=%i", copy);
2✔
2388
                }
2389

2390
                if (rt->netns_storage_socket[1] >= 0) {
120✔
2391
                        int copy;
2✔
2392

2393
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2✔
2394
                        if (copy < 0)
2✔
2395
                                return copy;
2396

2397
                        fprintf(f, " netns-socket-1=%i", copy);
2✔
2398
                }
2399

2400
                if (rt->ipcns_storage_socket[0] >= 0) {
120✔
2401
                        int copy;
×
2402

2403
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2404
                        if (copy < 0)
×
2405
                                return copy;
2406

2407
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2408
                }
2409

2410
                if (rt->ipcns_storage_socket[1] >= 0) {
120✔
2411
                        int copy;
×
2412

2413
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2414
                        if (copy < 0)
×
2415
                                return copy;
2416

2417
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2418
                }
2419

2420
                fputc('\n', f);
120✔
2421
        }
2422

2423
        return 0;
123✔
2424
}
2425

2426
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
131,724✔
2427
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
131,724✔
2428
        ExecSharedRuntime *rt = NULL;
131,724✔
2429
        int r;
131,724✔
2430

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

2436
        assert(u);
131,724✔
2437
        assert(key);
131,724✔
2438
        assert(value);
131,724✔
2439

2440
        /* Manager manages ExecSharedRuntime objects by the unit id.
2441
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2442
        if (isempty(u->id)) {
131,724✔
2443
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
2444
                return 0;
×
2445
        }
2446

2447
        if (u->manager) {
131,724✔
2448
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
131,724✔
2449
                        return log_oom();
×
2450

2451
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
131,724✔
2452
        }
2453
        if (!rt) {
131,724✔
2454
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
128,738✔
2455
                        return log_oom();
×
2456

2457
                rt = rt_create;
128,738✔
2458
        }
2459

2460
        if (streq(key, "tmp-dir")) {
131,724✔
2461
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2462
                        return -ENOMEM;
2463

2464
        } else if (streq(key, "var-tmp-dir")) {
131,724✔
2465
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2466
                        return -ENOMEM;
2467

2468
        } else if (streq(key, "netns-socket-0")) {
131,724✔
2469

2470
                safe_close(rt->netns_storage_socket[0]);
×
2471
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2472
                if (rt->netns_storage_socket[0] < 0)
×
2473
                        return 0;
2474

2475
        } else if (streq(key, "netns-socket-1")) {
131,724✔
2476

2477
                safe_close(rt->netns_storage_socket[1]);
×
2478
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2479
                if (rt->netns_storage_socket[1] < 0)
×
2480
                        return 0;
2481
        } else
2482
                return 0;
2483

2484
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
2485
        if (rt_create && u->manager) {
×
2486
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
2487
                if (r < 0) {
×
2488
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
2489
                        return 0;
×
2490
                }
2491

2492
                rt_create->manager = u->manager;
×
2493

2494
                /* Avoid cleanup */
2495
                TAKE_PTR(rt_create);
×
2496
        }
2497

2498
        return 1;
2499
}
2500

2501
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
99✔
2502
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
99✔
2503
        char *id = NULL;
99✔
2504
        int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
99✔
2505
        const char *p, *v = ASSERT_PTR(value);
99✔
2506
        size_t n;
99✔
2507

2508
        assert(m);
99✔
2509
        assert(fds);
99✔
2510

2511
        n = strcspn(v, " ");
99✔
2512
        id = strndupa_safe(v, n);
99✔
2513
        if (v[n] != ' ')
99✔
2514
                goto finalize;
×
2515
        p = v + n + 1;
99✔
2516

2517
        v = startswith(p, "tmp-dir=");
99✔
2518
        if (v) {
99✔
2519
                n = strcspn(v, " ");
99✔
2520
                tmp_dir = strndup(v, n);
99✔
2521
                if (!tmp_dir)
99✔
2522
                        return log_oom();
×
2523
                if (v[n] != ' ')
99✔
2524
                        goto finalize;
×
2525
                p = v + n + 1;
99✔
2526
        }
2527

2528
        v = startswith(p, "var-tmp-dir=");
99✔
2529
        if (v) {
99✔
2530
                n = strcspn(v, " ");
99✔
2531
                var_tmp_dir = strndup(v, n);
99✔
2532
                if (!var_tmp_dir)
99✔
2533
                        return log_oom();
×
2534
                if (v[n] != ' ')
99✔
2535
                        goto finalize;
98✔
2536
                p = v + n + 1;
1✔
2537
        }
2538

2539
        v = startswith(p, "netns-socket-0=");
1✔
2540
        if (v) {
1✔
2541
                char *buf;
1✔
2542

2543
                n = strcspn(v, " ");
1✔
2544
                buf = strndupa_safe(v, n);
1✔
2545

2546
                netns_fdpair[0] = deserialize_fd(fds, buf);
1✔
2547
                if (netns_fdpair[0] < 0)
1✔
2548
                        return netns_fdpair[0];
2549
                if (v[n] != ' ')
1✔
2550
                        goto finalize;
×
2551
                p = v + n + 1;
1✔
2552
        }
2553

2554
        v = startswith(p, "netns-socket-1=");
1✔
2555
        if (v) {
1✔
2556
                char *buf;
1✔
2557

2558
                n = strcspn(v, " ");
1✔
2559
                buf = strndupa_safe(v, n);
1✔
2560

2561
                netns_fdpair[1] = deserialize_fd(fds, buf);
1✔
2562
                if (netns_fdpair[1] < 0)
1✔
2563
                        return netns_fdpair[1];
2564
                if (v[n] != ' ')
1✔
2565
                        goto finalize;
1✔
2566
                p = v + n + 1;
×
2567
        }
2568

2569
        v = startswith(p, "ipcns-socket-0=");
×
2570
        if (v) {
×
2571
                char *buf;
×
2572

2573
                n = strcspn(v, " ");
×
2574
                buf = strndupa_safe(v, n);
×
2575

2576
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2577
                if (ipcns_fdpair[0] < 0)
×
2578
                        return ipcns_fdpair[0];
2579
                if (v[n] != ' ')
×
2580
                        goto finalize;
×
2581
                p = v + n + 1;
×
2582
        }
2583

2584
        v = startswith(p, "ipcns-socket-1=");
×
2585
        if (v) {
×
2586
                char *buf;
×
2587

2588
                n = strcspn(v, " ");
×
2589
                buf = strndupa_safe(v, n);
×
2590

2591
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2592
                if (ipcns_fdpair[1] < 0)
×
2593
                        return ipcns_fdpair[1];
2594
        }
2595

2596
finalize:
×
2597
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
99✔
2598
        if (r < 0)
99✔
2599
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2600
        return 0;
2601
}
2602

2603
void exec_shared_runtime_vacuum(Manager *m) {
1,276✔
2604
        ExecSharedRuntime *rt;
1,276✔
2605

2606
        assert(m);
1,276✔
2607

2608
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2609

2610
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,375✔
2611
                if (rt->n_ref > 0)
99✔
2612
                        continue;
99✔
2613

2614
                (void) exec_shared_runtime_free(rt);
×
2615
        }
2616
}
1,276✔
2617

2618
int exec_runtime_make(
7,715✔
2619
                const Unit *unit,
2620
                const ExecContext *context,
2621
                ExecSharedRuntime *shared,
2622
                DynamicCreds *creds,
2623
                ExecRuntime **ret) {
2624
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
7,715✔
2625
        _cleanup_free_ char *ephemeral = NULL;
7,715✔
2626
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
7,715✔
2627
        int r;
7,715✔
2628

2629
        assert(unit);
7,715✔
2630
        assert(context);
7,715✔
2631
        assert(ret);
7,715✔
2632

2633
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
7,715✔
2634
                *ret = NULL;
7,494✔
2635
                return 0;
7,494✔
2636
        }
2637

2638
        if (exec_needs_ephemeral(context)) {
221✔
2639
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2640
                if (r < 0)
×
2641
                        return r;
2642

2643
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2644
                if (r < 0)
×
2645
                        return r;
2646

2647
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2648
                        return -errno;
×
2649
        }
2650

2651
        rt = new(ExecRuntime, 1);
221✔
2652
        if (!rt)
221✔
2653
                return -ENOMEM;
2654

2655
        *rt = (ExecRuntime) {
221✔
2656
                .shared = shared,
2657
                .dynamic_creds = creds,
2658
                .ephemeral_copy = TAKE_PTR(ephemeral),
221✔
2659
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
221✔
2660
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
221✔
2661
        };
2662

2663
        *ret = TAKE_PTR(rt);
221✔
2664
        return 1;
221✔
2665
}
2666

2667
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
49,923✔
2668
        if (!rt)
49,923✔
2669
                return NULL;
2670

2671
        exec_shared_runtime_unref(rt->shared);
221✔
2672
        dynamic_creds_unref(rt->dynamic_creds);
221✔
2673

2674
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
221✔
2675

2676
        safe_close_pair(rt->ephemeral_storage_socket);
221✔
2677
        return mfree(rt);
221✔
2678
}
2679

2680
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
5,825✔
2681
        if (!rt)
5,825✔
2682
                return NULL;
2683

2684
        rt->shared = exec_shared_runtime_destroy(rt->shared);
101✔
2685
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
101✔
2686
        return exec_runtime_free(rt);
101✔
2687
}
2688

2689
void exec_runtime_clear(ExecRuntime *rt) {
28✔
2690
        if (!rt)
28✔
2691
                return;
2692

2693
        safe_close_pair(rt->ephemeral_storage_socket);
28✔
2694
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
28✔
2695
}
2696

2697
void exec_params_shallow_clear(ExecParameters *p) {
3,849✔
2698
        if (!p)
3,849✔
2699
                return;
2700

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

2704
        p->environment = strv_free(p->environment);
3,849✔
2705
        p->fd_names = strv_free(p->fd_names);
3,849✔
2706
        p->files_env = strv_free(p->files_env);
3,849✔
2707
        p->fds = mfree(p->fds);
3,849✔
2708
        p->exec_fd = safe_close(p->exec_fd);
3,849✔
2709
        p->user_lookup_fd = -EBADF;
3,849✔
2710
        p->bpf_restrict_fs_map_fd = -EBADF;
3,849✔
2711
        p->unit_id = mfree(p->unit_id);
3,849✔
2712
        p->invocation_id = SD_ID128_NULL;
3,849✔
2713
        p->invocation_id_string[0] = '\0';
3,849✔
2714
        p->confirm_spawn = mfree(p->confirm_spawn);
3,849✔
2715
}
2716

2717
void exec_params_deep_clear(ExecParameters *p) {
28✔
2718
        if (!p)
28✔
2719
                return;
2720

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

2725
        close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
28✔
2726

2727
        p->cgroup_path = mfree(p->cgroup_path);
28✔
2728

2729
        if (p->prefix) {
28✔
2730
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
28✔
2731
                p->prefix = mfree(p->prefix);
28✔
2732
        }
2733

2734
        p->received_credentials_directory = mfree(p->received_credentials_directory);
28✔
2735
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
28✔
2736

2737
        if (p->idle_pipe) {
28✔
UNCOV
2738
                close_many_and_free(p->idle_pipe, 4);
×
UNCOV
2739
                p->idle_pipe = NULL;
×
2740
        }
2741

2742
        p->stdin_fd = safe_close(p->stdin_fd);
28✔
2743
        p->stdout_fd = safe_close(p->stdout_fd);
28✔
2744
        p->stderr_fd = safe_close(p->stderr_fd);
28✔
2745

2746
        p->notify_socket = mfree(p->notify_socket);
28✔
2747

2748
        open_file_free_many(&p->open_files);
28✔
2749

2750
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
28✔
2751

2752
        exec_params_shallow_clear(p);
28✔
2753
}
2754

2755
void exec_directory_done(ExecDirectory *d) {
249,250✔
2756
        if (!d)
249,250✔
2757
                return;
2758

2759
        FOREACH_ARRAY(i, d->items, d->n_items) {
251,843✔
2760
                free(i->path);
2,593✔
2761
                strv_free(i->symlinks);
2,593✔
2762
        }
2763

2764
        d->items = mfree(d->items);
249,250✔
2765
        d->n_items = 0;
249,250✔
2766
        d->mode = 0755;
249,250✔
2767
}
2768

2769
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
6,610✔
2770
        assert(d);
6,610✔
2771
        assert(path);
6,610✔
2772

2773
        FOREACH_ARRAY(i, d->items, d->n_items)
9,434✔
2774
                if (path_equal(i->path, path))
2,839✔
2775
                        return i;
2776

2777
        return NULL;
2778
}
2779

2780
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
6,610✔
2781
        _cleanup_strv_free_ char **s = NULL;
×
2782
        _cleanup_free_ char *p = NULL;
6,610✔
2783
        ExecDirectoryItem *existing;
6,610✔
2784
        int r;
6,610✔
2785

2786
        assert(d);
6,610✔
2787
        assert(path);
6,610✔
2788

2789
        existing = exec_directory_find(d, path);
6,610✔
2790
        if (existing) {
6,610✔
2791
                r = strv_extend(&existing->symlinks, symlink);
15✔
2792
                if (r < 0)
15✔
2793
                        return r;
2794

2795
                existing->flags |= flags;
15✔
2796

2797
                return 0; /* existing item is updated */
15✔
2798
        }
2799

2800
        p = strdup(path);
6,595✔
2801
        if (!p)
6,595✔
2802
                return -ENOMEM;
2803

2804
        if (symlink) {
6,595✔
2805
                s = strv_new(symlink);
6✔
2806
                if (!s)
6✔
2807
                        return -ENOMEM;
2808
        }
2809

2810
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
6,595✔
2811
                return -ENOMEM;
2812

2813
        d->items[d->n_items++] = (ExecDirectoryItem) {
6,595✔
2814
                .path = TAKE_PTR(p),
6,595✔
2815
                .symlinks = TAKE_PTR(s),
6,595✔
2816
                .flags = flags,
2817
        };
2818

2819
        return 1; /* new item is added */
6,595✔
2820
}
2821

2822
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
1,226✔
2823
        assert(a);
1,226✔
2824
        assert(b);
1,226✔
2825

2826
        return path_compare(a->path, b->path);
1,226✔
2827
}
2828

2829
void exec_directory_sort(ExecDirectory *d) {
142,914✔
2830
        assert(d);
142,914✔
2831

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

2837
        if (d->n_items <= 1)
142,914✔
2838
                return;
2839

2840
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
208✔
2841

2842
        for (size_t i = 1; i < d->n_items; i++)
935✔
2843
                for (size_t j = 0; j < i; j++)
2,452✔
2844
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,740✔
2845
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
2846
                                break;
15✔
2847
                        }
2848
}
2849

2850
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
2851
        ExecDirectoryType t;
×
2852

2853
        assert(s);
×
2854

2855
        if (streq(s, "all"))
×
2856
                return EXEC_CLEAN_ALL;
2857
        if (streq(s, "fdstore"))
×
2858
                return EXEC_CLEAN_FDSTORE;
2859

2860
        t = exec_resource_type_from_string(s);
×
2861
        if (t < 0)
×
2862
                return (ExecCleanMask) t;
2863

2864
        return 1U << t;
×
2865
}
2866

2867
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
2868
        [EXEC_INPUT_NULL]      = "null",
2869
        [EXEC_INPUT_TTY]       = "tty",
2870
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
2871
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
2872
        [EXEC_INPUT_SOCKET]    = "socket",
2873
        [EXEC_INPUT_NAMED_FD]  = "fd",
2874
        [EXEC_INPUT_DATA]      = "data",
2875
        [EXEC_INPUT_FILE]      = "file",
2876
};
2877

2878
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
19,441✔
2879

2880
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
2881
        [EXEC_OUTPUT_INHERIT]             = "inherit",
2882
        [EXEC_OUTPUT_NULL]                = "null",
2883
        [EXEC_OUTPUT_TTY]                 = "tty",
2884
        [EXEC_OUTPUT_KMSG]                = "kmsg",
2885
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
2886
        [EXEC_OUTPUT_JOURNAL]             = "journal",
2887
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
2888
        [EXEC_OUTPUT_SOCKET]              = "socket",
2889
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
2890
        [EXEC_OUTPUT_FILE]                = "file",
2891
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
2892
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
2893
};
2894

2895
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
39,033✔
2896

2897
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
2898
        [EXEC_UTMP_INIT]  = "init",
2899
        [EXEC_UTMP_LOGIN] = "login",
2900
        [EXEC_UTMP_USER]  = "user",
2901
};
2902

2903
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
18,489✔
2904

2905
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
2906
        [EXEC_PRESERVE_NO]      = "no",
2907
        [EXEC_PRESERVE_YES]     = "yes",
2908
        [EXEC_PRESERVE_RESTART] = "restart",
2909
};
2910

2911
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
20,496✔
2912

2913
/* This table maps ExecDirectoryType to the setting it is configured with in the unit */
2914
static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2915
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectory",
2916
        [EXEC_DIRECTORY_STATE]         = "StateDirectory",
2917
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectory",
2918
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectory",
2919
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectory",
2920
};
2921

2922
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
174,246✔
2923

2924
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
2925
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2926
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
2927
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
2928
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
2929
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
2930
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
2931
};
2932

2933
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
2934

2935
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2936
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
2937
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
2938
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
2939
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
2940
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
2941
};
2942

2943
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
2944

2945
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
2946
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
2947
 * directories, specifically .timer units with their timestamp touch file. */
2948
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2949
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
2950
        [EXEC_DIRECTORY_STATE]         = "state",
2951
        [EXEC_DIRECTORY_CACHE]         = "cache",
2952
        [EXEC_DIRECTORY_LOGS]          = "logs",
2953
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
2954
};
2955

2956
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
337✔
2957

2958
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
2959
        [EXEC_KEYRING_INHERIT] = "inherit",
2960
        [EXEC_KEYRING_PRIVATE] = "private",
2961
        [EXEC_KEYRING_SHARED]  = "shared",
2962
};
2963

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