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

systemd / systemd / 15150396955

20 May 2025 10:32PM UTC coverage: 72.047% (-0.2%) from 72.25%
15150396955

push

github

web-flow
resolved: add new "DNS Delegate" concepts (#34368)

Various long standing issues (at least: #5573 #14159 #20485 #21260
#24532 #32022 #18056) have been asking for a way to delegate DNS
resolution of specific domains to very specific DNS servers.

This PR goes a major step towards that goal by adding a new concept "DNS
Delegate" which allows to configure just that. Basically, this adds a
third kind of DNS scope to resolved's logic: besides the per-link and
global DNS scopes there are now also "delegate" scopes, which can be
created by dropping in a new file /etc/systemd/dns-delegate/*.conf. They
carry DNS= and Domains= lines just like the global setting or what the
per-link configuration can carry.

And they are consulted the same way as link DNS scopes are considered,
following the same routing rules.

This allows to configure these DNS delegates statically via drop-in
files as mentioned, and only adds the most basic functionality. Later on
we might want to extend this:

1. Allow dynamic creation of DNS delegates via IPC with lifecycle bound
to IPC client (usecase: installing a DNS delegate that routes traffic to
some DNS-over-TLS server once basic setup is complete).
2. Allow configuration of protocol details per delegate the same way
this is currently allowed per-link.
3. Instead of strictly using DNS as delegation protocol, support an
alternative varlink based protocol (without retransmission problems and
so on) that systemd-machined and similar can implement.

This PR is not complete yet. Lacks docs and tests. Seems to work fine in
my local tests however.

Fixes: #5573
Fixes: #18056
Fixes: #20485

470 of 586 new or added lines in 14 files covered. (80.2%)

3358 existing lines in 54 files now uncovered.

299091 of 415134 relevant lines covered (72.05%)

703065.7 hits per line

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

76.47
/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 "dynamic-user.h"
29
#include "env-file.h"
30
#include "env-util.h"
31
#include "errno-list.h"
32
#include "escape.h"
33
#include "exec-credential.h"
34
#include "execute.h"
35
#include "execute-serialize.h"
36
#include "exit-status.h"
37
#include "fd-util.h"
38
#include "fileio.h"
39
#include "format-util.h"
40
#include "glob-util.h"
41
#include "hexdecoct.h"
42
#include "io-util.h"
43
#include "ioprio-util.h"
44
#include "lock-util.h"
45
#include "log.h"
46
#include "macro.h"
47
#include "manager.h"
48
#include "manager-dump.h"
49
#include "memory-util.h"
50
#include "missing_fs.h"
51
#include "mkdir-label.h"
52
#include "namespace.h"
53
#include "osc-context.h"
54
#include "parse-util.h"
55
#include "path-util.h"
56
#include "process-util.h"
57
#include "rlimit-util.h"
58
#include "rm-rf.h"
59
#include "seccomp-util.h"
60
#include "securebits-util.h"
61
#include "selinux-util.h"
62
#include "serialize.h"
63
#include "sort-util.h"
64
#include "special.h"
65
#include "stat-util.h"
66
#include "string-table.h"
67
#include "string-util.h"
68
#include "strv.h"
69
#include "syslog-util.h"
70
#include "terminal-util.h"
71
#include "tmpfile-util.h"
72
#include "umask-util.h"
73
#include "unit-serialize.h"
74
#include "user-util.h"
75
#include "utmp-wtmp.h"
76
#include "vpick.h"
77

78
static bool is_terminal_input(ExecInput i) {
34,655✔
79
        return IN_SET(i,
34,655✔
80
                      EXEC_INPUT_TTY,
81
                      EXEC_INPUT_TTY_FORCE,
82
                      EXEC_INPUT_TTY_FAIL);
83
}
84

85
static bool is_terminal_output(ExecOutput o) {
68,249✔
86
        return IN_SET(o,
68,249✔
87
                      EXEC_OUTPUT_TTY,
88
                      EXEC_OUTPUT_KMSG_AND_CONSOLE,
89
                      EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
90
}
91

92
const char* exec_context_tty_path(const ExecContext *context) {
14,779✔
93
        assert(context);
14,779✔
94

95
        if (context->stdio_as_fds)
14,779✔
96
                return NULL;
97

98
        if (context->tty_path)
14,114✔
99
                return context->tty_path;
711✔
100

101
        return "/dev/console";
102
}
103

104
int exec_context_apply_tty_size(
1,835✔
105
                const ExecContext *context,
106
                int input_fd,
107
                int output_fd,
108
                const char *tty_path) {
109

110
        unsigned rows, cols;
1,835✔
111
        int r;
1,835✔
112

113
        assert(context);
1,835✔
114
        assert(input_fd >= 0);
1,835✔
115
        assert(output_fd >= 0);
1,835✔
116

117
        if (!isatty_safe(output_fd))
1,835✔
118
                return 0;
1,835✔
119

120
        if (!tty_path)
1,086✔
121
                tty_path = exec_context_tty_path(context);
420✔
122

123
        /* Preferably use explicitly configured data */
124
        rows = context->tty_rows;
1,086✔
125
        cols = context->tty_cols;
1,086✔
126

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

134
        /* If we got nothing so far and we are talking to a physical device, then let's query dimensions from
135
         * the ANSI terminal driver. Note that we will not bother with this in case terminal reset via ansi
136
         * sequences is not enabled, as the DSR logic relies on ANSI sequences after all, and if we shall not
137
         * use those during initialization we need to skip it. */
138
        if (rows == UINT_MAX && cols == UINT_MAX &&
1,301✔
139
            exec_context_shall_ansi_seq_reset(context) &&
402✔
140
            isatty_safe(input_fd)) {
187✔
141
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
187✔
142
                if (r < 0)
187✔
143
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
187✔
144
        }
145

146
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
1,086✔
147
}
148

149
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p, sd_id128_t invocation_id) {
13,100✔
150
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
26,200✔
151
        int fd, r;
13,100✔
152

153
        assert(context);
13,100✔
154

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

160
        const char *path = exec_context_tty_path(context);
13,100✔
161

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

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

183
        if (context->tty_reset)
682✔
184
                (void) terminal_reset_defensive(
179✔
185
                                fd,
186
                                TERMINAL_RESET_SWITCH_TO_TEXT |
187
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
176✔
188

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

193
        if (!sd_id128_is_null(invocation_id)) {
1,315✔
194
                sd_id128_t context_id;
49✔
195

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

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

210
        if (context->tty_vhangup)
682✔
211
                (void) terminal_vhangup_fd(fd);
171✔
212

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

217
        if (context->tty_vt_disallocate && path)
682✔
218
                (void) vt_disallocate(path);
93✔
219
}
220

221
bool exec_needs_network_namespace(const ExecContext *context) {
56,681✔
222
        assert(context);
56,681✔
223

224
        return context->private_network || context->network_namespace_path;
56,681✔
225
}
226

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

231
bool exec_needs_ipc_namespace(const ExecContext *context) {
52,491✔
232
        assert(context);
52,491✔
233

234
        return context->private_ipc || context->ipc_namespace_path;
52,491✔
235
}
236

237
static bool needs_cgroup_namespace(ProtectControlGroups i) {
91,380✔
238
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
91,380✔
239
}
240

241
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
61,007✔
242
        assert(context);
61,007✔
243

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

257
bool exec_needs_cgroup_namespace(const ExecContext *context) {
30,373✔
258
        assert(context);
30,373✔
259

260
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
30,373✔
261
}
262

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

266
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
26,602✔
267
}
268

269
bool exec_is_cgroup_mount_read_only(const ExecContext *context) {
2,016✔
270
        assert(context);
2,016✔
271

272
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
2,016✔
273
}
274

275
bool exec_needs_pid_namespace(const ExecContext *context) {
73,687✔
276
        assert(context);
73,687✔
277

278
        return context->private_pids != PRIVATE_PIDS_NO && namespace_type_supported(NAMESPACE_PID);
73,687✔
279
}
280

281
bool exec_needs_mount_namespace(
31,825✔
282
                const ExecContext *context,
283
                const ExecParameters *params,
284
                const ExecRuntime *runtime) {
285

286
        assert(context);
31,825✔
287

288
        if (context->root_image)
31,825✔
289
                return true;
290

291
        if (!strv_isempty(context->read_write_paths) ||
31,806✔
292
            !strv_isempty(context->read_only_paths) ||
29,734✔
293
            !strv_isempty(context->inaccessible_paths) ||
29,727✔
294
            !strv_isempty(context->exec_paths) ||
29,708✔
295
            !strv_isempty(context->no_exec_paths))
29,708✔
296
                return true;
297

298
        if (context->n_bind_mounts > 0)
29,708✔
299
                return true;
300

301
        if (context->n_temporary_filesystems > 0)
29,651✔
302
                return true;
303

304
        if (context->n_mount_images > 0)
29,492✔
305
                return true;
306

307
        if (context->n_extension_images > 0)
29,469✔
308
                return true;
309

310
        if (!strv_isempty(context->extension_directories))
29,458✔
311
                return true;
312

313
        if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
29,453✔
314
                return true;
315

316
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
29,453✔
317
                return true;
318

319
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
28,426✔
320
                return true;
321

322
        if (context->private_devices ||
27,946✔
323
            context->private_mounts > 0 ||
27,206✔
324
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
26,776✔
325
            context->protect_system != PROTECT_SYSTEM_NO ||
26,747✔
326
            context->protect_home != PROTECT_HOME_NO ||
26,747✔
327
            context->protect_kernel_tunables ||
26,747✔
328
            context->protect_kernel_modules ||
26,747✔
329
            context->protect_kernel_logs ||
50,133✔
330
            exec_needs_cgroup_mount(context) ||
25,065✔
331
            context->protect_proc != PROTECT_PROC_DEFAULT ||
25,047✔
332
            context->proc_subset != PROC_SUBSET_ALL ||
49,977✔
333
            exec_needs_ipc_namespace(context) ||
49,968✔
334
            exec_needs_pid_namespace(context))
24,984✔
335
                return true;
2,997✔
336

337
        if (context->root_directory) {
24,949✔
338
                if (exec_context_get_effective_mount_apivfs(context))
5✔
339
                        return true;
340

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

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

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

356
        if (exec_context_get_effective_bind_log_sockets(context))
24,944✔
357
                return true;
358

359
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
149,320✔
360
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
129,257✔
361
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
4,875✔
362
                                return true;
363

364
        return false;
365
}
366

367
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
3,911✔
368
        assert(context);
3,911✔
369
        assert(params);
3,911✔
370

371
        if (!params->notify_socket)
3,911✔
372
                return NULL;
373

374
        if (!needs_sandboxing)
3,257✔
375
                return NULL;
376

377
        if (!context->root_directory && !context->root_image)
3,257✔
378
                return NULL;
379

UNCOV
380
        if (!exec_context_get_effective_mount_apivfs(context))
×
381
                return NULL;
382

383
        if (!FLAGS_SET(params->flags, EXEC_APPLY_CHROOT))
×
UNCOV
384
                return NULL;
×
385

386
        return "/run/host/notify";
387
}
388

389
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
11,871✔
390
        assert(context);
11,871✔
391

392
        if (!context->dynamic_user)
11,871✔
393
                return false;
394

395
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
104✔
396
                return false;
397

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

401
        return true;
402
}
403

404
int exec_params_get_cgroup_path(
14,470✔
405
                const ExecParameters *params,
406
                const CGroupContext *c,
407
                char **ret) {
408

409
        const char *subgroup = NULL;
14,470✔
410
        char *p;
14,470✔
411

412
        assert(params);
14,470✔
413
        assert(ret);
14,470✔
414

415
        if (!params->cgroup_path)
14,470✔
416
                return -EINVAL;
417

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

427
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
14,470✔
428
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
710✔
429
                        subgroup = ".control";
430
                else
431
                        subgroup = c->delegate_subgroup;
676✔
432
        }
433

434
        if (subgroup)
676✔
435
                p = path_join(params->cgroup_path, subgroup);
710✔
436
        else
437
                p = strdup(params->cgroup_path);
13,760✔
438
        if (!p)
14,470✔
439
                return -ENOMEM;
440

441
        *ret = p;
14,470✔
442
        return !!subgroup;
14,470✔
443
}
444

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

448
        return c->cpu_affinity_from_numa;
1,260✔
449
}
450

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

456
        if (!DEBUG_LOGGING)
2,230✔
457
                return;
2,230✔
458

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

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

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

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

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

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

495
        LOG_CONTEXT_PUSH_UNIT(unit);
4,460✔
496

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

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

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

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

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

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

530
        fdset = fdset_new();
2,230✔
531
        if (!fdset)
2,230✔
UNCOV
532
                return log_oom();
×
533

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

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

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

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

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

557
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,230✔
558
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,230✔
559

560
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,230✔
561
        dual_timestamp start_timestamp;
2,230✔
562

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

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

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

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

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

601
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
2,230✔
602
                       command->path, pidref.pid, r > 0 ? "via" : "without");
603

604
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,230✔
605

606
        *ret = TAKE_PIDREF(pidref);
2,230✔
607
        return 0;
2,230✔
608
}
609

610
void exec_context_init(ExecContext *c) {
58,605✔
611
        assert(c);
58,605✔
612

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

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

643
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
351,630✔
644
                d->mode = 0755;
293,025✔
645

646
        numa_policy_reset(&c->numa_policy);
58,605✔
647

648
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
58,605✔
649
}
58,605✔
650

651
void exec_context_done(ExecContext *c) {
47,062✔
652
        assert(c);
47,062✔
653

654
        c->environment = strv_free(c->environment);
47,062✔
655
        c->environment_files = strv_free(c->environment_files);
47,062✔
656
        c->pass_environment = strv_free(c->pass_environment);
47,062✔
657
        c->unset_environment = strv_free(c->unset_environment);
47,062✔
658

659
        rlimit_free_all(c->rlimit);
47,062✔
660

661
        for (size_t l = 0; l < 3; l++) {
188,248✔
662
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
141,186✔
663
                c->stdio_file[l] = mfree(c->stdio_file[l]);
141,186✔
664
        }
665

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

684
        c->supplementary_groups = strv_free(c->supplementary_groups);
47,062✔
685

686
        c->pam_name = mfree(c->pam_name);
47,062✔
687

688
        c->read_only_paths = strv_free(c->read_only_paths);
47,062✔
689
        c->read_write_paths = strv_free(c->read_write_paths);
47,062✔
690
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
47,062✔
691
        c->exec_paths = strv_free(c->exec_paths);
47,062✔
692
        c->no_exec_paths = strv_free(c->no_exec_paths);
47,062✔
693
        c->exec_search_path = strv_free(c->exec_search_path);
47,062✔
694

695
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
47,062✔
696
        c->bind_mounts = NULL;
47,062✔
697
        c->n_bind_mounts = 0;
47,062✔
698
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
47,062✔
699
        c->temporary_filesystems = NULL;
47,062✔
700
        c->n_temporary_filesystems = 0;
47,062✔
701
        c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
47,062✔
702

703
        cpu_set_reset(&c->cpu_set);
47,062✔
704
        numa_policy_reset(&c->numa_policy);
47,062✔
705

706
        c->utmp_id = mfree(c->utmp_id);
47,062✔
707
        c->selinux_context = mfree(c->selinux_context);
47,062✔
708
        c->apparmor_profile = mfree(c->apparmor_profile);
47,062✔
709
        c->smack_process_label = mfree(c->smack_process_label);
47,062✔
710

711
        c->restrict_filesystems = set_free(c->restrict_filesystems);
47,062✔
712

713
        c->syscall_filter = hashmap_free(c->syscall_filter);
47,062✔
714
        c->syscall_archs = set_free(c->syscall_archs);
47,062✔
715
        c->syscall_log = hashmap_free(c->syscall_log);
47,062✔
716
        c->address_families = set_free(c->address_families);
47,062✔
717

718
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
282,372✔
719
                exec_directory_done(d);
235,310✔
720

721
        c->log_level_max = -1;
47,062✔
722

723
        exec_context_free_log_extra_fields(c);
47,062✔
724
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
47,062✔
725
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
47,062✔
726

727
        c->log_ratelimit = (RateLimit) {};
47,062✔
728

729
        c->stdin_data = mfree(c->stdin_data);
47,062✔
730
        c->stdin_data_size = 0;
47,062✔
731

732
        c->network_namespace_path = mfree(c->network_namespace_path);
47,062✔
733
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
47,062✔
734

735
        c->log_namespace = mfree(c->log_namespace);
47,062✔
736

737
        c->load_credentials = hashmap_free(c->load_credentials);
47,062✔
738
        c->set_credentials = hashmap_free(c->set_credentials);
47,062✔
739
        c->import_credentials = ordered_set_free(c->import_credentials);
47,062✔
740

741
        c->root_image_policy = image_policy_free(c->root_image_policy);
47,062✔
742
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
47,062✔
743
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
47,062✔
744

745
        c->private_hostname = mfree(c->private_hostname);
47,062✔
746
}
47,062✔
747

748
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
5,036✔
749
        assert(c);
5,036✔
750

751
        if (!runtime_prefix)
5,036✔
752
                return 0;
753

754
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
5,049✔
755
                _cleanup_free_ char *p = NULL;
13✔
756

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

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

768
                STRV_FOREACH(symlink, i->symlinks) {
13✔
UNCOV
769
                        _cleanup_free_ char *symlink_abs = NULL;
×
770

771
                        if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
×
UNCOV
772
                                symlink_abs = path_join(runtime_prefix, "private", *symlink);
×
773
                        else
774
                                symlink_abs = path_join(runtime_prefix, *symlink);
×
775
                        if (!symlink_abs)
×
UNCOV
776
                                return -ENOMEM;
×
777

UNCOV
778
                        (void) unlink(symlink_abs);
×
779
                }
780
        }
781

782
        return 0;
783
}
784

785
int exec_context_destroy_mount_ns_dir(Unit *u) {
11,082✔
786
        _cleanup_free_ char *p = NULL;
11,082✔
787

788
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
11,082✔
789
                return 0;
790

791
        p = path_join("/run/systemd/propagate/", u->id);
2,057✔
792
        if (!p)
2,057✔
793
                return -ENOMEM;
794

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

799
        return 0;
800
}
801

802
void exec_command_done(ExecCommand *c) {
100,284✔
803
        assert(c);
100,284✔
804

805
        c->path = mfree(c->path);
100,284✔
806
        c->argv = strv_free(c->argv);
100,284✔
807
}
100,284✔
808

809
void exec_command_done_array(ExecCommand *c, size_t n) {
27,839✔
810
        FOREACH_ARRAY(i, c, n)
111,354✔
811
                exec_command_done(i);
83,515✔
812
}
27,839✔
813

814
ExecCommand* exec_command_free(ExecCommand *c) {
16,741✔
815
        if (!c)
16,741✔
816
                return NULL;
817

818
        exec_command_done(c);
16,741✔
819
        return mfree(c);
16,741✔
820
}
821

822
ExecCommand* exec_command_free_list(ExecCommand *c) {
123,612✔
823
        ExecCommand *i;
123,612✔
824

825
        while ((i = LIST_POP(command, c)))
140,353✔
826
                exec_command_free(i);
16,741✔
827

828
        return NULL;
123,612✔
829
}
830

831
void exec_command_free_array(ExecCommand **c, size_t n) {
19,195✔
832
        FOREACH_ARRAY(i, c, n)
142,792✔
833
                *i = exec_command_free_list(*i);
123,597✔
834
}
19,195✔
835

836
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
6,978✔
837
        FOREACH_ARRAY(i, c, n)
27,911✔
838
                exec_status_reset(&i->exec_status);
20,933✔
839
}
6,978✔
840

841
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
4,306✔
842
        FOREACH_ARRAY(i, c, n)
29,716✔
843
                LIST_FOREACH(command, z, *i)
27,617✔
844
                        exec_status_reset(&z->exec_status);
2,207✔
845
}
4,306✔
846

847
typedef struct InvalidEnvInfo {
848
        const Unit *unit;
849
        const char *path;
850
} InvalidEnvInfo;
851

852
static void invalid_env(const char *p, void *userdata) {
×
UNCOV
853
        InvalidEnvInfo *info = userdata;
×
854

855
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
UNCOV
856
}
×
857

858
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
38,496✔
859
        assert(c);
38,496✔
860

861
        switch (fd_index) {
38,496✔
862

863
        case STDIN_FILENO:
12,832✔
864
                if (c->std_input != EXEC_INPUT_NAMED_FD)
12,832✔
865
                        return NULL;
866

UNCOV
867
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
868

869
        case STDOUT_FILENO:
12,832✔
870
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
12,832✔
871
                        return NULL;
872

UNCOV
873
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
874

875
        case STDERR_FILENO:
12,832✔
876
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
12,832✔
877
                        return NULL;
878

UNCOV
879
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
880

881
        default:
882
                return NULL;
883
        }
884
}
885

886
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
2,230✔
887
        _cleanup_strv_free_ char **v = NULL;
2,230✔
888
        int r;
2,230✔
889

890
        assert(c);
2,230✔
891
        assert(ret);
2,230✔
892

893
        STRV_FOREACH(i, c->environment_files) {
2,232✔
894
                _cleanup_globfree_ glob_t pglob = {};
2✔
895
                bool ignore = false;
2✔
896
                char *fn = *i;
2✔
897

898
                if (fn[0] == '-') {
2✔
899
                        ignore = true;
1✔
900
                        fn++;
1✔
901
                }
902

903
                if (!path_is_absolute(fn)) {
2✔
904
                        if (ignore)
×
UNCOV
905
                                continue;
×
906
                        return -EINVAL;
907
                }
908

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

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

920
                FOREACH_ARRAY(path, pglob.gl_pathv, pglob.gl_pathc) {
2✔
921
                        _cleanup_strv_free_ char **p = NULL;
1✔
922

923
                        r = load_env_file(NULL, *path, &p);
1✔
924
                        if (r < 0) {
1✔
925
                                if (ignore)
×
UNCOV
926
                                        continue;
×
927
                                return r;
928
                        }
929

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

937
                                p = strv_env_clean_with_callback(p, invalid_env, &info);
1✔
938
                        }
939

940
                        if (!v)
1✔
941
                                v = TAKE_PTR(p);
1✔
942
                        else {
943
                                char **m = strv_env_merge(v, p);
×
944
                                if (!m)
×
UNCOV
945
                                        return -ENOMEM;
×
946

UNCOV
947
                                strv_free_and_replace(v, m);
×
948
                        }
949
                }
950
        }
951

952
        *ret = TAKE_PTR(v);
2,230✔
953

954
        return 0;
2,230✔
955
}
956

957
static bool tty_may_match_dev_console(const char *tty) {
370✔
958
        _cleanup_free_ char *resolved = NULL;
370✔
959

960
        if (!tty)
370✔
961
                return true;
962

963
        tty = skip_dev_prefix(tty);
370✔
964

965
        /* trivial identity? */
966
        if (streq(tty, "console"))
370✔
967
                return true;
968

969
        if (resolve_dev_console(&resolved) < 0)
39✔
970
                return true; /* if we could not resolve, assume it may */
971

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

976
static bool exec_context_may_touch_tty(const ExecContext *ec) {
22,494✔
977
        assert(ec);
22,494✔
978

979
        return ec->tty_reset ||
44,837✔
980
                ec->tty_vhangup ||
22,343✔
981
                ec->tty_vt_disallocate ||
22,343✔
982
                is_terminal_input(ec->std_input) ||
22,343✔
983
                is_terminal_output(ec->std_output) ||
44,745✔
984
                is_terminal_output(ec->std_error);
22,075✔
985
}
986

987
bool exec_context_may_touch_console(const ExecContext *ec) {
20,965✔
988

989
        return exec_context_may_touch_tty(ec) &&
21,335✔
990
               tty_may_match_dev_console(exec_context_tty_path(ec));
370✔
991
}
992

993
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,544✔
994
        assert(c);
1,544✔
995

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

1003
        if (!c->tty_reset)
1,544✔
1004
                return false;
1005

1006
        return !streq_ptr(strv_env_get(c->environment, "TERM"), "dumb");
531✔
1007
}
1008

1009
static void strv_fprintf(FILE *f, char **l) {
×
UNCOV
1010
        assert(f);
×
1011

1012
        STRV_FOREACH(g, l)
×
1013
                fprintf(f, " %s", *g);
×
UNCOV
1014
}
×
1015

1016
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
1,792✔
1017
        assert(f);
1,792✔
1018
        assert(prefix);
1,792✔
1019
        assert(name);
1,792✔
1020

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

1028
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1029
        assert(p);
×
UNCOV
1030
        assert(f);
×
1031

UNCOV
1032
        prefix = strempty(prefix);
×
1033

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

1062
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1063
        strv_dump(f, prefix, "Environment", p->environment);
×
UNCOV
1064
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1065

1066
        LIST_FOREACH(open_files, file, p->open_files)
×
UNCOV
1067
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1068

1069
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
UNCOV
1070
}
×
1071

1072
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
224✔
1073
        int r;
224✔
1074

1075
        assert(c);
224✔
1076
        assert(f);
224✔
1077

1078
        prefix = strempty(prefix);
224✔
1079

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

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

1139
        if (c->root_image)
224✔
UNCOV
1140
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1141

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

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

1159
        if (c->root_hash_path)
224✔
UNCOV
1160
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1161

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

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

1173
        if (c->root_verity)
224✔
UNCOV
1174
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1175

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

1179
        STRV_FOREACH(e, c->environment_files)
224✔
UNCOV
1180
                fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
×
1181

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

1185
        STRV_FOREACH(e, c->unset_environment)
224✔
UNCOV
1186
                fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
×
1187

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

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

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

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

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

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

1211
        if (c->nice_set)
224✔
UNCOV
1212
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
×
1213

1214
        if (c->oom_score_adjust_set)
224✔
1215
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
10✔
1216

1217
        if (c->coredump_filter_set)
224✔
UNCOV
1218
                fprintf(f, "%sCoredumpFilter: 0x%"PRIx64"\n", prefix, c->coredump_filter);
×
1219

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

1228
        if (c->ioprio_set) {
224✔
UNCOV
1229
                _cleanup_free_ char *class_str = NULL;
×
1230

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

UNCOV
1235
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
×
1236
        }
1237

1238
        if (c->cpu_sched_set) {
224✔
UNCOV
1239
                _cleanup_free_ char *policy_str = NULL;
×
1240

1241
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1242
                if (r >= 0)
×
UNCOV
1243
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1244

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

1252
        if (c->cpu_set.set) {
224✔
UNCOV
1253
                _cleanup_free_ char *affinity = NULL;
×
1254

1255
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
UNCOV
1256
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1257
        }
1258

1259
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
224✔
1260
                _cleanup_free_ char *nodes = NULL;
1✔
1261

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

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

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

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

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

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

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

1326
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
207✔
1327

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

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

1337
        if (c->log_level_max >= 0) {
224✔
1338
                _cleanup_free_ char *t = NULL;
1✔
1339

1340
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1341

1342
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1343
        }
1344

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

1350
        if (c->log_ratelimit.burst > 0)
224✔
UNCOV
1351
                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit.burst);
×
1352

1353
        if (!set_isempty(c->log_filter_allowed_patterns) || !set_isempty(c->log_filter_denied_patterns)) {
224✔
UNCOV
1354
                fprintf(f, "%sLogFilterPatterns:", prefix);
×
1355

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

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

1370
        if (c->log_namespace)
224✔
UNCOV
1371
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1372

1373
        if (c->secure_bits) {
224✔
UNCOV
1374
                _cleanup_free_ char *str = NULL;
×
1375

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

1381
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
224✔
1382
                _cleanup_free_ char *str = NULL;
16✔
1383

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

1389
        if (c->capability_ambient_set != 0) {
224✔
UNCOV
1390
                _cleanup_free_ char *str = NULL;
×
1391

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

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

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

1404
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
224✔
1405

1406
        if (c->pam_name)
224✔
UNCOV
1407
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
×
1408

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

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

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

1430
        if (c->utmp_id)
224✔
UNCOV
1431
                fprintf(f,
×
1432
                        "%sUtmpIdentifier: %s\n",
1433
                        prefix, c->utmp_id);
1434

1435
        if (c->selinux_context)
224✔
UNCOV
1436
                fprintf(f,
×
1437
                        "%sSELinuxContext: %s%s\n",
UNCOV
1438
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1439

1440
        if (c->apparmor_profile)
224✔
UNCOV
1441
                fprintf(f,
×
1442
                        "%sAppArmorProfile: %s%s\n",
UNCOV
1443
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1444

1445
        if (c->smack_process_label)
224✔
UNCOV
1446
                fprintf(f,
×
1447
                        "%sSmackProcessLabel: %s%s\n",
UNCOV
1448
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1449

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

1455
        fprintf(f,
224✔
1456
                "%sLockPersonality: %s\n",
1457
                prefix, yes_no(c->lock_personality));
224✔
1458

1459
        if (c->syscall_filter) {
224✔
1460
                fprintf(f,
11✔
1461
                        "%sSystemCallFilter: ",
1462
                        prefix);
1463

1464
                if (!c->syscall_allow_list)
11✔
UNCOV
1465
                        fputc('~', f);
×
1466

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

1475
                        if (first)
4,257✔
1476
                                first = false;
1477
                        else
1478
                                fputc(' ', f);
4,246✔
1479

1480
                        name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
4,257✔
1481
                        fputs(strna(name), f);
4,257✔
1482

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

1493
                fputc('\n', f);
11✔
1494
        }
1495

1496
        if (c->syscall_archs) {
224✔
1497
                fprintf(f,
11✔
1498
                        "%sSystemCallArchitectures:",
1499
                        prefix);
1500

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

1509
        if (exec_context_restrict_namespaces_set(c)) {
224✔
1510
                _cleanup_free_ char *s = NULL;
12✔
1511

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

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

1526
        if (c->network_namespace_path)
224✔
UNCOV
1527
                fprintf(f,
×
1528
                        "%sNetworkNamespacePath: %s\n",
1529
                        prefix, c->network_namespace_path);
1530

1531
        if (c->syscall_errno > 0) {
224✔
1532
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
223✔
1533

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

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

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

1567
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
224✔
1568
}
224✔
1569

1570
bool exec_context_maintains_privileges(const ExecContext *c) {
×
UNCOV
1571
        assert(c);
×
1572

1573
        /* Returns true if the process forked off would run under
1574
         * an unchanged UID or as root. */
1575

UNCOV
1576
        if (!c->user)
×
1577
                return true;
1578

1579
        if (STR_IN_SET(c->user, "root", "0"))
×
UNCOV
1580
                return true;
×
1581

UNCOV
1582
        return false;
×
1583
}
1584

1585
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,520✔
1586
        int p;
2,520✔
1587

1588
        assert(c);
2,520✔
1589

1590
        if (c->ioprio_set)
2,520✔
1591
                return c->ioprio;
18✔
1592

1593
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,502✔
1594
        if (p < 0)
2,502✔
1595
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1596

1597
        return ioprio_normalize(p);
2,502✔
1598
}
1599

1600
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
33,955✔
1601
        assert(c);
33,955✔
1602

1603
        /* Explicit setting wins */
1604
        if (c->mount_apivfs >= 0)
33,955✔
1605
                return c->mount_apivfs > 0;
122✔
1606

1607
        /* Default to "yes" if root directory or image are specified */
1608
        if (exec_context_with_rootfs(c))
33,833✔
1609
                return true;
52✔
1610

1611
        return false;
1612
}
1613

1614
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
28,444✔
1615
        assert(c);
28,444✔
1616

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

1622
        if (c->bind_log_sockets >= 0)
28,436✔
1623
                return c->bind_log_sockets > 0;
2✔
1624

1625
        if (exec_context_get_effective_mount_apivfs(c))
28,434✔
1626
                return true;
1627

1628
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1629
        if (exec_context_with_rootfs(c) && c->private_devices)
28,368✔
UNCOV
1630
                return true;
×
1631

1632
        return false;
1633
}
1634

1635
void exec_context_free_log_extra_fields(ExecContext *c) {
47,064✔
1636
        assert(c);
47,064✔
1637

1638
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
47,069✔
1639
                free(field->iov_base);
5✔
1640

1641
        c->log_extra_fields = mfree(c->log_extra_fields);
47,064✔
1642
        c->n_log_extra_fields = 0;
47,064✔
1643
}
47,064✔
1644

1645
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,529✔
1646
        _cleanup_close_ int fd = -EBADF;
1,529✔
1647
        const char *path;
1,529✔
1648
        struct stat st;
1,529✔
1649
        int r;
1,529✔
1650

1651
        assert(c);
1,529✔
1652

1653
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1654
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,529✔
1655

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

1662
        path = exec_context_tty_path(c);
49✔
1663
        if (!path)
49✔
1664
                return;
1665

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

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

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

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

UNCOV
1688
int exec_context_get_clean_directories(
×
1689
                ExecContext *c,
1690
                char **prefix,
1691
                ExecCleanMask mask,
1692
                char ***ret) {
1693

1694
        _cleanup_strv_free_ char **l = NULL;
×
UNCOV
1695
        int r;
×
1696

1697
        assert(c);
×
1698
        assert(prefix);
×
UNCOV
1699
        assert(ret);
×
1700

1701
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
1702
                if (!BIT_SET(mask, t))
×
UNCOV
1703
                        continue;
×
1704

1705
                if (!prefix[t])
×
UNCOV
1706
                        continue;
×
1707

1708
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
×
UNCOV
1709
                        char *j;
×
1710

1711
                        j = path_join(prefix[t], i->path);
×
UNCOV
1712
                        if (!j)
×
1713
                                return -ENOMEM;
1714

1715
                        r = strv_consume(&l, j);
×
UNCOV
1716
                        if (r < 0)
×
1717
                                return r;
1718

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

1725
                                r = strv_consume(&l, j);
×
UNCOV
1726
                                if (r < 0)
×
1727
                                        return r;
1728
                        }
1729

1730
                        STRV_FOREACH(symlink, i->symlinks) {
×
1731
                                j = path_join(prefix[t], *symlink);
×
UNCOV
1732
                                if (!j)
×
1733
                                        return -ENOMEM;
1734

1735
                                r = strv_consume(&l, j);
×
UNCOV
1736
                                if (r < 0)
×
1737
                                        return r;
1738
                        }
1739
                }
1740
        }
1741

1742
        *ret = TAKE_PTR(l);
×
UNCOV
1743
        return 0;
×
1744
}
1745

1746
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,240✔
1747
        ExecCleanMask mask = 0;
1,240✔
1748

1749
        assert(c);
1,240✔
1750
        assert(ret);
1,240✔
1751

1752
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
7,440✔
1753
                if (c->directories[t].n_items > 0)
6,200✔
1754
                        mask |= 1U << t;
252✔
1755

1756
        *ret = mask;
1,240✔
1757
        return 0;
1,240✔
1758
}
1759

1760
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,260✔
1761
        int n = 0, r;
1,260✔
1762

1763
        assert(c);
1,260✔
1764

1765
        if (c->oom_score_adjust_set)
1,260✔
1766
                return c->oom_score_adjust;
335✔
1767

1768
        r = get_oom_score_adjust(&n);
925✔
1769
        if (r < 0)
925✔
UNCOV
1770
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1771

1772
        return n;
925✔
1773
}
1774

1775
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1,260✔
1776
        _cleanup_free_ char *t = NULL;
1,260✔
1777
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1,260✔
1778
        int r;
1,260✔
1779

1780
        assert(c);
1,260✔
1781

1782
        if (c->coredump_filter_set)
1,260✔
UNCOV
1783
                return c->coredump_filter;
×
1784

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

1794
        return n;
1,260✔
1795
}
1796

1797
int exec_context_get_nice(const ExecContext *c) {
1,260✔
1798
        int n;
1,260✔
1799

1800
        assert(c);
1,260✔
1801

1802
        if (c->nice_set)
1,260✔
1803
                return c->nice;
6✔
1804

1805
        errno = 0;
1,254✔
1806
        n = getpriority(PRIO_PROCESS, 0);
1,254✔
1807
        if (errno > 0) {
1,254✔
UNCOV
1808
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1809
                n = 0;
1810
        }
1811

1812
        return n;
1813
}
1814

1815
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,260✔
1816
        int n;
1,260✔
1817

1818
        assert(c);
1,260✔
1819

1820
        if (c->cpu_sched_set)
1,260✔
UNCOV
1821
                return c->cpu_sched_policy;
×
1822

1823
        n = sched_getscheduler(0);
1,260✔
1824
        if (n < 0)
1,260✔
UNCOV
1825
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1826

1827
        return n < 0 ? SCHED_OTHER : n;
1,260✔
1828
}
1829

1830
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,260✔
1831
        struct sched_param p = {};
1,260✔
1832
        int r;
1,260✔
1833

1834
        assert(c);
1,260✔
1835

1836
        if (c->cpu_sched_set)
1,260✔
UNCOV
1837
                return c->cpu_sched_priority;
×
1838

1839
        r = sched_getparam(0, &p);
1,260✔
1840
        if (r < 0)
1,260✔
UNCOV
1841
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1842

1843
        return r >= 0 ? p.sched_priority : 0;
1,260✔
1844
}
1845

1846
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,260✔
1847
        int r;
1,260✔
1848

1849
        assert(c);
1,260✔
1850

1851
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,260✔
1852
                return c->timer_slack_nsec;
1853

1854
        r = prctl(PR_GET_TIMERSLACK);
1,260✔
1855
        if (r < 0)
1,260✔
UNCOV
1856
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1857

1858
        return (uint64_t) MAX(r, 0);
1,260✔
1859
}
1860

1861
bool exec_context_get_set_login_environment(const ExecContext *c) {
10,874✔
1862
        assert(c);
10,874✔
1863

1864
        if (c->set_login_environment >= 0)
10,874✔
UNCOV
1865
                return c->set_login_environment;
×
1866

1867
        return c->user || c->dynamic_user || c->pam_name;
19,580✔
1868
}
1869

1870
char** exec_context_get_syscall_filter(const ExecContext *c) {
1,260✔
1871
        _cleanup_strv_free_ char **l = NULL;
1,260✔
1872

1873
        assert(c);
1,260✔
1874

1875
#if HAVE_SECCOMP
1876
        void *id, *val;
1,260✔
1877
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
18,798✔
1878
                _cleanup_free_ char *name = NULL;
17,538✔
1879
                const char *e = NULL;
17,538✔
1880
                char *s;
17,538✔
1881
                int num = PTR_TO_INT(val);
17,538✔
1882

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

1887
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
17,538✔
1888
                if (!name)
17,538✔
UNCOV
1889
                        continue;
×
1890

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

1904
                if (strv_consume(&l, s) < 0)
17,538✔
1905
                        return NULL;
1906
        }
1907

1908
        strv_sort(l);
1,260✔
1909
#endif
1910

1911
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,260✔
1912
}
1913

1914
char** exec_context_get_syscall_archs(const ExecContext *c) {
1,260✔
1915
        _cleanup_strv_free_ char **l = NULL;
1,260✔
1916

1917
        assert(c);
1,260✔
1918

1919
#if HAVE_SECCOMP
1920
        void *id;
1,260✔
1921
        SET_FOREACH(id, c->syscall_archs) {
1,311✔
1922
                const char *name;
51✔
1923

1924
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
51✔
1925
                if (!name)
51✔
UNCOV
1926
                        continue;
×
1927

1928
                if (strv_extend(&l, name) < 0)
51✔
UNCOV
1929
                        return NULL;
×
1930
        }
1931

1932
        strv_sort(l);
1,260✔
1933
#endif
1934

1935
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,260✔
1936
}
1937

1938
char** exec_context_get_syscall_log(const ExecContext *c) {
1,260✔
1939
        _cleanup_strv_free_ char **l = NULL;
1,260✔
1940

1941
        assert(c);
1,260✔
1942

1943
#if HAVE_SECCOMP
1944
        void *id, *val;
1,260✔
1945
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,260✔
UNCOV
1946
                char *name = NULL;
×
1947

1948
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1949
                if (!name)
×
UNCOV
1950
                        continue;
×
1951

1952
                if (strv_consume(&l, name) < 0)
×
UNCOV
1953
                        return NULL;
×
1954
        }
1955

1956
        strv_sort(l);
1,260✔
1957
#endif
1958

1959
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,260✔
1960
}
1961

1962
char** exec_context_get_address_families(const ExecContext *c) {
1,260✔
1963
        _cleanup_strv_free_ char **l = NULL;
1,260✔
1964
        void *af;
1,260✔
1965

1966
        assert(c);
1,260✔
1967

1968
        SET_FOREACH(af, c->address_families) {
1,410✔
1969
                const char *name;
150✔
1970

1971
                name = af_to_name(PTR_TO_INT(af));
150✔
1972
                if (!name)
150✔
UNCOV
1973
                        continue;
×
1974

1975
                if (strv_extend(&l, name) < 0)
150✔
UNCOV
1976
                        return NULL;
×
1977
        }
1978

1979
        strv_sort(l);
1,260✔
1980

1981
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,260✔
1982
}
1983

1984
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1,260✔
1985
        assert(c);
1,260✔
1986

1987
#if HAVE_LIBBPF
1988
        char **l = set_get_strv(c->restrict_filesystems);
1,260✔
1989
        if (!l)
1,260✔
1990
                return NULL;
1991

1992
        return strv_sort(l);
1,260✔
1993
#else
1994
        return strv_new(NULL);
1995
#endif
1996
}
1997

1998
int exec_context_has_vpicked_extensions(const ExecContext *context) {
3✔
1999
        int r;
3✔
2000

2001
        assert(context);
3✔
2002

2003
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
3✔
UNCOV
2004
                r = path_uses_vpick(mi->source);
×
UNCOV
2005
                if (r != 0)
×
2006
                        return r;
2007
        }
2008
        STRV_FOREACH(ed, context->extension_directories) {
3✔
UNCOV
2009
                r = path_uses_vpick(*ed);
×
UNCOV
2010
                if (r != 0)
×
2011
                        return r;
2012
        }
2013

2014
        return 0;
2015
}
2016

2017
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
4,621✔
2018
        assert(s);
4,621✔
2019

2020
        *s = (ExecStatus) {
4,621✔
2021
                .pid = pid,
2022
        };
2023

2024
        if (ts)
4,621✔
2025
                s->start_timestamp = *ts;
4,621✔
2026
        else
UNCOV
2027
                dual_timestamp_now(&s->start_timestamp);
×
2028
}
4,621✔
2029

2030
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,061✔
2031
        assert(s);
2,061✔
2032

2033
        if (s->pid != pid)
2,061✔
2034
                *s = (ExecStatus) {
3✔
2035
                        .pid = pid,
2036
                };
2037

2038
        dual_timestamp_now(&s->exit_timestamp);
2,061✔
2039

2040
        s->code = code;
2,061✔
2041
        s->status = status;
2,061✔
2042

2043
        if (context && context->utmp_id)
2,061✔
2044
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2045
}
2,061✔
2046

2047
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
7,830✔
2048
        assert(s);
7,830✔
2049
        assert(ucred);
7,830✔
2050
        assert(ts);
7,830✔
2051

2052
        if (ucred->pid != s->pid)
7,830✔
2053
                *s = (ExecStatus) {
9✔
2054
                        .pid = ucred->pid,
2055
                };
2056

2057
        s->handoff_timestamp = *ts;
7,830✔
2058
}
7,830✔
2059

2060
void exec_status_reset(ExecStatus *s) {
25,080✔
2061
        assert(s);
25,080✔
2062

2063
        *s = (ExecStatus) {};
25,080✔
2064
}
25,080✔
2065

2066
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
94✔
2067
        assert(s);
94✔
2068
        assert(f);
94✔
2069

2070
        if (s->pid <= 0)
94✔
2071
                return;
2072

2073
        prefix = strempty(prefix);
10✔
2074

2075
        fprintf(f,
10✔
2076
                "%sPID: "PID_FMT"\n",
2077
                prefix, s->pid);
2078

2079
        if (dual_timestamp_is_set(&s->start_timestamp))
10✔
2080
                fprintf(f,
10✔
2081
                        "%sStart Timestamp: %s\n",
2082
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
10✔
2083

2084
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
10✔
2085
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
10✔
2086
                fprintf(f,
10✔
2087
                        "%sHandoff Timestamp: %s since start\n",
2088
                        prefix,
2089
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
20✔
2090
        else
UNCOV
2091
                fprintf(f,
×
2092
                        "%sHandoff Timestamp: %s\n",
UNCOV
2093
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
×
2094

2095
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2096

UNCOV
2097
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
×
UNCOV
2098
                        fprintf(f,
×
2099
                                "%sExit Timestamp: %s since handoff\n",
2100
                                prefix,
UNCOV
2101
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
×
UNCOV
2102
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
UNCOV
2103
                        fprintf(f,
×
2104
                                "%sExit Timestamp: %s since start\n",
2105
                                prefix,
UNCOV
2106
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2107
                else
UNCOV
2108
                        fprintf(f,
×
2109
                                "%sExit Timestamp: %s\n",
UNCOV
2110
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2111

UNCOV
2112
                fprintf(f,
×
2113
                        "%sExit Code: %s\n"
2114
                        "%sExit Status: %i\n",
UNCOV
2115
                        prefix, sigchld_code_to_string(s->code),
×
UNCOV
2116
                        prefix, s->status);
×
2117
        }
2118
}
2119

2120
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
94✔
2121
        _cleanup_free_ char *cmd = NULL;
188✔
2122
        const char *prefix2;
94✔
2123

2124
        assert(c);
94✔
2125
        assert(f);
94✔
2126

2127
        prefix = strempty(prefix);
94✔
2128
        prefix2 = strjoina(prefix, "\t");
470✔
2129

2130
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
94✔
2131

2132
        fprintf(f,
94✔
2133
                "%sCommand Line: %s\n",
2134
                prefix, strnull(cmd));
2135

2136
        exec_status_dump(&c->exec_status, f, prefix2);
94✔
2137
}
94✔
2138

2139
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
91✔
2140
        assert(f);
91✔
2141

2142
        prefix = strempty(prefix);
91✔
2143

2144
        LIST_FOREACH(command, i, c)
185✔
2145
                exec_command_dump(i, f, prefix);
94✔
2146
}
91✔
2147

2148
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
16,741✔
2149
        ExecCommand *end;
16,741✔
2150

2151
        assert(l);
16,741✔
2152
        assert(e);
16,741✔
2153

2154
        if (*l) {
16,741✔
2155
                /* It's kind of important, that we keep the order here */
2156
                end = LIST_FIND_TAIL(command, *l);
479✔
2157
                LIST_INSERT_AFTER(command, *l, end, e);
242✔
2158
        } else
2159
                *l = e;
16,499✔
2160
}
16,741✔
2161

2162
int exec_command_set(ExecCommand *c, const char *path, ...) {
172✔
2163
        va_list ap;
172✔
2164
        char **l, *p;
172✔
2165

2166
        assert(c);
172✔
2167
        assert(path);
172✔
2168

2169
        va_start(ap, path);
172✔
2170
        l = strv_new_ap(path, ap);
172✔
2171
        va_end(ap);
172✔
2172

2173
        if (!l)
172✔
2174
                return -ENOMEM;
172✔
2175

2176
        p = strdup(path);
172✔
2177
        if (!p) {
172✔
UNCOV
2178
                strv_free(l);
×
UNCOV
2179
                return -ENOMEM;
×
2180
        }
2181

2182
        free_and_replace(c->path, p);
172✔
2183

2184
        return strv_free_and_replace(c->argv, l);
172✔
2185
}
2186

2187
int exec_command_append(ExecCommand *c, const char *path, ...) {
247✔
2188
        char **l;
247✔
2189
        va_list ap;
247✔
2190
        int r;
247✔
2191

2192
        assert(c);
247✔
2193
        assert(path);
247✔
2194

2195
        va_start(ap, path);
247✔
2196
        l = strv_new_ap(path, ap);
247✔
2197
        va_end(ap);
247✔
2198

2199
        if (!l)
247✔
2200
                return -ENOMEM;
247✔
2201

2202
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
247✔
2203
        if (r < 0)
247✔
UNCOV
2204
                return r;
×
2205

2206
        return 0;
2207
}
2208

2209
static char *destroy_tree(char *path) {
269✔
2210
        if (!path)
269✔
2211
                return NULL;
2212

2213
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
84✔
2214
                log_debug("Spawning process to nuke '%s'", path);
84✔
2215

2216
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
84✔
2217
        }
2218

2219
        return mfree(path);
84✔
2220
}
2221

2222
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
130,668✔
2223
        assert(rt);
130,668✔
2224

2225
        if (rt->manager)
130,668✔
2226
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
172✔
2227

2228
        rt->id = mfree(rt->id);
130,668✔
2229
        rt->tmp_dir = mfree(rt->tmp_dir);
130,668✔
2230
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
130,668✔
2231
        safe_close_pair(rt->netns_storage_socket);
130,668✔
2232
        safe_close_pair(rt->ipcns_storage_socket);
130,668✔
2233
}
130,668✔
2234

2235
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
130,640✔
2236
        if (!rt)
130,640✔
2237
                return NULL;
2238

2239
        exec_shared_runtime_done(rt);
130,640✔
2240
        return mfree(rt);
130,640✔
2241
}
2242

2243
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
173✔
2244
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
133,770✔
2245

2246
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
49✔
2247
        if (!rt)
49✔
2248
                return NULL;
2249

2250
        assert(rt->n_ref > 0);
48✔
2251
        rt->n_ref--;
48✔
2252

2253
        if (rt->n_ref > 0)
48✔
2254
                return NULL;
2255

2256
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
48✔
2257
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
48✔
2258

2259
        return exec_shared_runtime_free(rt);
48✔
2260
}
2261

2262
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
130,640✔
2263
        _cleanup_free_ char *id_copy = NULL;
261,280✔
2264
        ExecSharedRuntime *n;
130,640✔
2265

2266
        assert(ret);
130,640✔
2267

2268
        id_copy = strdup(id);
130,640✔
2269
        if (!id_copy)
130,640✔
2270
                return -ENOMEM;
2271

2272
        n = new(ExecSharedRuntime, 1);
130,640✔
2273
        if (!n)
130,640✔
2274
                return -ENOMEM;
2275

2276
        *n = (ExecSharedRuntime) {
130,640✔
2277
                .id = TAKE_PTR(id_copy),
130,640✔
2278
                .netns_storage_socket = EBADF_PAIR,
2279
                .ipcns_storage_socket = EBADF_PAIR,
2280
        };
2281

2282
        *ret = n;
130,640✔
2283
        return 0;
130,640✔
2284
}
2285

2286
static int exec_shared_runtime_add(
172✔
2287
                Manager *m,
2288
                const char *id,
2289
                char **tmp_dir,
2290
                char **var_tmp_dir,
2291
                int netns_storage_socket[2],
2292
                int ipcns_storage_socket[2],
2293
                ExecSharedRuntime **ret) {
2294

2295
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
172✔
2296
        int r;
172✔
2297

2298
        assert(m);
172✔
2299
        assert(id);
172✔
2300

2301
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2302

2303
        r = exec_shared_runtime_allocate(&rt, id);
172✔
2304
        if (r < 0)
172✔
2305
                return r;
2306

2307
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
172✔
2308
        if (r < 0)
172✔
2309
                return r;
2310

2311
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
172✔
2312
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
172✔
2313
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
172✔
2314

2315
        if (netns_storage_socket) {
172✔
2316
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
172✔
2317
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
172✔
2318
        }
2319

2320
        if (ipcns_storage_socket) {
172✔
2321
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
172✔
2322
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
172✔
2323
        }
2324

2325
        rt->manager = m;
172✔
2326

2327
        if (ret)
172✔
2328
                *ret = rt;
69✔
2329
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2330
        TAKE_PTR(rt);
172✔
2331
        return 0;
172✔
2332
}
2333

2334
static int exec_shared_runtime_make(
6,221✔
2335
                Manager *m,
2336
                const ExecContext *c,
2337
                const char *id,
2338
                ExecSharedRuntime **ret) {
2339

2340
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
6,221✔
2341
        _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
12,442✔
2342
        int r;
6,221✔
2343

2344
        assert(m);
6,221✔
2345
        assert(c);
6,221✔
2346
        assert(id);
6,221✔
2347

2348
        /* It is not necessary to create ExecSharedRuntime object. */
2349
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
6,221✔
2350
                *ret = NULL;
6,152✔
2351
                return 0;
6,152✔
2352
        }
2353

2354
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
132✔
2355
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
63✔
UNCOV
2356
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
UNCOV
2357
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2358
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
63✔
2359
                if (r < 0)
63✔
2360
                        return r;
2361
        }
2362

2363
        if (exec_needs_network_namespace(c))
69✔
2364
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
7✔
UNCOV
2365
                        return -errno;
×
2366

2367
        if (exec_needs_ipc_namespace(c))
69✔
2368
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
UNCOV
2369
                        return -errno;
×
2370

2371
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
69✔
2372
        if (r < 0)
69✔
2373
                return r;
×
2374

2375
        return 1;
2376
}
2377

2378
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
6,324✔
2379
        ExecSharedRuntime *rt;
6,324✔
2380
        int r;
6,324✔
2381

2382
        assert(m);
6,324✔
2383
        assert(id);
6,324✔
2384
        assert(ret);
6,324✔
2385

2386
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
6,324✔
2387
        if (rt)
6,324✔
2388
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2389
                goto ref;
103✔
2390

2391
        if (!create) {
6,221✔
UNCOV
2392
                *ret = NULL;
×
UNCOV
2393
                return 0;
×
2394
        }
2395

2396
        /* If not found, then create a new object. */
2397
        r = exec_shared_runtime_make(m, c, id, &rt);
6,221✔
2398
        if (r < 0)
6,221✔
2399
                return r;
2400
        if (r == 0) {
6,221✔
2401
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2402
                *ret = NULL;
6,152✔
2403
                return 0;
6,152✔
2404
        }
2405

2406
ref:
69✔
2407
        /* increment reference counter. */
2408
        rt->n_ref++;
172✔
2409
        *ret = rt;
172✔
2410
        return 1;
172✔
2411
}
2412

2413
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
73✔
2414
        ExecSharedRuntime *rt;
73✔
2415

2416
        assert(m);
73✔
2417
        assert(f);
73✔
2418
        assert(fds);
73✔
2419

2420
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
197✔
2421
                fprintf(f, "exec-runtime=%s", rt->id);
124✔
2422

2423
                if (rt->tmp_dir)
124✔
2424
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
124✔
2425

2426
                if (rt->var_tmp_dir)
124✔
2427
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
124✔
2428

2429
                if (rt->netns_storage_socket[0] >= 0) {
124✔
2430
                        int copy;
2✔
2431

2432
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2✔
2433
                        if (copy < 0)
2✔
UNCOV
2434
                                return copy;
×
2435

2436
                        fprintf(f, " netns-socket-0=%i", copy);
2✔
2437
                }
2438

2439
                if (rt->netns_storage_socket[1] >= 0) {
124✔
2440
                        int copy;
2✔
2441

2442
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2✔
2443
                        if (copy < 0)
2✔
2444
                                return copy;
2445

2446
                        fprintf(f, " netns-socket-1=%i", copy);
2✔
2447
                }
2448

2449
                if (rt->ipcns_storage_socket[0] >= 0) {
124✔
UNCOV
2450
                        int copy;
×
2451

UNCOV
2452
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
UNCOV
2453
                        if (copy < 0)
×
2454
                                return copy;
2455

UNCOV
2456
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2457
                }
2458

2459
                if (rt->ipcns_storage_socket[1] >= 0) {
124✔
UNCOV
2460
                        int copy;
×
2461

UNCOV
2462
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
UNCOV
2463
                        if (copy < 0)
×
2464
                                return copy;
2465

UNCOV
2466
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2467
                }
2468

2469
                fputc('\n', f);
124✔
2470
        }
2471

2472
        return 0;
73✔
2473
}
2474

2475
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
133,598✔
2476
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
133,598✔
2477
        ExecSharedRuntime *rt = NULL;
133,598✔
2478
        int r;
133,598✔
2479

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

2485
        assert(u);
133,598✔
2486
        assert(key);
133,598✔
2487
        assert(value);
133,598✔
2488

2489
        /* Manager manages ExecSharedRuntime objects by the unit id.
2490
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2491
        if (isempty(u->id)) {
133,598✔
UNCOV
2492
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
UNCOV
2493
                return 0;
×
2494
        }
2495

2496
        if (u->manager) {
133,598✔
2497
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
133,598✔
UNCOV
2498
                        return log_oom();
×
2499

2500
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
133,598✔
2501
        }
2502
        if (!rt) {
133,598✔
2503
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
130,468✔
UNCOV
2504
                        return log_oom();
×
2505

2506
                rt = rt_create;
130,468✔
2507
        }
2508

2509
        if (streq(key, "tmp-dir")) {
133,598✔
UNCOV
2510
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2511
                        return -ENOMEM;
2512

2513
        } else if (streq(key, "var-tmp-dir")) {
133,598✔
2514
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2515
                        return -ENOMEM;
2516

2517
        } else if (streq(key, "netns-socket-0")) {
133,598✔
2518

UNCOV
2519
                safe_close(rt->netns_storage_socket[0]);
×
UNCOV
2520
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2521
                if (rt->netns_storage_socket[0] < 0)
×
2522
                        return 0;
2523

2524
        } else if (streq(key, "netns-socket-1")) {
133,598✔
2525

UNCOV
2526
                safe_close(rt->netns_storage_socket[1]);
×
UNCOV
2527
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
UNCOV
2528
                if (rt->netns_storage_socket[1] < 0)
×
2529
                        return 0;
2530
        } else
2531
                return 0;
2532

2533
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
UNCOV
2534
        if (rt_create && u->manager) {
×
UNCOV
2535
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
UNCOV
2536
                if (r < 0) {
×
UNCOV
2537
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
UNCOV
2538
                        return 0;
×
2539
                }
2540

UNCOV
2541
                rt_create->manager = u->manager;
×
2542

2543
                /* Avoid cleanup */
UNCOV
2544
                TAKE_PTR(rt_create);
×
2545
        }
2546

2547
        return 1;
2548
}
2549

2550
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
103✔
2551
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
103✔
2552
        char *id = NULL;
103✔
2553
        int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
103✔
2554
        const char *p, *v = ASSERT_PTR(value);
103✔
2555
        size_t n;
103✔
2556

2557
        assert(m);
103✔
2558
        assert(fds);
103✔
2559

2560
        n = strcspn(v, " ");
103✔
2561
        id = strndupa_safe(v, n);
103✔
2562
        if (v[n] != ' ')
103✔
UNCOV
2563
                goto finalize;
×
2564
        p = v + n + 1;
103✔
2565

2566
        v = startswith(p, "tmp-dir=");
103✔
2567
        if (v) {
103✔
2568
                n = strcspn(v, " ");
103✔
2569
                tmp_dir = strndup(v, n);
103✔
2570
                if (!tmp_dir)
103✔
UNCOV
2571
                        return log_oom();
×
2572
                if (v[n] != ' ')
103✔
UNCOV
2573
                        goto finalize;
×
2574
                p = v + n + 1;
103✔
2575
        }
2576

2577
        v = startswith(p, "var-tmp-dir=");
103✔
2578
        if (v) {
103✔
2579
                n = strcspn(v, " ");
103✔
2580
                var_tmp_dir = strndup(v, n);
103✔
2581
                if (!var_tmp_dir)
103✔
UNCOV
2582
                        return log_oom();
×
2583
                if (v[n] != ' ')
103✔
2584
                        goto finalize;
102✔
2585
                p = v + n + 1;
1✔
2586
        }
2587

2588
        v = startswith(p, "netns-socket-0=");
1✔
2589
        if (v) {
1✔
2590
                char *buf;
1✔
2591

2592
                n = strcspn(v, " ");
1✔
2593
                buf = strndupa_safe(v, n);
1✔
2594

2595
                netns_fdpair[0] = deserialize_fd(fds, buf);
1✔
2596
                if (netns_fdpair[0] < 0)
1✔
2597
                        return netns_fdpair[0];
2598
                if (v[n] != ' ')
1✔
2599
                        goto finalize;
×
2600
                p = v + n + 1;
1✔
2601
        }
2602

2603
        v = startswith(p, "netns-socket-1=");
1✔
2604
        if (v) {
1✔
2605
                char *buf;
1✔
2606

2607
                n = strcspn(v, " ");
1✔
2608
                buf = strndupa_safe(v, n);
1✔
2609

2610
                netns_fdpair[1] = deserialize_fd(fds, buf);
1✔
2611
                if (netns_fdpair[1] < 0)
1✔
2612
                        return netns_fdpair[1];
2613
                if (v[n] != ' ')
1✔
2614
                        goto finalize;
1✔
2615
                p = v + n + 1;
×
2616
        }
2617

2618
        v = startswith(p, "ipcns-socket-0=");
×
UNCOV
2619
        if (v) {
×
2620
                char *buf;
×
2621

UNCOV
2622
                n = strcspn(v, " ");
×
UNCOV
2623
                buf = strndupa_safe(v, n);
×
2624

2625
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
UNCOV
2626
                if (ipcns_fdpair[0] < 0)
×
2627
                        return ipcns_fdpair[0];
2628
                if (v[n] != ' ')
×
UNCOV
2629
                        goto finalize;
×
UNCOV
2630
                p = v + n + 1;
×
2631
        }
2632

UNCOV
2633
        v = startswith(p, "ipcns-socket-1=");
×
UNCOV
2634
        if (v) {
×
UNCOV
2635
                char *buf;
×
2636

UNCOV
2637
                n = strcspn(v, " ");
×
UNCOV
2638
                buf = strndupa_safe(v, n);
×
2639

UNCOV
2640
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
UNCOV
2641
                if (ipcns_fdpair[1] < 0)
×
2642
                        return ipcns_fdpair[1];
2643
        }
2644

UNCOV
2645
finalize:
×
2646
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
103✔
2647
        if (r < 0)
103✔
UNCOV
2648
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2649
        return 0;
2650
}
2651

2652
void exec_shared_runtime_vacuum(Manager *m) {
1,458✔
2653
        ExecSharedRuntime *rt;
1,458✔
2654

2655
        assert(m);
1,458✔
2656

2657
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2658

2659
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,561✔
2660
                if (rt->n_ref > 0)
103✔
2661
                        continue;
103✔
2662

UNCOV
2663
                (void) exec_shared_runtime_free(rt);
×
2664
        }
2665
}
1,458✔
2666

2667
int exec_runtime_make(
6,324✔
2668
                const Unit *unit,
2669
                const ExecContext *context,
2670
                ExecSharedRuntime *shared,
2671
                DynamicCreds *creds,
2672
                ExecRuntime **ret) {
2673
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
6,324✔
2674
        _cleanup_free_ char *ephemeral = NULL;
6,324✔
2675
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
6,324✔
2676
        int r;
6,324✔
2677

2678
        assert(unit);
6,324✔
2679
        assert(context);
6,324✔
2680
        assert(ret);
6,324✔
2681

2682
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
6,324✔
2683
                *ret = NULL;
6,151✔
2684
                return 0;
6,151✔
2685
        }
2686

2687
        if (exec_needs_ephemeral(context)) {
173✔
UNCOV
2688
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
UNCOV
2689
                if (r < 0)
×
2690
                        return r;
2691

UNCOV
2692
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
UNCOV
2693
                if (r < 0)
×
2694
                        return r;
2695

UNCOV
2696
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
UNCOV
2697
                        return -errno;
×
2698
        }
2699

2700
        rt = new(ExecRuntime, 1);
173✔
2701
        if (!rt)
173✔
2702
                return -ENOMEM;
2703

2704
        *rt = (ExecRuntime) {
173✔
2705
                .shared = shared,
2706
                .dynamic_creds = creds,
2707
                .ephemeral_copy = TAKE_PTR(ephemeral),
173✔
2708
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
173✔
2709
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
173✔
2710
        };
2711

2712
        *ret = TAKE_PTR(rt);
173✔
2713
        return 1;
173✔
2714
}
2715

2716
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
47,083✔
2717
        if (!rt)
47,083✔
2718
                return NULL;
2719

2720
        exec_shared_runtime_unref(rt->shared);
173✔
2721
        dynamic_creds_unref(rt->dynamic_creds);
173✔
2722

2723
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
173✔
2724

2725
        safe_close_pair(rt->ephemeral_storage_socket);
173✔
2726
        return mfree(rt);
173✔
2727
}
2728

2729
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
5,096✔
2730
        if (!rt)
5,096✔
2731
                return NULL;
2732

2733
        rt->shared = exec_shared_runtime_destroy(rt->shared);
49✔
2734
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
49✔
2735
        return exec_runtime_free(rt);
49✔
2736
}
2737

2738
void exec_runtime_clear(ExecRuntime *rt) {
28✔
2739
        if (!rt)
28✔
2740
                return;
2741

2742
        safe_close_pair(rt->ephemeral_storage_socket);
28✔
2743
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
28✔
2744
}
2745

2746
void exec_params_shallow_clear(ExecParameters *p) {
2,258✔
2747
        if (!p)
2,258✔
2748
                return;
2749

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

2753
        p->environment = strv_free(p->environment);
2,258✔
2754
        p->fd_names = strv_free(p->fd_names);
2,258✔
2755
        p->files_env = strv_free(p->files_env);
2,258✔
2756
        p->fds = mfree(p->fds);
2,258✔
2757
        p->exec_fd = safe_close(p->exec_fd);
2,258✔
2758
        p->user_lookup_fd = -EBADF;
2,258✔
2759
        p->bpf_restrict_fs_map_fd = -EBADF;
2,258✔
2760
        p->unit_id = mfree(p->unit_id);
2,258✔
2761
        p->invocation_id = SD_ID128_NULL;
2,258✔
2762
        p->invocation_id_string[0] = '\0';
2,258✔
2763
        p->confirm_spawn = mfree(p->confirm_spawn);
2,258✔
2764
}
2765

2766
void exec_params_deep_clear(ExecParameters *p) {
28✔
2767
        if (!p)
28✔
2768
                return;
2769

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

2774
        close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
28✔
2775

2776
        p->cgroup_path = mfree(p->cgroup_path);
28✔
2777

2778
        if (p->prefix) {
28✔
2779
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
28✔
2780
                p->prefix = mfree(p->prefix);
28✔
2781
        }
2782

2783
        p->received_credentials_directory = mfree(p->received_credentials_directory);
28✔
2784
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
28✔
2785

2786
        if (p->idle_pipe) {
28✔
UNCOV
2787
                close_many_and_free(p->idle_pipe, 4);
×
UNCOV
2788
                p->idle_pipe = NULL;
×
2789
        }
2790

2791
        p->stdin_fd = safe_close(p->stdin_fd);
28✔
2792
        p->stdout_fd = safe_close(p->stdout_fd);
28✔
2793
        p->stderr_fd = safe_close(p->stderr_fd);
28✔
2794

2795
        p->notify_socket = mfree(p->notify_socket);
28✔
2796

2797
        open_file_free_many(&p->open_files);
28✔
2798

2799
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
28✔
2800

2801
        exec_params_shallow_clear(p);
28✔
2802
}
2803

2804
void exec_directory_done(ExecDirectory *d) {
235,310✔
2805
        if (!d)
235,310✔
2806
                return;
2807

2808
        FOREACH_ARRAY(i, d->items, d->n_items) {
237,118✔
2809
                free(i->path);
1,808✔
2810
                strv_free(i->symlinks);
1,808✔
2811
        }
2812

2813
        d->items = mfree(d->items);
235,310✔
2814
        d->n_items = 0;
235,310✔
2815
        d->mode = 0755;
235,310✔
2816
}
2817

2818
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
5,532✔
2819
        assert(d);
5,532✔
2820
        assert(path);
5,532✔
2821

2822
        FOREACH_ARRAY(i, d->items, d->n_items)
7,876✔
2823
                if (path_equal(i->path, path))
2,359✔
2824
                        return i;
2825

2826
        return NULL;
2827
}
2828

2829
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
5,532✔
UNCOV
2830
        _cleanup_strv_free_ char **s = NULL;
×
2831
        _cleanup_free_ char *p = NULL;
5,532✔
2832
        ExecDirectoryItem *existing;
5,532✔
2833
        int r;
5,532✔
2834

2835
        assert(d);
5,532✔
2836
        assert(path);
5,532✔
2837

2838
        existing = exec_directory_find(d, path);
5,532✔
2839
        if (existing) {
5,532✔
2840
                r = strv_extend(&existing->symlinks, symlink);
15✔
2841
                if (r < 0)
15✔
2842
                        return r;
2843

2844
                existing->flags |= flags;
15✔
2845

2846
                return 0; /* existing item is updated */
15✔
2847
        }
2848

2849
        p = strdup(path);
5,517✔
2850
        if (!p)
5,517✔
2851
                return -ENOMEM;
2852

2853
        if (symlink) {
5,517✔
2854
                s = strv_new(symlink);
6✔
2855
                if (!s)
6✔
2856
                        return -ENOMEM;
2857
        }
2858

2859
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
5,517✔
2860
                return -ENOMEM;
2861

2862
        d->items[d->n_items++] = (ExecDirectoryItem) {
5,517✔
2863
                .path = TAKE_PTR(p),
5,517✔
2864
                .symlinks = TAKE_PTR(s),
5,517✔
2865
                .flags = flags,
2866
        };
2867

2868
        return 1; /* new item is added */
5,517✔
2869
}
2870

2871
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
890✔
2872
        assert(a);
890✔
2873
        assert(b);
890✔
2874

2875
        return path_compare(a->path, b->path);
890✔
2876
}
2877

2878
void exec_directory_sort(ExecDirectory *d) {
129,081✔
2879
        assert(d);
129,081✔
2880

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

2886
        if (d->n_items <= 1)
129,081✔
2887
                return;
2888

2889
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
160✔
2890

2891
        for (size_t i = 1; i < d->n_items; i++)
695✔
2892
                for (size_t j = 0; j < i; j++)
1,780✔
2893
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,260✔
2894
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
2895
                                break;
15✔
2896
                        }
2897
}
2898

UNCOV
2899
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
UNCOV
2900
        ExecDirectoryType t;
×
2901

UNCOV
2902
        assert(s);
×
2903

UNCOV
2904
        if (streq(s, "all"))
×
2905
                return EXEC_CLEAN_ALL;
UNCOV
2906
        if (streq(s, "fdstore"))
×
2907
                return EXEC_CLEAN_FDSTORE;
2908

UNCOV
2909
        t = exec_resource_type_from_string(s);
×
UNCOV
2910
        if (t < 0)
×
2911
                return (ExecCleanMask) t;
2912

UNCOV
2913
        return 1U << t;
×
2914
}
2915

2916
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
2917
        [EXEC_INPUT_NULL]      = "null",
2918
        [EXEC_INPUT_TTY]       = "tty",
2919
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
2920
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
2921
        [EXEC_INPUT_SOCKET]    = "socket",
2922
        [EXEC_INPUT_NAMED_FD]  = "fd",
2923
        [EXEC_INPUT_DATA]      = "data",
2924
        [EXEC_INPUT_FILE]      = "file",
2925
};
2926

2927
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
15,870✔
2928

2929
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
2930
        [EXEC_OUTPUT_INHERIT]             = "inherit",
2931
        [EXEC_OUTPUT_NULL]                = "null",
2932
        [EXEC_OUTPUT_TTY]                 = "tty",
2933
        [EXEC_OUTPUT_KMSG]                = "kmsg",
2934
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
2935
        [EXEC_OUTPUT_JOURNAL]             = "journal",
2936
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
2937
        [EXEC_OUTPUT_SOCKET]              = "socket",
2938
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
2939
        [EXEC_OUTPUT_FILE]                = "file",
2940
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
2941
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
2942
};
2943

2944
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
31,936✔
2945

2946
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
2947
        [EXEC_UTMP_INIT]  = "init",
2948
        [EXEC_UTMP_LOGIN] = "login",
2949
        [EXEC_UTMP_USER]  = "user",
2950
};
2951

2952
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,114✔
2953

2954
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
2955
        [EXEC_PRESERVE_NO]      = "no",
2956
        [EXEC_PRESERVE_YES]     = "yes",
2957
        [EXEC_PRESERVE_RESTART] = "restart",
2958
};
2959

2960
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
17,108✔
2961

2962
/* This table maps ExecDirectoryType to the setting it is configured with in the unit */
2963
static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2964
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectory",
2965
        [EXEC_DIRECTORY_STATE]         = "StateDirectory",
2966
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectory",
2967
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectory",
2968
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectory",
2969
};
2970

2971
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
139,268✔
2972

2973
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
2974
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2975
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
2976
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
2977
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
2978
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
2979
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
2980
};
2981

2982
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
2983

2984
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2985
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
2986
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
2987
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
2988
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
2989
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
2990
};
2991

UNCOV
2992
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
2993

2994
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
2995
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
2996
 * directories, specifically .timer units with their timestamp touch file. */
2997
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
2998
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
2999
        [EXEC_DIRECTORY_STATE]         = "state",
3000
        [EXEC_DIRECTORY_CACHE]         = "cache",
3001
        [EXEC_DIRECTORY_LOGS]          = "logs",
3002
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
3003
};
3004

3005
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
258✔
3006

3007
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3008
        [EXEC_KEYRING_INHERIT] = "inherit",
3009
        [EXEC_KEYRING_PRIVATE] = "private",
3010
        [EXEC_KEYRING_SHARED]  = "shared",
3011
};
3012

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