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

systemd / systemd / 14424536636

13 Apr 2025 12:15AM UTC coverage: 72.03% (+0.02%) from 72.01%
14424536636

push

github

web-flow
locale-util,kbd-util: several cleanups (#37090)

49 of 51 new or added lines in 5 files covered. (96.08%)

2391 existing lines in 44 files now uncovered.

297332 of 412788 relevant lines covered (72.03%)

683891.99 hits per line

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

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

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

17
#include "sd-messages.h"
18

19
#include "af-list.h"
20
#include "alloc-util.h"
21
#include "async.h"
22
#include "bitfield.h"
23
#include "cap-list.h"
24
#include "capability-util.h"
25
#include "cgroup-setup.h"
26
#include "constants.h"
27
#include "cpu-set-util.h"
28
#include "env-file.h"
29
#include "env-util.h"
30
#include "errno-list.h"
31
#include "escape.h"
32
#include "exec-credential.h"
33
#include "execute.h"
34
#include "execute-serialize.h"
35
#include "exit-status.h"
36
#include "fd-util.h"
37
#include "fileio.h"
38
#include "format-util.h"
39
#include "glob-util.h"
40
#include "hexdecoct.h"
41
#include "io-util.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 "mkdir-label.h"
51
#include "namespace.h"
52
#include "osc-context.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) {
33,517✔
77
        return IN_SET(i,
33,517✔
78
                      EXEC_INPUT_TTY,
79
                      EXEC_INPUT_TTY_FORCE,
80
                      EXEC_INPUT_TTY_FAIL);
81
}
82

83
static bool is_terminal_output(ExecOutput o) {
65,979✔
84
        return IN_SET(o,
65,979✔
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) {
14,596✔
91
        assert(context);
14,596✔
92

93
        if (context->stdio_as_fds)
14,596✔
94
                return NULL;
95

96
        if (context->tty_path)
13,963✔
97
                return context->tty_path;
671✔
98

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

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

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

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

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

118
        if (!tty_path)
1,066✔
119
                tty_path = exec_context_tty_path(context);
410✔
120

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

125
        /* Fill in data from kernel command line if anything is unspecified */
126
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
1,066✔
127
                (void) proc_cmdline_tty_size(
1,034✔
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, then let's query dimensions from
133
         * the ANSI terminal driver. Note that we will not bother with this in case terminal reset via ansi
134
         * sequences is not enabled, as the DSR logic relies on ANSI sequences after all, and if we shall not
135
         * use those during initialization we need to skip it. */
136
        if (rows == UINT_MAX && cols == UINT_MAX &&
1,269✔
137
            exec_context_shall_ansi_seq_reset(context) &&
378✔
138
            isatty_safe(input_fd)) {
175✔
139
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
175✔
140
                if (r < 0)
175✔
141
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
175✔
142
        }
143

144
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
1,066✔
145
}
146

147
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p, sd_id128_t invocation_id) {
12,951✔
148
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
25,902✔
149
        int fd, r;
12,951✔
150

151
        assert(context);
12,951✔
152

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

158
        const char *path = exec_context_tty_path(context);
12,951✔
159

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

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

181
        if (context->tty_reset)
672✔
182
                (void) terminal_reset_defensive(
169✔
183
                                fd,
184
                                TERMINAL_RESET_SWITCH_TO_TEXT |
185
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
166✔
186

187
        r = exec_context_apply_tty_size(context, fd, fd, path);
672✔
188
        if (r < 0)
672✔
189
                log_debug_errno(r, "Failed to configure TTY dimensions, ignoring: %m");
×
190

191
        if (!sd_id128_is_null(invocation_id)) {
1,295✔
192
                sd_id128_t context_id;
49✔
193

194
                r = osc_context_id_from_invocation_id(invocation_id, &context_id);
49✔
195
                if (r < 0)
49✔
196
                        log_debug_errno(r, "Failed to derive context ID from invocation ID, ignoring: %m");
49✔
197
                else {
198
                        _cleanup_free_ char *seq = NULL;
49✔
199

200
                        r = osc_context_close(context_id, &seq);
49✔
201
                        if (r < 0)
49✔
202
                                log_debug_errno(r, "Failed to acquire OSC close sequence, ignoring: %m");
×
203
                        else
204
                                (void) loop_write(fd, seq, SIZE_MAX);
49✔
205
                }
206
        }
207

208
        if (context->tty_vhangup)
672✔
209
                (void) terminal_vhangup_fd(fd);
161✔
210

211
        /* We don't need the fd anymore now, and it potentially points to a hungup TTY anyway, let's close it
212
         * hence. */
213
        _fd = safe_close(_fd);
672✔
214

215
        if (context->tty_vt_disallocate && path)
672✔
216
                (void) vt_disallocate(path);
87✔
217
}
218

219
bool exec_needs_network_namespace(const ExecContext *context) {
55,750✔
220
        assert(context);
55,750✔
221

222
        return context->private_network || context->network_namespace_path;
55,750✔
223
}
224

225
static bool exec_needs_ephemeral(const ExecContext *context) {
6,029✔
226
        return (context->root_image || context->root_directory) && context->root_ephemeral;
6,029✔
227
}
228

229
bool exec_needs_ipc_namespace(const ExecContext *context) {
51,692✔
230
        assert(context);
51,692✔
231

232
        return context->private_ipc || context->ipc_namespace_path;
51,692✔
233
}
234

235
static bool needs_cgroup_namespace(ProtectControlGroups i) {
90,432✔
236
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
90,432✔
237
}
238

239
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
60,357✔
240
        assert(context);
60,357✔
241

242
        /* If cgroup namespace is configured via ProtectControlGroups=private or strict but we can't actually
243
         * use cgroup namespace, we ignore the setting and do not unshare the namespace.
244
         * ProtectControlGroups=private and strict get downgraded to no and yes respectively. This ensures
245
         * that strict always gets a read-only mount of /sys/fs/cgroup/. */
246
        if (needs_cgroup_namespace(context->protect_control_groups) && !ns_type_supported(NAMESPACE_CGROUP)) {
60,357✔
247
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_PRIVATE)
×
248
                        return PROTECT_CONTROL_GROUPS_NO;
249
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_STRICT)
×
250
                        return PROTECT_CONTROL_GROUPS_YES;
251
        }
252
        return context->protect_control_groups;
60,357✔
253
}
254

255
bool exec_needs_cgroup_namespace(const ExecContext *context) {
30,075✔
256
        assert(context);
30,075✔
257

258
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
30,075✔
259
}
260

261
bool exec_needs_cgroup_mount(const ExecContext *context) {
26,264✔
262
        assert(context);
26,264✔
263

264
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
26,264✔
265
}
266

267
bool exec_is_cgroup_mount_read_only(const ExecContext *context) {
2,009✔
268
        assert(context);
2,009✔
269

270
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
2,009✔
271
}
272

273
bool exec_needs_pid_namespace(const ExecContext *context) {
72,156✔
274
        assert(context);
72,156✔
275

276
        return context->private_pids != PRIVATE_PIDS_NO && ns_type_supported(NAMESPACE_PID);
72,156✔
277
}
278

279
bool exec_needs_mount_namespace(
31,505✔
280
                const ExecContext *context,
281
                const ExecParameters *params,
282
                const ExecRuntime *runtime) {
283

284
        assert(context);
31,505✔
285

286
        if (context->root_image)
31,505✔
287
                return true;
288

289
        if (!strv_isempty(context->read_write_paths) ||
31,481✔
290
            !strv_isempty(context->read_only_paths) ||
29,387✔
291
            !strv_isempty(context->inaccessible_paths) ||
29,380✔
292
            !strv_isempty(context->exec_paths) ||
29,356✔
293
            !strv_isempty(context->no_exec_paths))
29,356✔
294
                return true;
295

296
        if (context->n_bind_mounts > 0)
29,356✔
297
                return true;
298

299
        if (context->n_temporary_filesystems > 0)
29,315✔
300
                return true;
301

302
        if (context->n_mount_images > 0)
29,156✔
303
                return true;
304

305
        if (context->n_extension_images > 0)
29,133✔
306
                return true;
307

308
        if (!strv_isempty(context->extension_directories))
29,122✔
309
                return true;
310

311
        if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
29,117✔
312
                return true;
313

314
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
29,117✔
315
                return true;
316

317
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
28,109✔
318
                return true;
319

320
        if (context->private_devices ||
27,623✔
321
            context->private_mounts > 0 ||
26,883✔
322
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
26,451✔
323
            context->protect_system != PROTECT_SYSTEM_NO ||
26,428✔
324
            context->protect_home != PROTECT_HOME_NO ||
325
            context->protect_kernel_tunables ||
326
            context->protect_kernel_modules ||
26,428✔
327
            context->protect_kernel_logs ||
24,754✔
328
            exec_needs_cgroup_mount(context) ||
24,754✔
329
            context->protect_proc != PROTECT_PROC_DEFAULT ||
24,736✔
330
            context->proc_subset != PROC_SUBSET_ALL ||
24,673✔
331
            exec_needs_ipc_namespace(context) ||
49,346✔
332
            exec_needs_pid_namespace(context))
24,673✔
333
                return true;
2,991✔
334

335
        if (context->root_directory) {
24,632✔
336
                if (exec_context_get_effective_mount_apivfs(context))
5✔
337
                        return true;
338

339
                for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
340
                        if (params && !params->prefix[t])
×
341
                                continue;
×
342

343
                        if (context->directories[t].n_items > 0)
×
344
                                return true;
345
                }
346
        }
347

348
        if (context->dynamic_user &&
24,627✔
349
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
350
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
351
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
352
                return true;
353

354
        if (exec_context_get_effective_bind_log_sockets(context))
24,627✔
355
                return true;
356

357
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
147,418✔
358
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
127,595✔
359
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
4,798✔
360
                                return true;
361

362
        return false;
363
}
364

365
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
3,893✔
366
        assert(context);
3,893✔
367
        assert(params);
3,893✔
368

369
        if (!params->notify_socket)
3,893✔
370
                return NULL;
371

372
        if (!needs_sandboxing)
3,242✔
373
                return NULL;
374

375
        if (!context->root_directory && !context->root_image)
3,242✔
376
                return NULL;
377

378
        if (!exec_context_get_effective_mount_apivfs(context))
×
379
                return NULL;
380

381
        if (!FLAGS_SET(params->flags, EXEC_APPLY_CHROOT))
×
382
                return NULL;
×
383

384
        return "/run/host/notify";
385
}
386

387
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
11,810✔
388
        assert(context);
11,810✔
389

390
        if (!context->dynamic_user)
11,810✔
391
                return false;
392

393
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
104✔
394
                return false;
395

396
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
98✔
397
                return false;
14✔
398

399
        return true;
400
}
401

402
int exec_params_get_cgroup_path(
14,301✔
403
                const ExecParameters *params,
404
                const CGroupContext *c,
405
                char **ret) {
406

407
        const char *subgroup = NULL;
14,301✔
408
        char *p;
14,301✔
409

410
        assert(params);
14,301✔
411
        assert(ret);
14,301✔
412

413
        if (!params->cgroup_path)
14,301✔
414
                return -EINVAL;
415

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

425
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
14,301✔
426
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
690✔
427
                        subgroup = ".control";
428
                else
429
                        subgroup = c->delegate_subgroup;
656✔
430
        }
431

432
        if (subgroup)
656✔
433
                p = path_join(params->cgroup_path, subgroup);
690✔
434
        else
435
                p = strdup(params->cgroup_path);
13,611✔
436
        if (!p)
14,301✔
437
                return -ENOMEM;
438

439
        *ret = p;
14,301✔
440
        return !!subgroup;
14,301✔
441
}
442

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

446
        return c->cpu_affinity_from_numa;
1,236✔
447
}
448

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

454
        if (!DEBUG_LOGGING)
2,185✔
455
                return;
2,185✔
456

457
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
4,370✔
458

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

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

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

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

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

493
        LOG_CONTEXT_PUSH_UNIT(unit);
4,370✔
494

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

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

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

511
                        r = cg_create(SYSTEMD_CGROUP_CONTROLLER, subcgroup_path);
48✔
512
                        if (r < 0)
48✔
513
                                return log_unit_error_errno(unit, r, "Failed to create subcgroup '%s': %m", subcgroup_path);
×
514
                }
515
        }
516

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

524
        r = open_serialization_file("sd-executor-state", &f);
2,185✔
525
        if (r < 0)
2,185✔
526
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
527

528
        fdset = fdset_new();
2,185✔
529
        if (!fdset)
2,185✔
530
                return log_oom();
×
531

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

536
        r = finish_serialization_file(f);
2,185✔
537
        if (r < 0)
2,185✔
538
                return log_unit_error_errno(unit, r, "Failed to finish serialization stream: %m");
×
539

540
        r = fd_cloexec(fileno(f), false);
2,185✔
541
        if (r < 0)
2,185✔
542
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m");
×
543

544
        r = fdset_cloexec(fdset, false);
2,185✔
545
        if (r < 0)
2,185✔
546
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m");
×
547

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

555
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,185✔
556
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,185✔
557

558
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,185✔
559
        dual_timestamp start_timestamp;
2,185✔
560

561
        /* Restore the original ambient capability set the manager was started with to pass it to
562
         * sd-executor. */
563
        r = capability_ambient_set_apply(unit->manager->saved_ambient_set, /* also_inherit= */ false);
2,185✔
564
        if (r < 0)
2,185✔
565
                return log_unit_error_errno(unit, r, "Failed to apply the starting ambient set: %m");
×
566

567
        /* Record the start timestamp before we fork so that it is guaranteed to be earlier than the
568
         * handoff timestamp. */
569
        dual_timestamp_now(&start_timestamp);
2,185✔
570

571
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
572
        r = posix_spawn_wrapper(
4,370✔
573
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
2,185✔
574
                        STRV_MAKE(unit->manager->executor_path,
2,185✔
575
                                  "--deserialize", serialization_fd_number,
576
                                  "--log-level", max_log_levels,
577
                                  "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))),
578
                        environ,
579
                        cg_unified() > 0 ? subcgroup_path : NULL,
580
                        &pidref);
581

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

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

599
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
4,370✔
600
                       command->path, pidref.pid, r > 0 ? "via" : "without");
601

602
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,185✔
603

604
        *ret = TAKE_PIDREF(pidref);
2,185✔
605
        return 0;
2,185✔
606
}
607

608
void exec_context_init(ExecContext *c) {
56,824✔
609
        assert(c);
56,824✔
610

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

614
        *c = (ExecContext) {
56,824✔
615
                .umask = 0022,
616
                .ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO,
56,824✔
617
                .cpu_sched_policy = SCHED_OTHER,
618
                .syslog_priority = LOG_DAEMON|LOG_INFO,
619
                .syslog_level_prefix = true,
620
                .ignore_sigpipe = true,
621
                .timer_slack_nsec = NSEC_INFINITY,
622
                .personality = PERSONALITY_INVALID,
623
                .timeout_clean_usec = USEC_INFINITY,
624
                .capability_bounding_set = CAP_MASK_UNSET,
625
                .restrict_namespaces = NAMESPACE_FLAGS_INITIAL,
626
                .delegate_namespaces = NAMESPACE_FLAGS_INITIAL,
627
                .log_level_max = -1,
628
#if HAVE_SECCOMP
629
                .syscall_errno = SECCOMP_ERROR_NUMBER_KILL,
630
#endif
631
                .tty_rows = UINT_MAX,
632
                .tty_cols = UINT_MAX,
633
                .private_mounts = -1,
634
                .mount_apivfs = -1,
635
                .bind_log_sockets = -1,
636
                .memory_ksm = -1,
637
                .set_login_environment = -1,
638
        };
639

640
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
340,944✔
641
                d->mode = 0755;
284,120✔
642

643
        numa_policy_reset(&c->numa_policy);
56,824✔
644

645
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
56,824✔
646
}
56,824✔
647

648
void exec_context_done(ExecContext *c) {
45,395✔
649
        assert(c);
45,395✔
650

651
        c->environment = strv_free(c->environment);
45,395✔
652
        c->environment_files = strv_free(c->environment_files);
45,395✔
653
        c->pass_environment = strv_free(c->pass_environment);
45,395✔
654
        c->unset_environment = strv_free(c->unset_environment);
45,395✔
655

656
        rlimit_free_all(c->rlimit);
45,395✔
657

658
        for (size_t l = 0; l < 3; l++) {
181,580✔
659
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
136,185✔
660
                c->stdio_file[l] = mfree(c->stdio_file[l]);
136,185✔
661
        }
662

663
        c->working_directory = mfree(c->working_directory);
45,395✔
664
        c->root_directory = mfree(c->root_directory);
45,395✔
665
        c->root_image = mfree(c->root_image);
45,395✔
666
        c->root_image_options = mount_options_free_all(c->root_image_options);
45,395✔
667
        c->root_hash = mfree(c->root_hash);
45,395✔
668
        c->root_hash_size = 0;
45,395✔
669
        c->root_hash_path = mfree(c->root_hash_path);
45,395✔
670
        c->root_hash_sig = mfree(c->root_hash_sig);
45,395✔
671
        c->root_hash_sig_size = 0;
45,395✔
672
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
45,395✔
673
        c->root_verity = mfree(c->root_verity);
45,395✔
674
        c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
45,395✔
675
        c->extension_directories = strv_free(c->extension_directories);
45,395✔
676
        c->tty_path = mfree(c->tty_path);
45,395✔
677
        c->syslog_identifier = mfree(c->syslog_identifier);
45,395✔
678
        c->user = mfree(c->user);
45,395✔
679
        c->group = mfree(c->group);
45,395✔
680

681
        c->supplementary_groups = strv_free(c->supplementary_groups);
45,395✔
682

683
        c->pam_name = mfree(c->pam_name);
45,395✔
684

685
        c->read_only_paths = strv_free(c->read_only_paths);
45,395✔
686
        c->read_write_paths = strv_free(c->read_write_paths);
45,395✔
687
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
45,395✔
688
        c->exec_paths = strv_free(c->exec_paths);
45,395✔
689
        c->no_exec_paths = strv_free(c->no_exec_paths);
45,395✔
690
        c->exec_search_path = strv_free(c->exec_search_path);
45,395✔
691

692
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
45,395✔
693
        c->bind_mounts = NULL;
45,395✔
694
        c->n_bind_mounts = 0;
45,395✔
695
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
45,395✔
696
        c->temporary_filesystems = NULL;
45,395✔
697
        c->n_temporary_filesystems = 0;
45,395✔
698
        c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
45,395✔
699

700
        cpu_set_reset(&c->cpu_set);
45,395✔
701
        numa_policy_reset(&c->numa_policy);
45,395✔
702

703
        c->utmp_id = mfree(c->utmp_id);
45,395✔
704
        c->selinux_context = mfree(c->selinux_context);
45,395✔
705
        c->apparmor_profile = mfree(c->apparmor_profile);
45,395✔
706
        c->smack_process_label = mfree(c->smack_process_label);
45,395✔
707

708
        c->restrict_filesystems = set_free_free(c->restrict_filesystems);
45,395✔
709

710
        c->syscall_filter = hashmap_free(c->syscall_filter);
45,395✔
711
        c->syscall_archs = set_free(c->syscall_archs);
45,395✔
712
        c->syscall_log = hashmap_free(c->syscall_log);
45,395✔
713
        c->address_families = set_free(c->address_families);
45,395✔
714

715
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
272,370✔
716
                exec_directory_done(d);
226,975✔
717

718
        c->log_level_max = -1;
45,395✔
719

720
        exec_context_free_log_extra_fields(c);
45,395✔
721
        c->log_filter_allowed_patterns = set_free_free(c->log_filter_allowed_patterns);
45,395✔
722
        c->log_filter_denied_patterns = set_free_free(c->log_filter_denied_patterns);
45,395✔
723

724
        c->log_ratelimit = (RateLimit) {};
45,395✔
725

726
        c->stdin_data = mfree(c->stdin_data);
45,395✔
727
        c->stdin_data_size = 0;
45,395✔
728

729
        c->network_namespace_path = mfree(c->network_namespace_path);
45,395✔
730
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
45,395✔
731

732
        c->log_namespace = mfree(c->log_namespace);
45,395✔
733

734
        c->load_credentials = hashmap_free(c->load_credentials);
45,395✔
735
        c->set_credentials = hashmap_free(c->set_credentials);
45,395✔
736
        c->import_credentials = ordered_set_free(c->import_credentials);
45,395✔
737

738
        c->root_image_policy = image_policy_free(c->root_image_policy);
45,395✔
739
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
45,395✔
740
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
45,395✔
741

742
        c->private_hostname = mfree(c->private_hostname);
45,395✔
743
}
45,395✔
744

745
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
4,868✔
746
        assert(c);
4,868✔
747

748
        if (!runtime_prefix)
4,868✔
749
                return 0;
750

751
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
4,881✔
752
                _cleanup_free_ char *p = NULL;
13✔
753

754
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
13✔
755
                        p = path_join(runtime_prefix, "private", i->path);
×
756
                else
757
                        p = path_join(runtime_prefix, i->path);
13✔
758
                if (!p)
13✔
759
                        return -ENOMEM;
760

761
                /* We execute this synchronously, since we need to be sure this is gone when we start the
762
                 * service next. */
763
                (void) rm_rf(p, REMOVE_ROOT);
13✔
764

765
                STRV_FOREACH(symlink, i->symlinks) {
13✔
766
                        _cleanup_free_ char *symlink_abs = NULL;
×
767

768
                        if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
×
769
                                symlink_abs = path_join(runtime_prefix, "private", *symlink);
×
770
                        else
771
                                symlink_abs = path_join(runtime_prefix, *symlink);
×
772
                        if (!symlink_abs)
×
773
                                return -ENOMEM;
×
774

775
                        (void) unlink(symlink_abs);
×
776
                }
777
        }
778

779
        return 0;
780
}
781

782
int exec_context_destroy_mount_ns_dir(Unit *u) {
10,769✔
783
        _cleanup_free_ char *p = NULL;
10,769✔
784

785
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
10,769✔
786
                return 0;
787

788
        p = path_join("/run/systemd/propagate/", u->id);
2,003✔
789
        if (!p)
2,003✔
790
                return -ENOMEM;
791

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

796
        return 0;
797
}
798

799
void exec_command_done(ExecCommand *c) {
96,955✔
800
        assert(c);
96,955✔
801

802
        c->path = mfree(c->path);
96,955✔
803
        c->argv = strv_free(c->argv);
96,955✔
804
}
96,955✔
805

806
void exec_command_done_array(ExecCommand *c, size_t n) {
26,928✔
807
        FOREACH_ARRAY(i, c, n)
107,710✔
808
                exec_command_done(i);
80,782✔
809
}
26,928✔
810

811
ExecCommand* exec_command_free(ExecCommand *c) {
16,145✔
812
        if (!c)
16,145✔
813
                return NULL;
814

815
        exec_command_done(c);
16,145✔
816
        return mfree(c);
16,145✔
817
}
818

819
ExecCommand* exec_command_free_list(ExecCommand *c) {
118,996✔
820
        ExecCommand *i;
118,996✔
821

822
        while ((i = LIST_POP(command, c)))
135,141✔
823
                exec_command_free(i);
16,145✔
824

825
        return NULL;
118,996✔
826
}
827

828
void exec_command_free_array(ExecCommand **c, size_t n) {
18,439✔
829
        FOREACH_ARRAY(i, c, n)
137,420✔
830
                *i = exec_command_free_list(*i);
118,981✔
831
}
18,439✔
832

833
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
6,799✔
834
        FOREACH_ARRAY(i, c, n)
27,195✔
835
                exec_status_reset(&i->exec_status);
20,396✔
836
}
6,799✔
837

838
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
4,155✔
839
        FOREACH_ARRAY(i, c, n)
28,742✔
840
                LIST_FOREACH(command, z, *i)
26,749✔
841
                        exec_status_reset(&z->exec_status);
2,162✔
842
}
4,155✔
843

844
typedef struct InvalidEnvInfo {
845
        const Unit *unit;
846
        const char *path;
847
} InvalidEnvInfo;
848

849
static void invalid_env(const char *p, void *userdata) {
×
850
        InvalidEnvInfo *info = userdata;
×
851

852
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
853
}
×
854

855
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
38,082✔
856
        assert(c);
38,082✔
857

858
        switch (fd_index) {
38,082✔
859

860
        case STDIN_FILENO:
12,694✔
861
                if (c->std_input != EXEC_INPUT_NAMED_FD)
12,694✔
862
                        return NULL;
863

864
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
865

866
        case STDOUT_FILENO:
12,694✔
867
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
12,694✔
868
                        return NULL;
869

870
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
871

872
        case STDERR_FILENO:
12,694✔
873
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
12,694✔
874
                        return NULL;
875

876
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
877

878
        default:
879
                return NULL;
880
        }
881
}
882

883
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
2,185✔
884
        _cleanup_strv_free_ char **v = NULL;
2,185✔
885
        int r;
2,185✔
886

887
        assert(c);
2,185✔
888
        assert(ret);
2,185✔
889

890
        STRV_FOREACH(i, c->environment_files) {
2,187✔
891
                _cleanup_globfree_ glob_t pglob = {};
2✔
892
                bool ignore = false;
2✔
893
                char *fn = *i;
2✔
894

895
                if (fn[0] == '-') {
2✔
896
                        ignore = true;
1✔
897
                        fn++;
1✔
898
                }
899

900
                if (!path_is_absolute(fn)) {
2✔
901
                        if (ignore)
×
902
                                continue;
×
903
                        return -EINVAL;
904
                }
905

906
                /* Filename supports globbing, take all matching files */
907
                r = safe_glob(fn, 0, &pglob);
2✔
908
                if (r < 0) {
2✔
909
                        if (ignore)
1✔
910
                                continue;
1✔
911
                        return r;
912
                }
913

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

917
                FOREACH_ARRAY(path, pglob.gl_pathv, pglob.gl_pathc) {
2✔
918
                        _cleanup_strv_free_ char **p = NULL;
1✔
919

920
                        r = load_env_file(NULL, *path, &p);
1✔
921
                        if (r < 0) {
1✔
922
                                if (ignore)
×
923
                                        continue;
×
924
                                return r;
925
                        }
926

927
                        /* Log invalid environment variables with filename */
928
                        if (p) {
1✔
929
                                InvalidEnvInfo info = {
1✔
930
                                        .unit = unit,
931
                                        .path = *path,
1✔
932
                                };
933

934
                                p = strv_env_clean_with_callback(p, invalid_env, &info);
1✔
935
                        }
936

937
                        if (!v)
1✔
938
                                v = TAKE_PTR(p);
1✔
939
                        else {
940
                                char **m = strv_env_merge(v, p);
×
941
                                if (!m)
×
942
                                        return -ENOMEM;
×
943

944
                                strv_free_and_replace(v, m);
×
945
                        }
946
                }
947
        }
948

949
        *ret = TAKE_PTR(v);
2,185✔
950

951
        return 0;
2,185✔
952
}
953

954
static bool tty_may_match_dev_console(const char *tty) {
363✔
955
        _cleanup_free_ char *resolved = NULL;
363✔
956

957
        if (!tty)
363✔
958
                return true;
959

960
        tty = skip_dev_prefix(tty);
363✔
961

962
        /* trivial identity? */
963
        if (streq(tty, "console"))
363✔
964
                return true;
965

966
        if (resolve_dev_console(&resolved) < 0)
39✔
967
                return true; /* if we could not resolve, assume it may */
968

969
        /* "tty0" means the active VC, so it may be the same sometimes */
970
        return path_equal(skip_dev_prefix(resolved), tty) || (streq(skip_dev_prefix(resolved), "tty0") && tty_is_vc(tty));
39✔
971
}
972

973
static bool exec_context_may_touch_tty(const ExecContext *ec) {
21,460✔
974
        assert(ec);
21,460✔
975

976
        return ec->tty_reset ||
21,460✔
977
                ec->tty_vhangup ||
21,460✔
978
                ec->tty_vt_disallocate ||
21,312✔
979
                is_terminal_input(ec->std_input) ||
21,312✔
980
                is_terminal_output(ec->std_output) ||
42,682✔
981
                is_terminal_output(ec->std_error);
21,048✔
982
}
983

984
bool exec_context_may_touch_console(const ExecContext *ec) {
19,966✔
985

986
        return exec_context_may_touch_tty(ec) &&
20,329✔
987
               tty_may_match_dev_console(exec_context_tty_path(ec));
363✔
988
}
989

990
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,480✔
991
        assert(c);
1,480✔
992

993
        /* Determines whether ANSI sequences shall be used during any terminal initialisation:
994
         *
995
         * 1. If the reset logic is enabled at all, this is an immediate no.
996
         *
997
         * 2. If $TERM is set to anything other than "dumb", it's a yes.
998
         */
999

1000
        if (!c->tty_reset)
1,480✔
1001
                return false;
1002

1003
        return !streq_ptr(strv_env_get(c->environment, "TERM"), "dumb");
499✔
1004
}
1005

1006
static void strv_fprintf(FILE *f, char **l) {
×
1007
        assert(f);
×
1008

1009
        STRV_FOREACH(g, l)
×
1010
                fprintf(f, " %s", *g);
×
1011
}
×
1012

1013
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
1,792✔
1014
        assert(f);
1,792✔
1015
        assert(prefix);
1,792✔
1016
        assert(name);
1,792✔
1017

1018
        if (!strv_isempty(strv)) {
1,792✔
1019
                fprintf(f, "%s%s:", prefix, name);
×
1020
                strv_fprintf(f, strv);
×
1021
                fputs("\n", f);
×
1022
        }
1023
}
1,792✔
1024

1025
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1026
        assert(p);
×
1027
        assert(f);
×
1028

1029
        prefix = strempty(prefix);
×
1030

1031
        fprintf(f,
×
1032
                "%sRuntimeScope: %s\n"
1033
                "%sExecFlags: %u\n"
1034
                "%sSELinuxContextNetwork: %s\n"
1035
                "%sCgroupSupportedMask: %u\n"
1036
                "%sCgroupPath: %s\n"
1037
                "%sCrededentialsDirectory: %s\n"
1038
                "%sEncryptedCredentialsDirectory: %s\n"
1039
                "%sConfirmSpawn: %s\n"
1040
                "%sShallConfirmSpawn: %s\n"
1041
                "%sWatchdogUSec: " USEC_FMT "\n"
1042
                "%sNotifySocket: %s\n"
1043
                "%sDebugInvocation: %s\n"
1044
                "%sFallbackSmackProcessLabel: %s\n",
1045
                prefix, runtime_scope_to_string(p->runtime_scope),
×
1046
                prefix, p->flags,
×
1047
                prefix, yes_no(p->selinux_context_net),
×
1048
                prefix, p->cgroup_supported,
×
1049
                prefix, p->cgroup_path,
×
1050
                prefix, strempty(p->received_credentials_directory),
×
1051
                prefix, strempty(p->received_encrypted_credentials_directory),
×
1052
                prefix, strempty(p->confirm_spawn),
×
1053
                prefix, yes_no(p->shall_confirm_spawn),
×
1054
                prefix, p->watchdog_usec,
×
1055
                prefix, strempty(p->notify_socket),
×
1056
                prefix, yes_no(p->debug_invocation),
×
1057
                prefix, strempty(p->fallback_smack_process_label));
×
1058

1059
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1060
        strv_dump(f, prefix, "Environment", p->environment);
×
1061
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1062

1063
        LIST_FOREACH(open_files, file, p->open_files)
×
1064
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1065

1066
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1067
}
×
1068

1069
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
224✔
1070
        int r;
224✔
1071

1072
        assert(c);
224✔
1073
        assert(f);
224✔
1074

1075
        prefix = strempty(prefix);
224✔
1076

1077
        fprintf(f,
224✔
1078
                "%sUMask: %04o\n"
1079
                "%sWorkingDirectory: %s\n"
1080
                "%sRootDirectory: %s\n"
1081
                "%sRootEphemeral: %s\n"
1082
                "%sNonBlocking: %s\n"
1083
                "%sPrivateTmp: %s\n"
1084
                "%sPrivateDevices: %s\n"
1085
                "%sProtectKernelTunables: %s\n"
1086
                "%sProtectKernelModules: %s\n"
1087
                "%sProtectKernelLogs: %s\n"
1088
                "%sProtectClock: %s\n"
1089
                "%sProtectControlGroups: %s\n"
1090
                "%sPrivateNetwork: %s\n"
1091
                "%sPrivateUsers: %s\n"
1092
                "%sPrivatePIDs: %s\n"
1093
                "%sProtectHome: %s\n"
1094
                "%sProtectSystem: %s\n"
1095
                "%sMountAPIVFS: %s\n"
1096
                "%sBindLogSockets: %s\n"
1097
                "%sIgnoreSIGPIPE: %s\n"
1098
                "%sMemoryDenyWriteExecute: %s\n"
1099
                "%sRestrictRealtime: %s\n"
1100
                "%sRestrictSUIDSGID: %s\n"
1101
                "%sKeyringMode: %s\n"
1102
                "%sProtectHostname: %s%s%s\n"
1103
                "%sProtectProc: %s\n"
1104
                "%sProcSubset: %s\n",
1105
                prefix, c->umask,
224✔
1106
                prefix, empty_to_root(c->working_directory),
224✔
1107
                prefix, empty_to_root(c->root_directory),
224✔
1108
                prefix, yes_no(c->root_ephemeral),
224✔
1109
                prefix, yes_no(c->non_blocking),
224✔
1110
                prefix, private_tmp_to_string(c->private_tmp),
224✔
1111
                prefix, yes_no(c->private_devices),
224✔
1112
                prefix, yes_no(c->protect_kernel_tunables),
224✔
1113
                prefix, yes_no(c->protect_kernel_modules),
224✔
1114
                prefix, yes_no(c->protect_kernel_logs),
224✔
1115
                prefix, yes_no(c->protect_clock),
224✔
1116
                prefix, protect_control_groups_to_string(c->protect_control_groups),
224✔
1117
                prefix, yes_no(c->private_network),
224✔
1118
                prefix, private_users_to_string(c->private_users),
224✔
1119
                prefix, private_pids_to_string(c->private_pids),
224✔
1120
                prefix, protect_home_to_string(c->protect_home),
224✔
1121
                prefix, protect_system_to_string(c->protect_system),
224✔
1122
                prefix, yes_no(exec_context_get_effective_mount_apivfs(c)),
224✔
1123
                prefix, yes_no(exec_context_get_effective_bind_log_sockets(c)),
224✔
1124
                prefix, yes_no(c->ignore_sigpipe),
224✔
1125
                prefix, yes_no(c->memory_deny_write_execute),
224✔
1126
                prefix, yes_no(c->restrict_realtime),
224✔
1127
                prefix, yes_no(c->restrict_suid_sgid),
224✔
1128
                prefix, exec_keyring_mode_to_string(c->keyring_mode),
224✔
1129
                prefix, protect_hostname_to_string(c->protect_hostname), c->private_hostname ? ":" : "", strempty(c->private_hostname),
448✔
1130
                prefix, protect_proc_to_string(c->protect_proc),
224✔
1131
                prefix, proc_subset_to_string(c->proc_subset));
224✔
1132

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

1136
        if (c->root_image)
224✔
1137
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1138

1139
        if (c->root_image_options) {
224✔
1140
                fprintf(f, "%sRootImageOptions:", prefix);
×
1141
                LIST_FOREACH(mount_options, o, c->root_image_options)
×
1142
                        if (!isempty(o->options))
×
1143
                                fprintf(f, " %s:%s",
×
1144
                                        partition_designator_to_string(o->partition_designator),
1145
                                        o->options);
1146
                fprintf(f, "\n");
×
1147
        }
1148

1149
        if (c->root_hash) {
224✔
1150
                _cleanup_free_ char *encoded = NULL;
×
1151
                encoded = hexmem(c->root_hash, c->root_hash_size);
×
1152
                if (encoded)
×
1153
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1154
        }
1155

1156
        if (c->root_hash_path)
224✔
1157
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1158

1159
        if (c->root_hash_sig) {
224✔
1160
                _cleanup_free_ char *encoded = NULL;
×
1161
                ssize_t len;
×
1162
                len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded);
×
1163
                if (len)
×
1164
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1165
        }
1166

1167
        if (c->root_hash_sig_path)
224✔
1168
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1169

1170
        if (c->root_verity)
224✔
1171
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1172

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

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

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

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

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

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

1190
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
1,130✔
1191
                        fprintf(f,
10✔
1192
                                "%s%s: %s%s\n",
1193
                                prefix,
1194
                                exec_directory_type_to_string(dt),
1195
                                c->directories[dt].items[i].path,
1196
                                FLAGS_SET(c->directories[dt].items[i].flags, EXEC_DIRECTORY_READ_ONLY) ? " (ro)" : "");
10✔
1197

1198
                        STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
10✔
1199
                                fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
×
1200
                }
1201
        }
1202

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

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

1208
        if (c->nice_set)
224✔
1209
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
×
1210

1211
        if (c->oom_score_adjust_set)
224✔
1212
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
10✔
1213

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

1217
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
3,808✔
1218
                if (c->rlimit[i]) {
3,584✔
1219
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
20✔
1220
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1221
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
20✔
1222
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
20✔
1223
                }
1224

1225
        if (c->ioprio_set) {
224✔
1226
                _cleanup_free_ char *class_str = NULL;
×
1227

1228
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
×
1229
                if (r >= 0)
×
1230
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
×
1231

1232
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
×
1233
        }
1234

1235
        if (c->cpu_sched_set) {
224✔
1236
                _cleanup_free_ char *policy_str = NULL;
×
1237

1238
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1239
                if (r >= 0)
×
1240
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1241

1242
                fprintf(f,
×
1243
                        "%sCPUSchedulingPriority: %i\n"
1244
                        "%sCPUSchedulingResetOnFork: %s\n",
1245
                        prefix, c->cpu_sched_priority,
×
1246
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1247
        }
1248

1249
        if (c->cpu_set.set) {
224✔
1250
                _cleanup_free_ char *affinity = NULL;
×
1251

1252
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1253
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1254
        }
1255

1256
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
224✔
1257
                _cleanup_free_ char *nodes = NULL;
1✔
1258

1259
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1260
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1261
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1262
        }
1263

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

1267
        fprintf(f,
224✔
1268
                "%sStandardInput: %s\n"
1269
                "%sStandardOutput: %s\n"
1270
                "%sStandardError: %s\n",
1271
                prefix, exec_input_to_string(c->std_input),
224✔
1272
                prefix, exec_output_to_string(c->std_output),
224✔
1273
                prefix, exec_output_to_string(c->std_error));
224✔
1274

1275
        if (c->std_input == EXEC_INPUT_NAMED_FD)
224✔
1276
                fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
×
1277
        if (c->std_output == EXEC_OUTPUT_NAMED_FD)
224✔
1278
                fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
×
1279
        if (c->std_error == EXEC_OUTPUT_NAMED_FD)
224✔
1280
                fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
×
1281

1282
        if (c->std_input == EXEC_INPUT_FILE)
224✔
1283
                fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
×
1284
        if (c->std_output == EXEC_OUTPUT_FILE)
224✔
1285
                fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1286
        if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
224✔
1287
                fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1288
        if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1289
                fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1290
        if (c->std_error == EXEC_OUTPUT_FILE)
224✔
1291
                fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1292
        if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
224✔
1293
                fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1294
        if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1295
                fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1296

1297
        if (c->tty_path)
224✔
1298
                fprintf(f,
×
1299
                        "%sTTYPath: %s\n"
1300
                        "%sTTYReset: %s\n"
1301
                        "%sTTYVHangup: %s\n"
1302
                        "%sTTYVTDisallocate: %s\n"
1303
                        "%sTTYRows: %u\n"
1304
                        "%sTTYColumns: %u\n",
1305
                        prefix, c->tty_path,
1306
                        prefix, yes_no(c->tty_reset),
×
1307
                        prefix, yes_no(c->tty_vhangup),
×
1308
                        prefix, yes_no(c->tty_vt_disallocate),
×
1309
                        prefix, c->tty_rows,
×
1310
                        prefix, c->tty_cols);
×
1311

1312
        if (IN_SET(c->std_output,
224✔
1313
                   EXEC_OUTPUT_KMSG,
1314
                   EXEC_OUTPUT_JOURNAL,
1315
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1316
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1317
            IN_SET(c->std_error,
17✔
1318
                   EXEC_OUTPUT_KMSG,
1319
                   EXEC_OUTPUT_JOURNAL,
1320
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1321
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1322

1323
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
207✔
1324

1325
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
207✔
1326
                if (r >= 0)
207✔
1327
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
207✔
1328

1329
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
207✔
1330
                if (r >= 0)
207✔
1331
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
207✔
1332
        }
1333

1334
        if (c->log_level_max >= 0) {
224✔
1335
                _cleanup_free_ char *t = NULL;
1✔
1336

1337
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1338

1339
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1340
        }
1341

1342
        if (c->log_ratelimit.interval > 0)
224✔
1343
                fprintf(f,
×
1344
                        "%sLogRateLimitIntervalSec: %s\n",
1345
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1346

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

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

1353
                char *pattern;
×
1354
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1355
                        fprintf(f, " %s", pattern);
×
1356
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1357
                        fprintf(f, " ~%s", pattern);
×
1358
                fputc('\n', f);
×
1359
        }
1360

1361
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
228✔
1362
                fprintf(f, "%sLogExtraFields: ", prefix);
4✔
1363
                fwrite(field->iov_base, 1, field->iov_len, f);
4✔
1364
                fputc('\n', f);
4✔
1365
        }
1366

1367
        if (c->log_namespace)
224✔
1368
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1369

1370
        if (c->secure_bits) {
224✔
1371
                _cleanup_free_ char *str = NULL;
×
1372

1373
                r = secure_bits_to_string_alloc(c->secure_bits, &str);
×
1374
                if (r >= 0)
×
1375
                        fprintf(f, "%sSecure Bits: %s\n", prefix, str);
×
1376
        }
1377

1378
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
224✔
1379
                _cleanup_free_ char *str = NULL;
16✔
1380

1381
                r = capability_set_to_string(c->capability_bounding_set, &str);
16✔
1382
                if (r >= 0)
16✔
1383
                        fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
16✔
1384
        }
1385

1386
        if (c->capability_ambient_set != 0) {
224✔
1387
                _cleanup_free_ char *str = NULL;
×
1388

1389
                r = capability_set_to_string(c->capability_ambient_set, &str);
×
1390
                if (r >= 0)
×
1391
                        fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
×
1392
        }
1393

1394
        if (c->user)
224✔
1395
                fprintf(f, "%sUser: %s\n", prefix, c->user);
1✔
1396
        if (c->group)
224✔
1397
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1398

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

1401
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
224✔
1402

1403
        if (c->pam_name)
224✔
1404
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
×
1405

1406
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
224✔
1407
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
224✔
1408
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
224✔
1409
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
224✔
1410
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
224✔
1411
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
224✔
1412

1413
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
228✔
1414
                fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
4✔
1415
                        mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
4✔
1416
                        mount->ignore_enoent ? "-": "",
4✔
1417
                        mount->source,
1418
                        mount->destination,
1419
                        mount->recursive ? "rbind" : "norbind");
4✔
1420

1421
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
224✔
1422
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1423
                        tmpfs->path,
1424
                        isempty(tmpfs->options) ? "" : ":",
×
1425
                        strempty(tmpfs->options));
×
1426

1427
        if (c->utmp_id)
224✔
1428
                fprintf(f,
×
1429
                        "%sUtmpIdentifier: %s\n",
1430
                        prefix, c->utmp_id);
1431

1432
        if (c->selinux_context)
224✔
1433
                fprintf(f,
×
1434
                        "%sSELinuxContext: %s%s\n",
1435
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1436

1437
        if (c->apparmor_profile)
224✔
1438
                fprintf(f,
×
1439
                        "%sAppArmorProfile: %s%s\n",
1440
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1441

1442
        if (c->smack_process_label)
224✔
1443
                fprintf(f,
×
1444
                        "%sSmackProcessLabel: %s%s\n",
1445
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1446

1447
        if (c->personality != PERSONALITY_INVALID)
224✔
1448
                fprintf(f,
1✔
1449
                        "%sPersonality: %s\n",
1450
                        prefix, strna(personality_to_string(c->personality)));
1451

1452
        fprintf(f,
224✔
1453
                "%sLockPersonality: %s\n",
1454
                prefix, yes_no(c->lock_personality));
224✔
1455

1456
        if (c->syscall_filter) {
224✔
1457
                fprintf(f,
11✔
1458
                        "%sSystemCallFilter: ",
1459
                        prefix);
1460

1461
                if (!c->syscall_allow_list)
11✔
1462
                        fputc('~', f);
×
1463

1464
#if HAVE_SECCOMP
1465
                void *id, *val;
11✔
1466
                bool first = true;
11✔
1467
                HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
4,268✔
1468
                        _cleanup_free_ char *name = NULL;
4,257✔
1469
                        const char *errno_name = NULL;
4,257✔
1470
                        int num = PTR_TO_INT(val);
4,257✔
1471

1472
                        if (first)
4,257✔
1473
                                first = false;
1474
                        else
1475
                                fputc(' ', f);
4,246✔
1476

1477
                        name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
4,257✔
1478
                        fputs(strna(name), f);
4,257✔
1479

1480
                        if (num >= 0) {
4,257✔
1481
                                errno_name = seccomp_errno_or_action_to_string(num);
×
1482
                                if (errno_name)
×
1483
                                        fprintf(f, ":%s", errno_name);
×
1484
                                else
1485
                                        fprintf(f, ":%d", num);
×
1486
                        }
1487
                }
1488
#endif
1489

1490
                fputc('\n', f);
11✔
1491
        }
1492

1493
        if (c->syscall_archs) {
224✔
1494
                fprintf(f,
11✔
1495
                        "%sSystemCallArchitectures:",
1496
                        prefix);
1497

1498
#if HAVE_SECCOMP
1499
                void *id;
11✔
1500
                SET_FOREACH(id, c->syscall_archs)
22✔
1501
                        fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
11✔
1502
#endif
1503
                fputc('\n', f);
11✔
1504
        }
1505

1506
        if (exec_context_restrict_namespaces_set(c)) {
224✔
1507
                _cleanup_free_ char *s = NULL;
12✔
1508

1509
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
12✔
1510
                if (r >= 0)
12✔
1511
                        fprintf(f, "%sRestrictNamespaces: %s\n",
24✔
1512
                                prefix, strna(s));
1513
        }
1514

1515
#if HAVE_LIBBPF
1516
        if (exec_context_restrict_filesystems_set(c)) {
224✔
1517
                char *fs;
×
1518
                SET_FOREACH(fs, c->restrict_filesystems)
×
1519
                        fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
×
1520
        }
1521
#endif
1522

1523
        if (c->network_namespace_path)
224✔
1524
                fprintf(f,
×
1525
                        "%sNetworkNamespacePath: %s\n",
1526
                        prefix, c->network_namespace_path);
1527

1528
        if (c->syscall_errno > 0) {
224✔
1529
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
223✔
1530

1531
#if HAVE_SECCOMP
1532
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
223✔
1533
                if (errno_name)
223✔
1534
                        fputs(errno_name, f);
223✔
1535
                else
1536
                        fprintf(f, "%d", c->syscall_errno);
×
1537
#endif
1538
                fputc('\n', f);
223✔
1539
        }
1540

1541
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
224✔
1542
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1543
                        mount->ignore_enoent ? "-": "",
×
1544
                        mount->source,
1545
                        mount->destination);
1546
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1547
                        fprintf(f, ":%s:%s",
×
1548
                                partition_designator_to_string(o->partition_designator),
1549
                                strempty(o->options));
×
1550
                fprintf(f, "\n");
×
1551
        }
1552

1553
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
224✔
1554
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1555
                        mount->ignore_enoent ? "-": "",
×
1556
                        mount->source);
1557
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1558
                        fprintf(f, ":%s:%s",
×
1559
                                partition_designator_to_string(o->partition_designator),
1560
                                strempty(o->options));
×
1561
                fprintf(f, "\n");
×
1562
        }
1563

1564
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
224✔
1565
}
224✔
1566

1567
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1568
        assert(c);
×
1569

1570
        /* Returns true if the process forked off would run under
1571
         * an unchanged UID or as root. */
1572

1573
        if (!c->user)
×
1574
                return true;
1575

1576
        if (STR_IN_SET(c->user, "root", "0"))
×
1577
                return true;
×
1578

1579
        return false;
×
1580
}
1581

1582
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,472✔
1583
        int p;
2,472✔
1584

1585
        assert(c);
2,472✔
1586

1587
        if (c->ioprio_set)
2,472✔
1588
                return c->ioprio;
18✔
1589

1590
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,454✔
1591
        if (p < 0)
2,454✔
1592
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1593

1594
        return ioprio_normalize(p);
2,454✔
1595
}
1596

1597
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
33,569✔
1598
        assert(c);
33,569✔
1599

1600
        /* Explicit setting wins */
1601
        if (c->mount_apivfs >= 0)
33,569✔
1602
                return c->mount_apivfs > 0;
122✔
1603

1604
        /* Default to "yes" if root directory or image are specified */
1605
        if (exec_context_with_rootfs(c))
33,447✔
1606
                return true;
72✔
1607

1608
        return false;
1609
}
1610

1611
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
28,096✔
1612
        assert(c);
28,096✔
1613

1614
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1615
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1616
        if (c->log_namespace)
28,096✔
1617
                return true;
1618

1619
        if (c->bind_log_sockets >= 0)
28,088✔
1620
                return c->bind_log_sockets > 0;
2✔
1621

1622
        if (exec_context_get_effective_mount_apivfs(c))
28,086✔
1623
                return true;
1624

1625
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1626
        if (exec_context_with_rootfs(c) && c->private_devices)
28,010✔
1627
                return true;
×
1628

1629
        return false;
1630
}
1631

1632
void exec_context_free_log_extra_fields(ExecContext *c) {
45,397✔
1633
        assert(c);
45,397✔
1634

1635
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
45,402✔
1636
                free(field->iov_base);
5✔
1637

1638
        c->log_extra_fields = mfree(c->log_extra_fields);
45,397✔
1639
        c->n_log_extra_fields = 0;
45,397✔
1640
}
45,397✔
1641

1642
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,494✔
1643
        _cleanup_close_ int fd = -EBADF;
1,494✔
1644
        const char *path;
1,494✔
1645
        struct stat st;
1,494✔
1646
        int r;
1,494✔
1647

1648
        assert(c);
1,494✔
1649

1650
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1651
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,494✔
1652

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

1659
        path = exec_context_tty_path(c);
49✔
1660
        if (!path)
49✔
1661
                return;
1662

1663
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
49✔
1664
        if (fd < 0)
49✔
1665
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1666
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1667
                                             path);
1668

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

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

1680
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
49✔
1681
        if (r < 0)
49✔
1682
                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✔
1683
}
1684

1685
int exec_context_get_clean_directories(
×
1686
                ExecContext *c,
1687
                char **prefix,
1688
                ExecCleanMask mask,
1689
                char ***ret) {
1690

1691
        _cleanup_strv_free_ char **l = NULL;
×
1692
        int r;
×
1693

1694
        assert(c);
×
1695
        assert(prefix);
×
1696
        assert(ret);
×
1697

1698
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
1699
                if (!BIT_SET(mask, t))
×
1700
                        continue;
×
1701

1702
                if (!prefix[t])
×
1703
                        continue;
×
1704

1705
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
×
1706
                        char *j;
×
1707

1708
                        j = path_join(prefix[t], i->path);
×
1709
                        if (!j)
×
1710
                                return -ENOMEM;
1711

1712
                        r = strv_consume(&l, j);
×
1713
                        if (r < 0)
×
1714
                                return r;
1715

1716
                        /* Also remove private directories unconditionally. */
1717
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
×
1718
                                j = path_join(prefix[t], "private", i->path);
×
1719
                                if (!j)
×
1720
                                        return -ENOMEM;
1721

1722
                                r = strv_consume(&l, j);
×
1723
                                if (r < 0)
×
1724
                                        return r;
1725
                        }
1726

1727
                        STRV_FOREACH(symlink, i->symlinks) {
×
1728
                                j = path_join(prefix[t], *symlink);
×
1729
                                if (!j)
×
1730
                                        return -ENOMEM;
1731

1732
                                r = strv_consume(&l, j);
×
1733
                                if (r < 0)
×
1734
                                        return r;
1735
                        }
1736
                }
1737
        }
1738

1739
        *ret = TAKE_PTR(l);
×
1740
        return 0;
×
1741
}
1742

1743
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,216✔
1744
        ExecCleanMask mask = 0;
1,216✔
1745

1746
        assert(c);
1,216✔
1747
        assert(ret);
1,216✔
1748

1749
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
7,296✔
1750
                if (c->directories[t].n_items > 0)
6,080✔
1751
                        mask |= 1U << t;
237✔
1752

1753
        *ret = mask;
1,216✔
1754
        return 0;
1,216✔
1755
}
1756

1757
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,236✔
1758
        int n = 0, r;
1,236✔
1759

1760
        assert(c);
1,236✔
1761

1762
        if (c->oom_score_adjust_set)
1,236✔
1763
                return c->oom_score_adjust;
354✔
1764

1765
        r = get_oom_score_adjust(&n);
882✔
1766
        if (r < 0)
882✔
1767
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1768

1769
        return n;
882✔
1770
}
1771

1772
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1,236✔
1773
        _cleanup_free_ char *t = NULL;
1,236✔
1774
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1,236✔
1775
        int r;
1,236✔
1776

1777
        assert(c);
1,236✔
1778

1779
        if (c->coredump_filter_set)
1,236✔
1780
                return c->coredump_filter;
×
1781

1782
        r = read_one_line_file("/proc/self/coredump_filter", &t);
1,236✔
1783
        if (r < 0)
1,236✔
1784
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1785
        else {
1786
                r = safe_atoux64(t, &n);
1,236✔
1787
                if (r < 0)
1,236✔
1788
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1789
        }
1790

1791
        return n;
1,236✔
1792
}
1793

1794
int exec_context_get_nice(const ExecContext *c) {
1,236✔
1795
        int n;
1,236✔
1796

1797
        assert(c);
1,236✔
1798

1799
        if (c->nice_set)
1,236✔
1800
                return c->nice;
6✔
1801

1802
        errno = 0;
1,230✔
1803
        n = getpriority(PRIO_PROCESS, 0);
1,230✔
1804
        if (errno > 0) {
1,230✔
1805
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1806
                n = 0;
1807
        }
1808

1809
        return n;
1810
}
1811

1812
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,236✔
1813
        int n;
1,236✔
1814

1815
        assert(c);
1,236✔
1816

1817
        if (c->cpu_sched_set)
1,236✔
1818
                return c->cpu_sched_policy;
×
1819

1820
        n = sched_getscheduler(0);
1,236✔
1821
        if (n < 0)
1,236✔
1822
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1823

1824
        return n < 0 ? SCHED_OTHER : n;
1,236✔
1825
}
1826

1827
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,236✔
1828
        struct sched_param p = {};
1,236✔
1829
        int r;
1,236✔
1830

1831
        assert(c);
1,236✔
1832

1833
        if (c->cpu_sched_set)
1,236✔
1834
                return c->cpu_sched_priority;
×
1835

1836
        r = sched_getparam(0, &p);
1,236✔
1837
        if (r < 0)
1,236✔
1838
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1839

1840
        return r >= 0 ? p.sched_priority : 0;
1,236✔
1841
}
1842

1843
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,236✔
1844
        int r;
1,236✔
1845

1846
        assert(c);
1,236✔
1847

1848
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,236✔
1849
                return c->timer_slack_nsec;
1850

1851
        r = prctl(PR_GET_TIMERSLACK);
1,236✔
1852
        if (r < 0)
1,236✔
1853
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1854

1855
        return (uint64_t) MAX(r, 0);
1,236✔
1856
}
1857

1858
bool exec_context_get_set_login_environment(const ExecContext *c) {
10,755✔
1859
        assert(c);
10,755✔
1860

1861
        if (c->set_login_environment >= 0)
10,755✔
1862
                return c->set_login_environment;
×
1863

1864
        return c->user || c->dynamic_user || c->pam_name;
19,378✔
1865
}
1866

1867
char** exec_context_get_syscall_filter(const ExecContext *c) {
1,236✔
1868
        _cleanup_strv_free_ char **l = NULL;
1,236✔
1869

1870
        assert(c);
1,236✔
1871

1872
#if HAVE_SECCOMP
1873
        void *id, *val;
1,236✔
1874
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
18,774✔
1875
                _cleanup_free_ char *name = NULL;
17,538✔
1876
                const char *e = NULL;
17,538✔
1877
                char *s;
17,538✔
1878
                int num = PTR_TO_INT(val);
17,538✔
1879

1880
                if (c->syscall_allow_list && num >= 0)
17,538✔
1881
                        /* syscall with num >= 0 in allow-list is denied. */
1882
                        continue;
×
1883

1884
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
17,538✔
1885
                if (!name)
17,538✔
1886
                        continue;
×
1887

1888
                if (num >= 0) {
17,538✔
1889
                        e = seccomp_errno_or_action_to_string(num);
×
1890
                        if (e) {
×
1891
                                s = strjoin(name, ":", e);
×
1892
                                if (!s)
×
1893
                                        return NULL;
1894
                        } else {
1895
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1896
                                        return NULL;
1897
                        }
1898
                } else
1899
                        s = TAKE_PTR(name);
17,538✔
1900

1901
                if (strv_consume(&l, s) < 0)
17,538✔
1902
                        return NULL;
1903
        }
1904

1905
        strv_sort(l);
1,236✔
1906
#endif
1907

1908
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,236✔
1909
}
1910

1911
char** exec_context_get_syscall_archs(const ExecContext *c) {
1,236✔
1912
        _cleanup_strv_free_ char **l = NULL;
1,236✔
1913

1914
        assert(c);
1,236✔
1915

1916
#if HAVE_SECCOMP
1917
        void *id;
1,236✔
1918
        SET_FOREACH(id, c->syscall_archs) {
1,287✔
1919
                const char *name;
51✔
1920

1921
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
51✔
1922
                if (!name)
51✔
1923
                        continue;
×
1924

1925
                if (strv_extend(&l, name) < 0)
51✔
1926
                        return NULL;
×
1927
        }
1928

1929
        strv_sort(l);
1,236✔
1930
#endif
1931

1932
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,236✔
1933
}
1934

1935
char** exec_context_get_syscall_log(const ExecContext *c) {
1,236✔
1936
        _cleanup_strv_free_ char **l = NULL;
1,236✔
1937

1938
        assert(c);
1,236✔
1939

1940
#if HAVE_SECCOMP
1941
        void *id, *val;
1,236✔
1942
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,236✔
1943
                char *name = NULL;
×
1944

1945
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1946
                if (!name)
×
1947
                        continue;
×
1948

1949
                if (strv_consume(&l, name) < 0)
×
1950
                        return NULL;
×
1951
        }
1952

1953
        strv_sort(l);
1,236✔
1954
#endif
1955

1956
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,236✔
1957
}
1958

1959
char** exec_context_get_address_families(const ExecContext *c) {
1,236✔
1960
        _cleanup_strv_free_ char **l = NULL;
1,236✔
1961
        void *af;
1,236✔
1962

1963
        assert(c);
1,236✔
1964

1965
        SET_FOREACH(af, c->address_families) {
1,386✔
1966
                const char *name;
150✔
1967

1968
                name = af_to_name(PTR_TO_INT(af));
150✔
1969
                if (!name)
150✔
1970
                        continue;
×
1971

1972
                if (strv_extend(&l, name) < 0)
150✔
1973
                        return NULL;
×
1974
        }
1975

1976
        strv_sort(l);
1,236✔
1977

1978
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,236✔
1979
}
1980

1981
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1,236✔
1982
        assert(c);
1,236✔
1983

1984
#if HAVE_LIBBPF
1985
        char **l = set_get_strv(c->restrict_filesystems);
1,236✔
1986
        if (!l)
1,236✔
1987
                return NULL;
1988

1989
        return strv_sort(l);
1,236✔
1990
#else
1991
        return strv_new(NULL);
1992
#endif
1993
}
1994

1995
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
4,516✔
1996
        assert(s);
4,516✔
1997

1998
        *s = (ExecStatus) {
4,516✔
1999
                .pid = pid,
2000
        };
2001

2002
        if (ts)
4,516✔
2003
                s->start_timestamp = *ts;
4,516✔
2004
        else
UNCOV
2005
                dual_timestamp_now(&s->start_timestamp);
×
2006
}
4,516✔
2007

2008
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,016✔
2009
        assert(s);
2,016✔
2010

2011
        if (s->pid != pid)
2,016✔
2012
                *s = (ExecStatus) {
6✔
2013
                        .pid = pid,
2014
                };
2015

2016
        dual_timestamp_now(&s->exit_timestamp);
2,016✔
2017

2018
        s->code = code;
2,016✔
2019
        s->status = status;
2,016✔
2020

2021
        if (context && context->utmp_id)
2,016✔
2022
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2023
}
2,016✔
2024

2025
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
7,686✔
2026
        assert(s);
7,686✔
2027
        assert(ucred);
7,686✔
2028
        assert(ts);
7,686✔
2029

2030
        if (ucred->pid != s->pid)
7,686✔
2031
                *s = (ExecStatus) {
8✔
2032
                        .pid = ucred->pid,
2033
                };
2034

2035
        s->handoff_timestamp = *ts;
7,686✔
2036
}
7,686✔
2037

2038
void exec_status_reset(ExecStatus *s) {
24,464✔
2039
        assert(s);
24,464✔
2040

2041
        *s = (ExecStatus) {};
24,464✔
2042
}
24,464✔
2043

2044
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
94✔
2045
        assert(s);
94✔
2046
        assert(f);
94✔
2047

2048
        if (s->pid <= 0)
94✔
2049
                return;
2050

2051
        prefix = strempty(prefix);
10✔
2052

2053
        fprintf(f,
10✔
2054
                "%sPID: "PID_FMT"\n",
2055
                prefix, s->pid);
2056

2057
        if (dual_timestamp_is_set(&s->start_timestamp))
10✔
2058
                fprintf(f,
10✔
2059
                        "%sStart Timestamp: %s\n",
2060
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
10✔
2061

2062
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
10✔
2063
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
10✔
2064
                fprintf(f,
10✔
2065
                        "%sHandoff Timestamp: %s since start\n",
2066
                        prefix,
2067
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
20✔
2068
        else
UNCOV
2069
                fprintf(f,
×
2070
                        "%sHandoff Timestamp: %s\n",
2071
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
×
2072

2073
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2074

UNCOV
2075
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
×
UNCOV
2076
                        fprintf(f,
×
2077
                                "%sExit Timestamp: %s since handoff\n",
2078
                                prefix,
UNCOV
2079
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
×
UNCOV
2080
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2081
                        fprintf(f,
×
2082
                                "%sExit Timestamp: %s since start\n",
2083
                                prefix,
UNCOV
2084
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2085
                else
2086
                        fprintf(f,
×
2087
                                "%sExit Timestamp: %s\n",
2088
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2089

2090
                fprintf(f,
×
2091
                        "%sExit Code: %s\n"
2092
                        "%sExit Status: %i\n",
UNCOV
2093
                        prefix, sigchld_code_to_string(s->code),
×
UNCOV
2094
                        prefix, s->status);
×
2095
        }
2096
}
2097

2098
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
94✔
2099
        _cleanup_free_ char *cmd = NULL;
188✔
2100
        const char *prefix2;
94✔
2101

2102
        assert(c);
94✔
2103
        assert(f);
94✔
2104

2105
        prefix = strempty(prefix);
94✔
2106
        prefix2 = strjoina(prefix, "\t");
470✔
2107

2108
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
94✔
2109

2110
        fprintf(f,
94✔
2111
                "%sCommand Line: %s\n",
2112
                prefix, strnull(cmd));
2113

2114
        exec_status_dump(&c->exec_status, f, prefix2);
94✔
2115
}
94✔
2116

2117
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
91✔
2118
        assert(f);
91✔
2119

2120
        prefix = strempty(prefix);
91✔
2121

2122
        LIST_FOREACH(command, i, c)
185✔
2123
                exec_command_dump(i, f, prefix);
94✔
2124
}
91✔
2125

2126
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
16,145✔
2127
        ExecCommand *end;
16,145✔
2128

2129
        assert(l);
16,145✔
2130
        assert(e);
16,145✔
2131

2132
        if (*l) {
16,145✔
2133
                /* It's kind of important, that we keep the order here */
2134
                end = LIST_FIND_TAIL(command, *l);
369✔
2135
                LIST_INSERT_AFTER(command, *l, end, e);
132✔
2136
        } else
2137
                *l = e;
16,013✔
2138
}
16,145✔
2139

2140
int exec_command_set(ExecCommand *c, const char *path, ...) {
172✔
2141
        va_list ap;
172✔
2142
        char **l, *p;
172✔
2143

2144
        assert(c);
172✔
2145
        assert(path);
172✔
2146

2147
        va_start(ap, path);
172✔
2148
        l = strv_new_ap(path, ap);
172✔
2149
        va_end(ap);
172✔
2150

2151
        if (!l)
172✔
2152
                return -ENOMEM;
172✔
2153

2154
        p = strdup(path);
172✔
2155
        if (!p) {
172✔
UNCOV
2156
                strv_free(l);
×
UNCOV
2157
                return -ENOMEM;
×
2158
        }
2159

2160
        free_and_replace(c->path, p);
172✔
2161

2162
        return strv_free_and_replace(c->argv, l);
172✔
2163
}
2164

2165
int exec_command_append(ExecCommand *c, const char *path, ...) {
247✔
2166
        char **l;
247✔
2167
        va_list ap;
247✔
2168
        int r;
247✔
2169

2170
        assert(c);
247✔
2171
        assert(path);
247✔
2172

2173
        va_start(ap, path);
247✔
2174
        l = strv_new_ap(path, ap);
247✔
2175
        va_end(ap);
247✔
2176

2177
        if (!l)
247✔
2178
                return -ENOMEM;
247✔
2179

2180
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
247✔
2181
        if (r < 0)
247✔
UNCOV
2182
                return r;
×
2183

2184
        return 0;
2185
}
2186

2187
static char *destroy_tree(char *path) {
262✔
2188
        if (!path)
262✔
2189
                return NULL;
2190

2191
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
84✔
2192
                log_debug("Spawning process to nuke '%s'", path);
84✔
2193

2194
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
84✔
2195
        }
2196

2197
        return mfree(path);
84✔
2198
}
2199

2200
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
124,050✔
2201
        assert(rt);
124,050✔
2202

2203
        if (rt->manager)
124,050✔
2204
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
167✔
2205

2206
        rt->id = mfree(rt->id);
124,050✔
2207
        rt->tmp_dir = mfree(rt->tmp_dir);
124,050✔
2208
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
124,050✔
2209
        safe_close_pair(rt->netns_storage_socket);
124,050✔
2210
        safe_close_pair(rt->ipcns_storage_socket);
124,050✔
2211
}
124,050✔
2212

2213
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
124,022✔
2214
        if (!rt)
124,022✔
2215
                return NULL;
2216

2217
        exec_shared_runtime_done(rt);
124,022✔
2218
        return mfree(rt);
124,022✔
2219
}
2220

2221
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
168✔
2222
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
127,036✔
2223

2224
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
48✔
2225
        if (!rt)
48✔
2226
                return NULL;
2227

2228
        assert(rt->n_ref > 0);
47✔
2229
        rt->n_ref--;
47✔
2230

2231
        if (rt->n_ref > 0)
47✔
2232
                return NULL;
2233

2234
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
47✔
2235
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
47✔
2236

2237
        return exec_shared_runtime_free(rt);
47✔
2238
}
2239

2240
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
124,022✔
2241
        _cleanup_free_ char *id_copy = NULL;
248,044✔
2242
        ExecSharedRuntime *n;
124,022✔
2243

2244
        assert(ret);
124,022✔
2245

2246
        id_copy = strdup(id);
124,022✔
2247
        if (!id_copy)
124,022✔
2248
                return -ENOMEM;
2249

2250
        n = new(ExecSharedRuntime, 1);
124,022✔
2251
        if (!n)
124,022✔
2252
                return -ENOMEM;
2253

2254
        *n = (ExecSharedRuntime) {
124,022✔
2255
                .id = TAKE_PTR(id_copy),
124,022✔
2256
                .netns_storage_socket = EBADF_PAIR,
2257
                .ipcns_storage_socket = EBADF_PAIR,
2258
        };
2259

2260
        *ret = n;
124,022✔
2261
        return 0;
124,022✔
2262
}
2263

2264
static int exec_shared_runtime_add(
167✔
2265
                Manager *m,
2266
                const char *id,
2267
                char **tmp_dir,
2268
                char **var_tmp_dir,
2269
                int netns_storage_socket[2],
2270
                int ipcns_storage_socket[2],
2271
                ExecSharedRuntime **ret) {
2272

2273
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
167✔
2274
        int r;
167✔
2275

2276
        assert(m);
167✔
2277
        assert(id);
167✔
2278

2279
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2280

2281
        r = exec_shared_runtime_allocate(&rt, id);
167✔
2282
        if (r < 0)
167✔
2283
                return r;
2284

2285
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
167✔
2286
        if (r < 0)
167✔
2287
                return r;
2288

2289
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
167✔
2290
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
167✔
2291
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
167✔
2292

2293
        if (netns_storage_socket) {
167✔
2294
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
167✔
2295
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
167✔
2296
        }
2297

2298
        if (ipcns_storage_socket) {
167✔
2299
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
167✔
2300
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
167✔
2301
        }
2302

2303
        rt->manager = m;
167✔
2304

2305
        if (ret)
167✔
2306
                *ret = rt;
68✔
2307
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2308
        TAKE_PTR(rt);
167✔
2309
        return 0;
167✔
2310
}
2311

2312
static int exec_shared_runtime_make(
5,930✔
2313
                Manager *m,
2314
                const ExecContext *c,
2315
                const char *id,
2316
                ExecSharedRuntime **ret) {
2317

2318
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
5,930✔
2319
        _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
11,860✔
2320
        int r;
5,930✔
2321

2322
        assert(m);
5,930✔
2323
        assert(c);
5,930✔
2324
        assert(id);
5,930✔
2325

2326
        /* It is not necessary to create ExecSharedRuntime object. */
2327
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
5,930✔
2328
                *ret = NULL;
5,862✔
2329
                return 0;
5,862✔
2330
        }
2331

2332
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
131✔
2333
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
63✔
UNCOV
2334
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
UNCOV
2335
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2336
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
63✔
2337
                if (r < 0)
63✔
2338
                        return r;
2339
        }
2340

2341
        if (exec_needs_network_namespace(c))
68✔
2342
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
6✔
UNCOV
2343
                        return -errno;
×
2344

2345
        if (exec_needs_ipc_namespace(c))
68✔
2346
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
UNCOV
2347
                        return -errno;
×
2348

2349
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
68✔
2350
        if (r < 0)
68✔
UNCOV
2351
                return r;
×
2352

2353
        return 1;
2354
}
2355

2356
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
6,029✔
2357
        ExecSharedRuntime *rt;
6,029✔
2358
        int r;
6,029✔
2359

2360
        assert(m);
6,029✔
2361
        assert(id);
6,029✔
2362
        assert(ret);
6,029✔
2363

2364
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
6,029✔
2365
        if (rt)
6,029✔
2366
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2367
                goto ref;
99✔
2368

2369
        if (!create) {
5,930✔
UNCOV
2370
                *ret = NULL;
×
UNCOV
2371
                return 0;
×
2372
        }
2373

2374
        /* If not found, then create a new object. */
2375
        r = exec_shared_runtime_make(m, c, id, &rt);
5,930✔
2376
        if (r < 0)
5,930✔
2377
                return r;
2378
        if (r == 0) {
5,930✔
2379
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2380
                *ret = NULL;
5,862✔
2381
                return 0;
5,862✔
2382
        }
2383

2384
ref:
68✔
2385
        /* increment reference counter. */
2386
        rt->n_ref++;
167✔
2387
        *ret = rt;
167✔
2388
        return 1;
167✔
2389
}
2390

2391
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
71✔
2392
        ExecSharedRuntime *rt;
71✔
2393

2394
        assert(m);
71✔
2395
        assert(f);
71✔
2396
        assert(fds);
71✔
2397

2398
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
191✔
2399
                fprintf(f, "exec-runtime=%s", rt->id);
120✔
2400

2401
                if (rt->tmp_dir)
120✔
2402
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
120✔
2403

2404
                if (rt->var_tmp_dir)
120✔
2405
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
120✔
2406

2407
                if (rt->netns_storage_socket[0] >= 0) {
120✔
2408
                        int copy;
2✔
2409

2410
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2✔
2411
                        if (copy < 0)
2✔
UNCOV
2412
                                return copy;
×
2413

2414
                        fprintf(f, " netns-socket-0=%i", copy);
2✔
2415
                }
2416

2417
                if (rt->netns_storage_socket[1] >= 0) {
120✔
2418
                        int copy;
2✔
2419

2420
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2✔
2421
                        if (copy < 0)
2✔
2422
                                return copy;
2423

2424
                        fprintf(f, " netns-socket-1=%i", copy);
2✔
2425
                }
2426

2427
                if (rt->ipcns_storage_socket[0] >= 0) {
120✔
UNCOV
2428
                        int copy;
×
2429

2430
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
UNCOV
2431
                        if (copy < 0)
×
2432
                                return copy;
2433

UNCOV
2434
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2435
                }
2436

2437
                if (rt->ipcns_storage_socket[1] >= 0) {
120✔
UNCOV
2438
                        int copy;
×
2439

2440
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
UNCOV
2441
                        if (copy < 0)
×
2442
                                return copy;
2443

UNCOV
2444
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2445
                }
2446

2447
                fputc('\n', f);
120✔
2448
        }
2449

2450
        return 0;
71✔
2451
}
2452

2453
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
126,869✔
2454
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
126,869✔
2455
        ExecSharedRuntime *rt = NULL;
126,869✔
2456
        int r;
126,869✔
2457

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

2463
        assert(u);
126,869✔
2464
        assert(key);
126,869✔
2465
        assert(value);
126,869✔
2466

2467
        /* Manager manages ExecSharedRuntime objects by the unit id.
2468
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2469
        if (isempty(u->id)) {
126,869✔
UNCOV
2470
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
UNCOV
2471
                return 0;
×
2472
        }
2473

2474
        if (u->manager) {
126,869✔
2475
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
126,869✔
UNCOV
2476
                        return log_oom();
×
2477

2478
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
126,869✔
2479
        }
2480
        if (!rt) {
126,869✔
2481
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
123,855✔
UNCOV
2482
                        return log_oom();
×
2483

2484
                rt = rt_create;
123,855✔
2485
        }
2486

2487
        if (streq(key, "tmp-dir")) {
126,869✔
UNCOV
2488
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2489
                        return -ENOMEM;
2490

2491
        } else if (streq(key, "var-tmp-dir")) {
126,869✔
UNCOV
2492
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2493
                        return -ENOMEM;
2494

2495
        } else if (streq(key, "netns-socket-0")) {
126,869✔
2496

UNCOV
2497
                safe_close(rt->netns_storage_socket[0]);
×
UNCOV
2498
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2499
                if (rt->netns_storage_socket[0] < 0)
×
2500
                        return 0;
2501

2502
        } else if (streq(key, "netns-socket-1")) {
126,869✔
2503

UNCOV
2504
                safe_close(rt->netns_storage_socket[1]);
×
UNCOV
2505
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2506
                if (rt->netns_storage_socket[1] < 0)
×
2507
                        return 0;
2508
        } else
2509
                return 0;
2510

2511
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
UNCOV
2512
        if (rt_create && u->manager) {
×
UNCOV
2513
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
2514
                if (r < 0) {
×
2515
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
2516
                        return 0;
×
2517
                }
2518

UNCOV
2519
                rt_create->manager = u->manager;
×
2520

2521
                /* Avoid cleanup */
UNCOV
2522
                TAKE_PTR(rt_create);
×
2523
        }
2524

2525
        return 1;
2526
}
2527

2528
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
99✔
2529
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
99✔
2530
        char *id = NULL;
99✔
2531
        int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
99✔
2532
        const char *p, *v = ASSERT_PTR(value);
99✔
2533
        size_t n;
99✔
2534

2535
        assert(m);
99✔
2536
        assert(fds);
99✔
2537

2538
        n = strcspn(v, " ");
99✔
2539
        id = strndupa_safe(v, n);
99✔
2540
        if (v[n] != ' ')
99✔
UNCOV
2541
                goto finalize;
×
2542
        p = v + n + 1;
99✔
2543

2544
        v = startswith(p, "tmp-dir=");
99✔
2545
        if (v) {
99✔
2546
                n = strcspn(v, " ");
99✔
2547
                tmp_dir = strndup(v, n);
99✔
2548
                if (!tmp_dir)
99✔
UNCOV
2549
                        return log_oom();
×
2550
                if (v[n] != ' ')
99✔
2551
                        goto finalize;
×
2552
                p = v + n + 1;
99✔
2553
        }
2554

2555
        v = startswith(p, "var-tmp-dir=");
99✔
2556
        if (v) {
99✔
2557
                n = strcspn(v, " ");
99✔
2558
                var_tmp_dir = strndup(v, n);
99✔
2559
                if (!var_tmp_dir)
99✔
UNCOV
2560
                        return log_oom();
×
2561
                if (v[n] != ' ')
99✔
2562
                        goto finalize;
98✔
2563
                p = v + n + 1;
1✔
2564
        }
2565

2566
        v = startswith(p, "netns-socket-0=");
1✔
2567
        if (v) {
1✔
2568
                char *buf;
1✔
2569

2570
                n = strcspn(v, " ");
1✔
2571
                buf = strndupa_safe(v, n);
1✔
2572

2573
                netns_fdpair[0] = deserialize_fd(fds, buf);
1✔
2574
                if (netns_fdpair[0] < 0)
1✔
2575
                        return netns_fdpair[0];
2576
                if (v[n] != ' ')
1✔
UNCOV
2577
                        goto finalize;
×
2578
                p = v + n + 1;
1✔
2579
        }
2580

2581
        v = startswith(p, "netns-socket-1=");
1✔
2582
        if (v) {
1✔
2583
                char *buf;
1✔
2584

2585
                n = strcspn(v, " ");
1✔
2586
                buf = strndupa_safe(v, n);
1✔
2587

2588
                netns_fdpair[1] = deserialize_fd(fds, buf);
1✔
2589
                if (netns_fdpair[1] < 0)
1✔
2590
                        return netns_fdpair[1];
2591
                if (v[n] != ' ')
1✔
2592
                        goto finalize;
1✔
UNCOV
2593
                p = v + n + 1;
×
2594
        }
2595

UNCOV
2596
        v = startswith(p, "ipcns-socket-0=");
×
UNCOV
2597
        if (v) {
×
2598
                char *buf;
×
2599

2600
                n = strcspn(v, " ");
×
UNCOV
2601
                buf = strndupa_safe(v, n);
×
2602

2603
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
UNCOV
2604
                if (ipcns_fdpair[0] < 0)
×
2605
                        return ipcns_fdpair[0];
2606
                if (v[n] != ' ')
×
UNCOV
2607
                        goto finalize;
×
2608
                p = v + n + 1;
×
2609
        }
2610

UNCOV
2611
        v = startswith(p, "ipcns-socket-1=");
×
UNCOV
2612
        if (v) {
×
2613
                char *buf;
×
2614

2615
                n = strcspn(v, " ");
×
UNCOV
2616
                buf = strndupa_safe(v, n);
×
2617

2618
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
UNCOV
2619
                if (ipcns_fdpair[1] < 0)
×
2620
                        return ipcns_fdpair[1];
2621
        }
2622

UNCOV
2623
finalize:
×
2624
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
99✔
2625
        if (r < 0)
99✔
UNCOV
2626
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2627
        return 0;
2628
}
2629

2630
void exec_shared_runtime_vacuum(Manager *m) {
1,436✔
2631
        ExecSharedRuntime *rt;
1,436✔
2632

2633
        assert(m);
1,436✔
2634

2635
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2636

2637
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,535✔
2638
                if (rt->n_ref > 0)
99✔
2639
                        continue;
99✔
2640

UNCOV
2641
                (void) exec_shared_runtime_free(rt);
×
2642
        }
2643
}
1,436✔
2644

2645
int exec_runtime_make(
6,029✔
2646
                const Unit *unit,
2647
                const ExecContext *context,
2648
                ExecSharedRuntime *shared,
2649
                DynamicCreds *creds,
2650
                ExecRuntime **ret) {
2651
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
6,029✔
2652
        _cleanup_free_ char *ephemeral = NULL;
6,029✔
2653
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
6,029✔
2654
        int r;
6,029✔
2655

2656
        assert(unit);
6,029✔
2657
        assert(context);
6,029✔
2658
        assert(ret);
6,029✔
2659

2660
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
6,029✔
2661
                *ret = NULL;
5,861✔
2662
                return 0;
5,861✔
2663
        }
2664

2665
        if (exec_needs_ephemeral(context)) {
168✔
UNCOV
2666
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
UNCOV
2667
                if (r < 0)
×
2668
                        return r;
2669

UNCOV
2670
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
UNCOV
2671
                if (r < 0)
×
2672
                        return r;
2673

UNCOV
2674
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
UNCOV
2675
                        return -errno;
×
2676
        }
2677

2678
        rt = new(ExecRuntime, 1);
168✔
2679
        if (!rt)
168✔
2680
                return -ENOMEM;
2681

2682
        *rt = (ExecRuntime) {
168✔
2683
                .shared = shared,
2684
                .dynamic_creds = creds,
2685
                .ephemeral_copy = TAKE_PTR(ephemeral),
168✔
2686
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
168✔
2687
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
168✔
2688
        };
2689

2690
        *ret = TAKE_PTR(rt);
168✔
2691
        return 1;
168✔
2692
}
2693

2694
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
45,415✔
2695
        if (!rt)
45,415✔
2696
                return NULL;
2697

2698
        exec_shared_runtime_unref(rt->shared);
168✔
2699
        dynamic_creds_unref(rt->dynamic_creds);
168✔
2700

2701
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
168✔
2702

2703
        safe_close_pair(rt->ephemeral_storage_socket);
168✔
2704
        return mfree(rt);
168✔
2705
}
2706

2707
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
4,928✔
2708
        if (!rt)
4,928✔
2709
                return NULL;
2710

2711
        rt->shared = exec_shared_runtime_destroy(rt->shared);
48✔
2712
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
48✔
2713
        return exec_runtime_free(rt);
48✔
2714
}
2715

2716
void exec_runtime_clear(ExecRuntime *rt) {
28✔
2717
        if (!rt)
28✔
2718
                return;
2719

2720
        safe_close_pair(rt->ephemeral_storage_socket);
28✔
2721
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
28✔
2722
}
2723

2724
void exec_params_shallow_clear(ExecParameters *p) {
2,213✔
2725
        if (!p)
2,213✔
2726
                return;
2727

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

2731
        p->environment = strv_free(p->environment);
2,213✔
2732
        p->fd_names = strv_free(p->fd_names);
2,213✔
2733
        p->files_env = strv_free(p->files_env);
2,213✔
2734
        p->fds = mfree(p->fds);
2,213✔
2735
        p->exec_fd = safe_close(p->exec_fd);
2,213✔
2736
        p->user_lookup_fd = -EBADF;
2,213✔
2737
        p->bpf_restrict_fs_map_fd = -EBADF;
2,213✔
2738
        p->unit_id = mfree(p->unit_id);
2,213✔
2739
        p->invocation_id = SD_ID128_NULL;
2,213✔
2740
        p->invocation_id_string[0] = '\0';
2,213✔
2741
        p->confirm_spawn = mfree(p->confirm_spawn);
2,213✔
2742
}
2743

2744
void exec_params_deep_clear(ExecParameters *p) {
28✔
2745
        if (!p)
28✔
2746
                return;
2747

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

2752
        close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
28✔
2753

2754
        p->cgroup_path = mfree(p->cgroup_path);
28✔
2755

2756
        if (p->prefix) {
28✔
2757
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
28✔
2758
                p->prefix = mfree(p->prefix);
28✔
2759
        }
2760

2761
        p->received_credentials_directory = mfree(p->received_credentials_directory);
28✔
2762
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
28✔
2763

2764
        if (p->idle_pipe) {
28✔
UNCOV
2765
                close_many_and_free(p->idle_pipe, 4);
×
UNCOV
2766
                p->idle_pipe = NULL;
×
2767
        }
2768

2769
        p->stdin_fd = safe_close(p->stdin_fd);
28✔
2770
        p->stdout_fd = safe_close(p->stdout_fd);
28✔
2771
        p->stderr_fd = safe_close(p->stderr_fd);
28✔
2772

2773
        p->notify_socket = mfree(p->notify_socket);
28✔
2774

2775
        open_file_free_many(&p->open_files);
28✔
2776

2777
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
28✔
2778

2779
        exec_params_shallow_clear(p);
28✔
2780
}
2781

2782
void exec_directory_done(ExecDirectory *d) {
226,975✔
2783
        if (!d)
226,975✔
2784
                return;
2785

2786
        FOREACH_ARRAY(i, d->items, d->n_items) {
228,736✔
2787
                free(i->path);
1,761✔
2788
                strv_free(i->symlinks);
1,761✔
2789
        }
2790

2791
        d->items = mfree(d->items);
226,975✔
2792
        d->n_items = 0;
226,975✔
2793
        d->mode = 0755;
226,975✔
2794
}
2795

2796
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
5,468✔
2797
        assert(d);
5,468✔
2798
        assert(path);
5,468✔
2799

2800
        FOREACH_ARRAY(i, d->items, d->n_items)
7,792✔
2801
                if (path_equal(i->path, path))
2,339✔
2802
                        return i;
2803

2804
        return NULL;
2805
}
2806

2807
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
5,468✔
UNCOV
2808
        _cleanup_strv_free_ char **s = NULL;
×
2809
        _cleanup_free_ char *p = NULL;
5,468✔
2810
        ExecDirectoryItem *existing;
5,468✔
2811
        int r;
5,468✔
2812

2813
        assert(d);
5,468✔
2814
        assert(path);
5,468✔
2815

2816
        existing = exec_directory_find(d, path);
5,468✔
2817
        if (existing) {
5,468✔
2818
                r = strv_extend(&existing->symlinks, symlink);
15✔
2819
                if (r < 0)
15✔
2820
                        return r;
2821

2822
                existing->flags |= flags;
15✔
2823

2824
                return 0; /* existing item is updated */
15✔
2825
        }
2826

2827
        p = strdup(path);
5,453✔
2828
        if (!p)
5,453✔
2829
                return -ENOMEM;
2830

2831
        if (symlink) {
5,453✔
2832
                s = strv_new(symlink);
6✔
2833
                if (!s)
6✔
2834
                        return -ENOMEM;
2835
        }
2836

2837
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
5,453✔
2838
                return -ENOMEM;
2839

2840
        d->items[d->n_items++] = (ExecDirectoryItem) {
5,453✔
2841
                .path = TAKE_PTR(p),
5,453✔
2842
                .symlinks = TAKE_PTR(s),
5,453✔
2843
                .flags = flags,
2844
        };
2845

2846
        return 1; /* new item is added */
5,453✔
2847
}
2848

2849
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
876✔
2850
        assert(a);
876✔
2851
        assert(b);
876✔
2852

2853
        return path_compare(a->path, b->path);
876✔
2854
}
2855

2856
void exec_directory_sort(ExecDirectory *d) {
124,111✔
2857
        assert(d);
124,111✔
2858

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

2864
        if (d->n_items <= 1)
124,111✔
2865
                return;
2866

2867
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
158✔
2868

2869
        for (size_t i = 1; i < d->n_items; i++)
685✔
2870
                for (size_t j = 0; j < i; j++)
1,752✔
2871
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,240✔
2872
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
2873
                                break;
15✔
2874
                        }
2875
}
2876

UNCOV
2877
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
UNCOV
2878
        ExecDirectoryType t;
×
2879

2880
        assert(s);
×
2881

2882
        if (streq(s, "all"))
×
2883
                return EXEC_CLEAN_ALL;
2884
        if (streq(s, "fdstore"))
×
2885
                return EXEC_CLEAN_FDSTORE;
2886

UNCOV
2887
        t = exec_resource_type_from_string(s);
×
UNCOV
2888
        if (t < 0)
×
2889
                return (ExecCleanMask) t;
2890

UNCOV
2891
        return 1U << t;
×
2892
}
2893

2894
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
2895
        [EXEC_INPUT_NULL]      = "null",
2896
        [EXEC_INPUT_TTY]       = "tty",
2897
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
2898
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
2899
        [EXEC_INPUT_SOCKET]    = "socket",
2900
        [EXEC_INPUT_NAMED_FD]  = "fd",
2901
        [EXEC_INPUT_DATA]      = "data",
2902
        [EXEC_INPUT_FILE]      = "file",
2903
};
2904

2905
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
15,677✔
2906

2907
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
2908
        [EXEC_OUTPUT_INHERIT]             = "inherit",
2909
        [EXEC_OUTPUT_NULL]                = "null",
2910
        [EXEC_OUTPUT_TTY]                 = "tty",
2911
        [EXEC_OUTPUT_KMSG]                = "kmsg",
2912
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
2913
        [EXEC_OUTPUT_JOURNAL]             = "journal",
2914
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
2915
        [EXEC_OUTPUT_SOCKET]              = "socket",
2916
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
2917
        [EXEC_OUTPUT_FILE]                = "file",
2918
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
2919
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
2920
};
2921

2922
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
31,542✔
2923

2924
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
2925
        [EXEC_UTMP_INIT]  = "init",
2926
        [EXEC_UTMP_LOGIN] = "login",
2927
        [EXEC_UTMP_USER]  = "user",
2928
};
2929

2930
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
14,931✔
2931

2932
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
2933
        [EXEC_PRESERVE_NO]      = "no",
2934
        [EXEC_PRESERVE_YES]     = "yes",
2935
        [EXEC_PRESERVE_RESTART] = "restart",
2936
};
2937

2938
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
16,893✔
2939

2940
/* This table maps ExecDirectoryType to the setting it is configured with in the unit */
2941
static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2942
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectory",
2943
        [EXEC_DIRECTORY_STATE]         = "StateDirectory",
2944
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectory",
2945
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectory",
2946
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectory",
2947
};
2948

2949
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
137,678✔
2950

2951
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
2952
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2953
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
2954
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
2955
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
2956
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
2957
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
2958
};
2959

2960
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
2961

2962
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2963
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
2964
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
2965
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
2966
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
2967
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
2968
};
2969

UNCOV
2970
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
2971

2972
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
2973
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
2974
 * directories, specifically .timer units with their timestamp touch file. */
2975
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2976
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
2977
        [EXEC_DIRECTORY_STATE]         = "state",
2978
        [EXEC_DIRECTORY_CACHE]         = "cache",
2979
        [EXEC_DIRECTORY_LOGS]          = "logs",
2980
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
2981
};
2982

2983
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
243✔
2984

2985
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
2986
        [EXEC_KEYRING_INHERIT] = "inherit",
2987
        [EXEC_KEYRING_PRIVATE] = "private",
2988
        [EXEC_KEYRING_SHARED]  = "shared",
2989
};
2990

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