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

systemd / systemd / 16104615022

06 Jul 2025 08:06PM UTC coverage: 72.1% (+0.04%) from 72.057%
16104615022

push

github

web-flow
Two follow-ups for recent PRs (#38062)

12 of 13 new or added lines in 2 files covered. (92.31%)

1207 existing lines in 43 files now uncovered.

300846 of 417265 relevant lines covered (72.1%)

718237.88 hits per line

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

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

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

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

68
static bool is_terminal_input(ExecInput i) {
35,214✔
69
        return IN_SET(i,
35,214✔
70
                      EXEC_INPUT_TTY,
71
                      EXEC_INPUT_TTY_FORCE,
72
                      EXEC_INPUT_TTY_FAIL);
73
}
74

75
static bool is_terminal_output(ExecOutput o) {
69,337✔
76
        return IN_SET(o,
69,337✔
77
                      EXEC_OUTPUT_TTY,
78
                      EXEC_OUTPUT_KMSG_AND_CONSOLE,
79
                      EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
80
}
81

82
const char* exec_context_tty_path(const ExecContext *context) {
15,021✔
83
        assert(context);
15,021✔
84

85
        if (context->stdio_as_fds)
15,021✔
86
                return NULL;
87

88
        if (context->tty_path)
14,352✔
89
                return context->tty_path;
678✔
90

91
        return "/dev/console";
92
}
93

94
int exec_context_apply_tty_size(
1,863✔
95
                const ExecContext *context,
96
                int input_fd,
97
                int output_fd,
98
                const char *tty_path) {
99

100
        unsigned rows, cols;
1,863✔
101
        int r;
1,863✔
102

103
        assert(context);
1,863✔
104
        assert(input_fd >= 0);
1,863✔
105
        assert(output_fd >= 0);
1,863✔
106

107
        if (!isatty_safe(output_fd))
1,863✔
108
                return 0;
1,863✔
109

110
        if (!tty_path)
1,092✔
111
                tty_path = exec_context_tty_path(context);
418✔
112

113
        /* Preferably use explicitly configured data */
114
        rows = context->tty_rows;
1,092✔
115
        cols = context->tty_cols;
1,092✔
116

117
        /* Fill in data from kernel command line if anything is unspecified */
118
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
1,092✔
119
                (void) proc_cmdline_tty_size(
1,060✔
120
                                tty_path,
121
                                rows == UINT_MAX ? &rows : NULL,
122
                                cols == UINT_MAX ? &cols : NULL);
123

124
        /* If we got nothing so far and we are talking to a physical device, then let's query dimensions from
125
         * the ANSI terminal driver. Note that we will not bother with this in case terminal reset via ansi
126
         * sequences is not enabled, as the DSR logic relies on ANSI sequences after all, and if we shall not
127
         * use those during initialization we need to skip it. */
128
        if (rows == UINT_MAX && cols == UINT_MAX &&
1,299✔
129
            exec_context_shall_ansi_seq_reset(context) &&
386✔
130
            isatty_safe(input_fd)) {
179✔
131
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
179✔
132
                if (r < 0)
179✔
133
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
179✔
134
        }
135

136
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
1,092✔
137
}
138

139
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *parameters, sd_id128_t invocation_id) {
13,354✔
140
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
26,708✔
141
        int fd, r;
13,354✔
142

143
        assert(context);
13,354✔
144

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

150
        const char *path = exec_context_tty_path(context);
13,354✔
151

152
        if (parameters && parameters->stdout_fd >= 0 && isatty_safe(parameters->stdout_fd))
13,354✔
153
                fd = parameters->stdout_fd;
16✔
154
        else if (path && (context->tty_path || is_terminal_input(context->std_input) ||
13,338✔
155
                        is_terminal_output(context->std_output) || is_terminal_output(context->std_error))) {
12,342✔
156
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
674✔
157
                if (fd < 0)
674✔
UNCOV
158
                        return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path);
×
159
        } else
160
                return;   /* nothing to do */
161

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

173
        if (context->tty_reset)
690✔
174
                (void) terminal_reset_defensive(
171✔
175
                                fd,
176
                                TERMINAL_RESET_SWITCH_TO_TEXT |
177
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
168✔
178

179
        r = exec_context_apply_tty_size(context, fd, fd, path);
690✔
180
        if (r < 0)
690✔
181
                log_debug_errno(r, "Failed to configure TTY dimensions, ignoring: %m");
×
182

183
        if (!sd_id128_is_null(invocation_id)) {
1,329✔
184
                sd_id128_t context_id;
51✔
185

186
                r = osc_context_id_from_invocation_id(invocation_id, &context_id);
51✔
187
                if (r < 0)
51✔
188
                        log_debug_errno(r, "Failed to derive context ID from invocation ID, ignoring: %m");
51✔
189
                else {
190
                        _cleanup_free_ char *seq = NULL;
51✔
191

192
                        r = osc_context_close(context_id, &seq);
51✔
193
                        if (r < 0)
51✔
194
                                log_debug_errno(r, "Failed to acquire OSC close sequence, ignoring: %m");
×
195
                        else
196
                                (void) loop_write(fd, seq, SIZE_MAX);
51✔
197
                }
198
        }
199

200
        if (context->tty_vhangup)
690✔
201
                (void) terminal_vhangup_fd(fd);
163✔
202

203
        /* We don't need the fd anymore now, and it potentially points to a hungup TTY anyway, let's close it
204
         * hence. */
205
        _fd = safe_close(_fd);
690✔
206

207
        if (context->tty_vt_disallocate && path)
690✔
208
                (void) vt_disallocate(path);
89✔
209
}
210

211
bool exec_needs_network_namespace(const ExecContext *context) {
58,661✔
212
        assert(context);
58,661✔
213

214
        return context->private_network || context->network_namespace_path;
58,661✔
215
}
216

217
static bool exec_needs_ephemeral(const ExecContext *context) {
6,440✔
218
        return (context->root_image || context->root_directory) && context->root_ephemeral;
6,440✔
219
}
220

221
bool exec_needs_ipc_namespace(const ExecContext *context) {
53,975✔
222
        assert(context);
53,975✔
223

224
        return context->private_ipc || context->ipc_namespace_path;
53,975✔
225
}
226

227
static bool needs_cgroup_namespace(ProtectControlGroups i) {
137,636✔
228
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
137,636✔
229
}
230

231
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
84,590✔
232
        assert(context);
84,590✔
233

234
        /* If cgroup namespace is configured via ProtectControlGroups=private or strict but we can't actually
235
         * use cgroup namespace, we ignore the setting and do not unshare the namespace.
236
         * ProtectControlGroups=private and strict get downgraded to no and yes respectively. This ensures
237
         * that strict always gets a read-only mount of /sys/fs/cgroup/. */
238
        if (needs_cgroup_namespace(context->protect_control_groups) && !namespace_type_supported(NAMESPACE_CGROUP)) {
84,590✔
239
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_PRIVATE)
×
240
                        return PROTECT_CONTROL_GROUPS_NO;
241
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_STRICT)
×
242
                        return PROTECT_CONTROL_GROUPS_YES;
243
        }
244
        return context->protect_control_groups;
84,590✔
245
}
246

247
bool exec_needs_cgroup_namespace(const ExecContext *context) {
53,046✔
248
        assert(context);
53,046✔
249

250
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
53,046✔
251
}
252

253
bool exec_needs_cgroup_mount(const ExecContext *context) {
27,486✔
254
        assert(context);
27,486✔
255

256
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
27,486✔
257
}
258

259
bool exec_is_cgroup_mount_read_only(const ExecContext *context) {
2,029✔
260
        assert(context);
2,029✔
261

262
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
2,029✔
263
}
264

265
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
75,390✔
266
        assert(context);
75,390✔
267

268
        /* PID namespaces don't really make sense for control processes so let's not use them for those. */
269
        if (params && FLAGS_SET(params->flags, EXEC_IS_CONTROL))
75,390✔
270
                return false;
271

272
        return context->private_pids != PRIVATE_PIDS_NO && namespace_type_supported(NAMESPACE_PID);
68,404✔
273
}
274

275
bool exec_needs_mount_namespace(
32,818✔
276
                const ExecContext *context,
277
                const ExecParameters *params,
278
                const ExecRuntime *runtime) {
279

280
        assert(context);
32,818✔
281

282
        if (context->root_image)
32,818✔
283
                return true;
284

285
        if (!strv_isempty(context->read_write_paths) ||
32,793✔
286
            !strv_isempty(context->read_only_paths) ||
30,682✔
287
            !strv_isempty(context->inaccessible_paths) ||
30,669✔
288
            !strv_isempty(context->exec_paths) ||
30,644✔
289
            !strv_isempty(context->no_exec_paths))
30,644✔
290
                return true;
291

292
        if (context->n_bind_mounts > 0)
30,644✔
293
                return true;
294

295
        if (context->n_temporary_filesystems > 0)
30,569✔
296
                return true;
297

298
        if (context->n_mount_images > 0)
30,410✔
299
                return true;
300

301
        if (context->n_extension_images > 0)
30,387✔
302
                return true;
303

304
        if (!strv_isempty(context->extension_directories))
30,376✔
305
                return true;
306

307
        if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
30,371✔
308
                return true;
309

310
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
30,371✔
311
                return true;
312

313
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
29,361✔
314
                return true;
315

316
        if (context->private_devices ||
28,884✔
317
            context->private_mounts > 0 ||
28,513✔
318
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,049✔
319
            context->protect_system != PROTECT_SYSTEM_NO ||
28,020✔
320
            context->protect_home != PROTECT_HOME_NO ||
28,020✔
321
            context->protect_kernel_tunables ||
28,020✔
322
            context->protect_kernel_modules ||
28,020✔
323
            context->protect_kernel_logs ||
51,829✔
324
            exec_needs_cgroup_mount(context) ||
25,913✔
325
            context->protect_proc != PROTECT_PROC_DEFAULT ||
25,895✔
326
            context->proc_subset != PROC_SUBSET_ALL ||
51,655✔
327
            exec_needs_ipc_namespace(context) ||
51,640✔
328
            exec_needs_pid_namespace(context, params))
25,820✔
329
                return true;
3,117✔
330

331
        if (context->root_directory) {
25,767✔
332
                if (exec_context_get_effective_mount_apivfs(context))
5✔
333
                        return true;
334

335
                for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
336
                        if (params && !params->prefix[t])
×
337
                                continue;
×
338

339
                        if (context->directories[t].n_items > 0)
×
340
                                return true;
341
                }
342
        }
343

344
        if (context->dynamic_user &&
25,762✔
345
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
346
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
347
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
348
                return true;
349

350
        if (exec_context_get_effective_bind_log_sockets(context))
25,762✔
351
                return true;
352

353
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
154,228✔
354
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
133,700✔
355
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,228✔
356
                                return true;
357

358
        return false;
359
}
360

361
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
3,945✔
362
        assert(context);
3,945✔
363
        assert(params);
3,945✔
364

365
        if (!params->notify_socket)
3,945✔
366
                return NULL;
367

368
        if (!needs_sandboxing)
3,293✔
369
                return NULL;
370

371
        if (!context->root_directory && !context->root_image)
3,293✔
372
                return NULL;
373

374
        if (!exec_context_get_effective_mount_apivfs(context))
×
375
                return NULL;
376

377
        if (!FLAGS_SET(params->flags, EXEC_APPLY_CHROOT))
×
378
                return NULL;
×
379

380
        return "/run/host/notify";
381
}
382

383
int exec_log_level_max(const ExecContext *context, const ExecParameters *params) {
23,664✔
384
        assert(context);
23,664✔
385
        assert(params);
23,664✔
386

387
        if (params->debug_invocation)
23,664✔
388
                return LOG_DEBUG;
389

390
        return context->log_level_max < 0 ? log_get_max_level() : context->log_level_max;
23,660✔
391
}
392

393
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
12,073✔
394
        assert(context);
12,073✔
395

396
        if (!context->dynamic_user)
12,073✔
397
                return false;
398

399
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
104✔
400
                return false;
401

402
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
98✔
403
                return false;
14✔
404

405
        return true;
406
}
407

408
int exec_params_needs_control_subcgroup(const ExecParameters *params) {
2,274✔
409
        /* Keep this in sync with exec_params_get_cgroup_path(). */
410
        return FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE|EXEC_CONTROL_CGROUP|EXEC_IS_CONTROL);
2,274✔
411
}
412

413
int exec_params_get_cgroup_path(
12,514✔
414
                const ExecParameters *params,
415
                const CGroupContext *c,
416
                const char *prefix,
417
                char **ret) {
418

419
        const char *subgroup = NULL;
12,514✔
420
        char *p;
12,514✔
421

422
        assert(params);
12,514✔
423
        assert(c);
12,514✔
424
        assert(ret);
12,514✔
425

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

435
        /* Keep this in sync with exec_params_needs_control_subcgroup(). */
436
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
12,514✔
437
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
664✔
438
                        subgroup = ".control";
439
                else
440
                        subgroup = c->delegate_subgroup;
628✔
441
        }
442

443
        if (subgroup)
628✔
444
                p = path_join(prefix, subgroup);
664✔
445
        else
446
                p = strdup(strempty(prefix));
11,858✔
447
        if (!p)
12,514✔
448
                return -ENOMEM;
449

450
        *ret = p;
12,514✔
451
        return !!subgroup;
12,514✔
452
}
453

454
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
1,472✔
455
        assert(c);
1,472✔
456

457
        return c->cpu_affinity_from_numa;
1,472✔
458
}
459

460
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
2,244✔
461
        assert(unit);
2,244✔
462
        assert(msg);
2,244✔
463
        assert(executable);
2,244✔
464

465
        if (!DEBUG_LOGGING)
2,244✔
466
                return;
2,244✔
467

468
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
4,488✔
469

470
        log_unit_struct(unit, LOG_DEBUG,
2,244✔
471
                        LOG_ITEM("EXECUTABLE=%s", executable),
472
                        LOG_UNIT_MESSAGE(unit, "%s: %s", msg, strnull(cmdline)),
473
                        LOG_UNIT_INVOCATION_ID(unit));
474
}
475

476
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
477

478
int exec_spawn(
2,244✔
479
                Unit *unit,
480
                ExecCommand *command,
481
                const ExecContext *context,
482
                ExecParameters *params,
483
                ExecRuntime *runtime,
484
                const CGroupContext *cgroup_context,
485
                PidRef *ret) {
486

487
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL;
2,244✔
488
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
489
        _cleanup_fclose_ FILE *f = NULL;
2,244✔
490
        int r;
2,244✔
491

492
        assert(unit);
2,244✔
493
        assert(unit->manager);
2,244✔
494
        assert(unit->manager->executor_fd >= 0);
2,244✔
495
        assert(unit->manager->executor_path);
2,244✔
496
        assert(command);
2,244✔
497
        assert(context);
2,244✔
498
        assert(params);
2,244✔
499
        assert(!params->fds || FLAGS_SET(params->flags, EXEC_PASS_FDS));
2,244✔
500
        assert(params->fds || (params->n_socket_fds + params->n_storage_fds + params->n_extra_fds == 0));
2,244✔
501
        assert(!params->files_env); /* We fill this field, ensure it comes NULL-initialized to us */
2,244✔
502
        assert(ret);
2,244✔
503

504
        LOG_CONTEXT_PUSH_UNIT(unit);
4,488✔
505

506
        r = exec_context_load_environment(unit, context, &params->files_env);
2,244✔
507
        if (r < 0)
2,244✔
508
                return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
×
509

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

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

521
        const char *cgtarget;
2,244✔
522
        if (exec_params_needs_control_subcgroup(params)) {
2,244✔
523
                r = exec_params_get_cgroup_path(params, cgroup_context, params->cgroup_path, &subcgroup_path);
×
524
                if (r < 0)
×
525
                        return log_unit_error_errno(unit, r, "Failed to acquire subcgroup path: %m");
×
526
                if (r > 0) {
×
527
                        /* If there's a subcgroup, then let's create it here now (the main cgroup was already
528
                         * realized by the unit logic) */
529

530
                        r = cg_create(subcgroup_path);
×
531
                        if (r < 0)
×
532
                                return log_unit_error_errno(unit, r, "Failed to create subcgroup '%s': %m", subcgroup_path);
×
533
                }
534

535
                cgtarget = subcgroup_path;
×
536
        } else
537
                cgtarget = params->cgroup_path;
2,244✔
538

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

546
        r = open_serialization_file("sd-executor-state", &f);
2,244✔
547
        if (r < 0)
2,244✔
548
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
549

550
        fdset = fdset_new();
2,244✔
551
        if (!fdset)
2,244✔
552
                return log_oom();
×
553

554
        r = exec_serialize_invocation(f, fdset, context, command, params, runtime, cgroup_context);
2,244✔
555
        if (r < 0)
2,244✔
556
                return log_unit_error_errno(unit, r, "Failed to serialize parameters: %m");
×
557

558
        r = finish_serialization_file(f);
2,244✔
559
        if (r < 0)
2,244✔
560
                return log_unit_error_errno(unit, r, "Failed to finish serialization stream: %m");
×
561

562
        r = fd_cloexec(fileno(f), false);
2,244✔
563
        if (r < 0)
2,244✔
564
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m");
×
565

566
        r = fdset_cloexec(fdset, false);
2,244✔
567
        if (r < 0)
2,244✔
568
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m");
×
569

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

577
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,244✔
578
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,244✔
579

580
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,244✔
581
        dual_timestamp start_timestamp;
2,244✔
582

583
        /* Restore the original ambient capability set the manager was started with to pass it to
584
         * sd-executor. */
585
        r = capability_ambient_set_apply(unit->manager->saved_ambient_set, /* also_inherit= */ false);
2,244✔
586
        if (r < 0)
2,244✔
587
                return log_unit_error_errno(unit, r, "Failed to apply the starting ambient set: %m");
×
588

589
        /* Record the start timestamp before we fork so that it is guaranteed to be earlier than the
590
         * handoff timestamp. */
591
        dual_timestamp_now(&start_timestamp);
2,244✔
592

593
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
594
        r = posix_spawn_wrapper(
2,244✔
595
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
2,244✔
596
                        STRV_MAKE(unit->manager->executor_path,
2,244✔
597
                                  "--deserialize", serialization_fd_number,
598
                                  "--log-level", max_log_levels,
599
                                  "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))),
600
                        environ,
601
                        cgtarget,
602
                        &pidref);
603

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

607
        if (r == -EUCLEAN && cgtarget)
2,244✔
608
                return log_unit_error_errno(unit, r,
×
609
                                            "Failed to spawn process into cgroup '%s', because the cgroup "
610
                                            "or one of its parents or siblings is in the threaded mode.",
611
                                            cgtarget);
612
        if (r < 0)
2,244✔
613
                return log_unit_error_errno(unit, r, "Failed to spawn executor: %m");
×
614
        /* We add the new process to the cgroup both in the child (so that we can be sure that no user code is ever
615
         * executed outside of the cgroup) and in the parent (so that we can be sure that when we kill the cgroup the
616
         * process will be killed too). */
617
        if (r == 0 && cgtarget)
2,244✔
618
                (void) cg_attach(cgtarget, pidref.pid);
×
619
        /* r > 0: Already in the right cgroup thanks to CLONE_INTO_CGROUP */
620

621
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
2,244✔
622
                       command->path, pidref.pid, r > 0 ? "via" : "without");
623

624
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,244✔
625

626
        *ret = TAKE_PIDREF(pidref);
2,244✔
627
        return 0;
2,244✔
628
}
629

630
void exec_context_init(ExecContext *c) {
59,412✔
631
        assert(c);
59,412✔
632

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

636
        *c = (ExecContext) {
59,412✔
637
                .umask = 0022,
638
                .ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO,
59,412✔
639
                .cpu_sched_policy = SCHED_OTHER,
640
                .syslog_priority = LOG_DAEMON|LOG_INFO,
641
                .syslog_level_prefix = true,
642
                .ignore_sigpipe = true,
643
                .timer_slack_nsec = NSEC_INFINITY,
644
                .personality = PERSONALITY_INVALID,
645
                .timeout_clean_usec = USEC_INFINITY,
646
                .capability_bounding_set = CAP_MASK_UNSET,
647
                .restrict_namespaces = NAMESPACE_FLAGS_INITIAL,
648
                .delegate_namespaces = NAMESPACE_FLAGS_INITIAL,
649
                .log_level_max = -1,
650
#if HAVE_SECCOMP
651
                .syscall_errno = SECCOMP_ERROR_NUMBER_KILL,
652
#endif
653
                .tty_rows = UINT_MAX,
654
                .tty_cols = UINT_MAX,
655
                .private_mounts = -1,
656
                .mount_apivfs = -1,
657
                .bind_log_sockets = -1,
658
                .memory_ksm = -1,
659
                .private_var_tmp = _PRIVATE_TMP_INVALID,
660
                .set_login_environment = -1,
661
        };
662

663
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
356,472✔
664
                d->mode = 0755;
297,060✔
665

666
        numa_policy_reset(&c->numa_policy);
59,412✔
667

668
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
59,412✔
669
}
59,412✔
670

671
void exec_context_done(ExecContext *c) {
47,609✔
672
        assert(c);
47,609✔
673

674
        c->environment = strv_free(c->environment);
47,609✔
675
        c->environment_files = strv_free(c->environment_files);
47,609✔
676
        c->pass_environment = strv_free(c->pass_environment);
47,609✔
677
        c->unset_environment = strv_free(c->unset_environment);
47,609✔
678

679
        rlimit_free_all(c->rlimit);
47,609✔
680

681
        for (size_t l = 0; l < 3; l++) {
190,436✔
682
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
142,827✔
683
                c->stdio_file[l] = mfree(c->stdio_file[l]);
142,827✔
684
        }
685

686
        c->working_directory = mfree(c->working_directory);
47,609✔
687
        c->root_directory = mfree(c->root_directory);
47,609✔
688
        c->root_image = mfree(c->root_image);
47,609✔
689
        c->root_image_options = mount_options_free_all(c->root_image_options);
47,609✔
690
        c->root_hash = mfree(c->root_hash);
47,609✔
691
        c->root_hash_size = 0;
47,609✔
692
        c->root_hash_path = mfree(c->root_hash_path);
47,609✔
693
        c->root_hash_sig = mfree(c->root_hash_sig);
47,609✔
694
        c->root_hash_sig_size = 0;
47,609✔
695
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
47,609✔
696
        c->root_verity = mfree(c->root_verity);
47,609✔
697
        c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
47,609✔
698
        c->extension_directories = strv_free(c->extension_directories);
47,609✔
699
        c->tty_path = mfree(c->tty_path);
47,609✔
700
        c->syslog_identifier = mfree(c->syslog_identifier);
47,609✔
701
        c->user = mfree(c->user);
47,609✔
702
        c->group = mfree(c->group);
47,609✔
703

704
        c->supplementary_groups = strv_free(c->supplementary_groups);
47,609✔
705

706
        c->pam_name = mfree(c->pam_name);
47,609✔
707

708
        c->read_only_paths = strv_free(c->read_only_paths);
47,609✔
709
        c->read_write_paths = strv_free(c->read_write_paths);
47,609✔
710
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
47,609✔
711
        c->exec_paths = strv_free(c->exec_paths);
47,609✔
712
        c->no_exec_paths = strv_free(c->no_exec_paths);
47,609✔
713
        c->exec_search_path = strv_free(c->exec_search_path);
47,609✔
714

715
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
47,609✔
716
        c->bind_mounts = NULL;
47,609✔
717
        c->n_bind_mounts = 0;
47,609✔
718
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
47,609✔
719
        c->temporary_filesystems = NULL;
47,609✔
720
        c->n_temporary_filesystems = 0;
47,609✔
721
        c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
47,609✔
722

723
        cpu_set_done(&c->cpu_set);
47,609✔
724
        numa_policy_reset(&c->numa_policy);
47,609✔
725

726
        c->utmp_id = mfree(c->utmp_id);
47,609✔
727
        c->selinux_context = mfree(c->selinux_context);
47,609✔
728
        c->apparmor_profile = mfree(c->apparmor_profile);
47,609✔
729
        c->smack_process_label = mfree(c->smack_process_label);
47,609✔
730

731
        c->restrict_filesystems = set_free(c->restrict_filesystems);
47,609✔
732

733
        c->syscall_filter = hashmap_free(c->syscall_filter);
47,609✔
734
        c->syscall_archs = set_free(c->syscall_archs);
47,609✔
735
        c->syscall_log = hashmap_free(c->syscall_log);
47,609✔
736
        c->address_families = set_free(c->address_families);
47,609✔
737

738
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
285,654✔
739
                exec_directory_done(d);
238,045✔
740

741
        c->log_level_max = -1;
47,609✔
742

743
        exec_context_free_log_extra_fields(c);
47,609✔
744
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
47,609✔
745
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
47,609✔
746

747
        c->log_ratelimit = (RateLimit) {};
47,609✔
748

749
        c->stdin_data = mfree(c->stdin_data);
47,609✔
750
        c->stdin_data_size = 0;
47,609✔
751

752
        c->network_namespace_path = mfree(c->network_namespace_path);
47,609✔
753
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
47,609✔
754

755
        c->log_namespace = mfree(c->log_namespace);
47,609✔
756

757
        c->load_credentials = hashmap_free(c->load_credentials);
47,609✔
758
        c->set_credentials = hashmap_free(c->set_credentials);
47,609✔
759
        c->import_credentials = ordered_set_free(c->import_credentials);
47,609✔
760

761
        c->root_image_policy = image_policy_free(c->root_image_policy);
47,609✔
762
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
47,609✔
763
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
47,609✔
764

765
        c->private_hostname = mfree(c->private_hostname);
47,609✔
766
}
47,609✔
767

768
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
5,057✔
769
        assert(c);
5,057✔
770

771
        if (!runtime_prefix)
5,057✔
772
                return 0;
773

774
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
5,062✔
775
                _cleanup_free_ char *p = NULL;
5✔
776

777
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
5✔
778
                        p = path_join(runtime_prefix, "private", i->path);
×
779
                else
780
                        p = path_join(runtime_prefix, i->path);
5✔
781
                if (!p)
5✔
782
                        return -ENOMEM;
783

784
                /* We execute this synchronously, since we need to be sure this is gone when we start the
785
                 * service next. */
786
                (void) rm_rf(p, REMOVE_ROOT);
5✔
787

788
                STRV_FOREACH(symlink, i->symlinks) {
5✔
789
                        _cleanup_free_ char *symlink_abs = NULL;
×
790

791
                        if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
×
792
                                symlink_abs = path_join(runtime_prefix, "private", *symlink);
×
793
                        else
794
                                symlink_abs = path_join(runtime_prefix, *symlink);
×
795
                        if (!symlink_abs)
×
796
                                return -ENOMEM;
×
797

798
                        (void) unlink(symlink_abs);
×
799
                }
800
        }
801

802
        return 0;
803
}
804

805
int exec_context_destroy_mount_ns_dir(Unit *u) {
11,111✔
806
        _cleanup_free_ char *p = NULL;
11,111✔
807

808
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
11,111✔
809
                return 0;
810

811
        p = path_join("/run/systemd/propagate/", u->id);
2,109✔
812
        if (!p)
2,109✔
813
                return -ENOMEM;
814

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

819
        return 0;
820
}
821

822
void exec_command_done(ExecCommand *c) {
100,779✔
823
        assert(c);
100,779✔
824

825
        c->path = mfree(c->path);
100,779✔
826
        c->argv = strv_free(c->argv);
100,779✔
827
}
100,779✔
828

829
void exec_command_done_array(ExecCommand *c, size_t n) {
28,088✔
830
        FOREACH_ARRAY(i, c, n)
112,350✔
831
                exec_command_done(i);
84,262✔
832
}
28,088✔
833

834
ExecCommand* exec_command_free(ExecCommand *c) {
16,489✔
835
        if (!c)
16,489✔
836
                return NULL;
837

838
        exec_command_done(c);
16,489✔
839
        return mfree(c);
16,489✔
840
}
841

842
ExecCommand* exec_command_free_list(ExecCommand *c) {
125,262✔
843
        ExecCommand *i;
125,262✔
844

845
        while ((i = LIST_POP(command, c)))
141,751✔
846
                exec_command_free(i);
16,489✔
847

848
        return NULL;
125,262✔
849
}
850

851
void exec_command_free_array(ExecCommand **c, size_t n) {
19,493✔
852
        FOREACH_ARRAY(i, c, n)
144,740✔
853
                *i = exec_command_free_list(*i);
125,247✔
854
}
19,493✔
855

856
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
6,970✔
857
        FOREACH_ARRAY(i, c, n)
27,879✔
858
                exec_status_reset(&i->exec_status);
20,909✔
859
}
6,970✔
860

861
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
4,365✔
862
        FOREACH_ARRAY(i, c, n)
30,072✔
863
                LIST_FOREACH(command, z, *i)
27,871✔
864
                        exec_status_reset(&z->exec_status);
2,164✔
865
}
4,365✔
866

867
typedef struct InvalidEnvInfo {
868
        const Unit *unit;
869
        const char *path;
870
} InvalidEnvInfo;
871

872
static void invalid_env(const char *p, void *userdata) {
×
873
        InvalidEnvInfo *info = userdata;
×
874

875
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
876
}
×
877

878
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
39,912✔
879
        assert(c);
39,912✔
880

881
        switch (fd_index) {
39,912✔
882

883
        case STDIN_FILENO:
13,304✔
884
                if (c->std_input != EXEC_INPUT_NAMED_FD)
13,304✔
885
                        return NULL;
886

887
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
888

889
        case STDOUT_FILENO:
13,304✔
890
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
13,304✔
891
                        return NULL;
892

893
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
894

895
        case STDERR_FILENO:
13,304✔
896
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
13,304✔
897
                        return NULL;
898

899
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
900

901
        default:
902
                return NULL;
903
        }
904
}
905

906
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
2,244✔
907
        _cleanup_strv_free_ char **v = NULL;
2,244✔
908
        int r;
2,244✔
909

910
        assert(c);
2,244✔
911
        assert(ret);
2,244✔
912

913
        STRV_FOREACH(i, c->environment_files) {
2,246✔
914
                _cleanup_strv_free_ char **paths = NULL;
2✔
915
                bool ignore = false;
2✔
916
                char *fn = *i;
2✔
917

918
                if (fn[0] == '-') {
2✔
919
                        ignore = true;
1✔
920
                        fn++;
1✔
921
                }
922

923
                if (!path_is_absolute(fn)) {
2✔
924
                        if (ignore)
×
925
                                continue;
×
926
                        return -EINVAL;
927
                }
928

929
                /* Filename supports globbing, take all matching files */
930
                r = safe_glob(fn, /* flags = */ 0, &paths);
2✔
931
                if (r < 0) {
2✔
932
                        if (ignore)
1✔
933
                                continue;
1✔
934
                        return r;
935
                }
936

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

940
                STRV_FOREACH(path, paths) {
2✔
941
                        _cleanup_strv_free_ char **p = NULL;
1✔
942

943
                        r = load_env_file(NULL, *path, &p);
1✔
944
                        if (r < 0) {
1✔
945
                                if (ignore)
×
946
                                        continue;
×
947
                                return r;
948
                        }
949

950
                        /* Log invalid environment variables with filename */
951
                        if (p) {
1✔
952
                                InvalidEnvInfo info = {
1✔
953
                                        .unit = unit,
954
                                        .path = *path,
1✔
955
                                };
956

957
                                strv_env_clean_with_callback(p, invalid_env, &info);
1✔
958
                        }
959

960
                        if (!v)
1✔
961
                                v = TAKE_PTR(p);
1✔
962
                        else {
963
                                char **m = strv_env_merge(v, p);
×
964
                                if (!m)
×
965
                                        return -ENOMEM;
×
966

967
                                strv_free_and_replace(v, m);
×
968
                        }
969
                }
970
        }
971

972
        *ret = TAKE_PTR(v);
2,244✔
973

974
        return 0;
2,244✔
975
}
976

977
static bool tty_may_match_dev_console(const char *tty) {
368✔
978
        _cleanup_free_ char *resolved = NULL;
368✔
979

980
        if (!tty)
368✔
981
                return true;
982

983
        tty = skip_dev_prefix(tty);
368✔
984

985
        /* trivial identity? */
986
        if (streq(tty, "console"))
368✔
987
                return true;
988

989
        if (resolve_dev_console(&resolved) < 0)
38✔
990
                return true; /* if we could not resolve, assume it may */
991

992
        /* "tty0" means the active VC, so it may be the same sometimes */
993
        return path_equal(skip_dev_prefix(resolved), tty) || (streq(skip_dev_prefix(resolved), "tty0") && tty_is_vc(tty));
38✔
994
}
995

996
static bool exec_context_may_touch_tty(const ExecContext *ec) {
22,805✔
997
        assert(ec);
22,805✔
998

999
        return ec->tty_reset ||
45,463✔
1000
                ec->tty_vhangup ||
22,658✔
1001
                ec->tty_vt_disallocate ||
22,658✔
1002
                is_terminal_input(ec->std_input) ||
22,658✔
1003
                is_terminal_output(ec->std_output) ||
45,368✔
1004
                is_terminal_output(ec->std_error);
22,386✔
1005
}
1006

1007
bool exec_context_may_touch_console(const ExecContext *ec) {
21,282✔
1008

1009
        return exec_context_may_touch_tty(ec) &&
21,650✔
1010
               tty_may_match_dev_console(exec_context_tty_path(ec));
368✔
1011
}
1012

1013
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,548✔
1014
        assert(c);
1,548✔
1015

1016
        /* Determines whether ANSI sequences shall be used during any terminal initialisation:
1017
         *
1018
         * 1. If the reset logic is enabled at all, this is an immediate no.
1019
         *
1020
         * 2. If $TERM is set to anything other than "dumb", it's a yes.
1021
         */
1022

1023
        if (!c->tty_reset)
1,548✔
1024
                return false;
1025

1026
        return !streq_ptr(strv_env_get(c->environment, "TERM"), "dumb");
507✔
1027
}
1028

1029
static void strv_fprintf(FILE *f, char **l) {
×
1030
        assert(f);
×
1031

1032
        STRV_FOREACH(g, l)
×
1033
                fprintf(f, " %s", *g);
×
1034
}
×
1035

1036
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
1,792✔
1037
        assert(f);
1,792✔
1038
        assert(prefix);
1,792✔
1039
        assert(name);
1,792✔
1040

1041
        if (!strv_isempty(strv)) {
1,792✔
1042
                fprintf(f, "%s%s:", prefix, name);
×
1043
                strv_fprintf(f, strv);
×
1044
                fputs("\n", f);
×
1045
        }
1046
}
1,792✔
1047

1048
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1049
        assert(p);
×
1050
        assert(f);
×
1051

1052
        prefix = strempty(prefix);
×
1053

1054
        fprintf(f,
×
1055
                "%sRuntimeScope: %s\n"
1056
                "%sExecFlags: %u\n"
1057
                "%sSELinuxContextNetwork: %s\n"
1058
                "%sCgroupSupportedMask: %u\n"
1059
                "%sCgroupPath: %s\n"
1060
                "%sCrededentialsDirectory: %s\n"
1061
                "%sEncryptedCredentialsDirectory: %s\n"
1062
                "%sConfirmSpawn: %s\n"
1063
                "%sShallConfirmSpawn: %s\n"
1064
                "%sWatchdogUSec: " USEC_FMT "\n"
1065
                "%sNotifySocket: %s\n"
1066
                "%sDebugInvocation: %s\n"
1067
                "%sFallbackSmackProcessLabel: %s\n",
1068
                prefix, runtime_scope_to_string(p->runtime_scope),
×
1069
                prefix, p->flags,
×
1070
                prefix, yes_no(p->selinux_context_net),
×
1071
                prefix, p->cgroup_supported,
×
1072
                prefix, p->cgroup_path,
×
1073
                prefix, strempty(p->received_credentials_directory),
×
1074
                prefix, strempty(p->received_encrypted_credentials_directory),
×
1075
                prefix, strempty(p->confirm_spawn),
×
1076
                prefix, yes_no(p->shall_confirm_spawn),
×
1077
                prefix, p->watchdog_usec,
×
1078
                prefix, strempty(p->notify_socket),
×
1079
                prefix, yes_no(p->debug_invocation),
×
1080
                prefix, strempty(p->fallback_smack_process_label));
×
1081

1082
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1083
        strv_dump(f, prefix, "Environment", p->environment);
×
1084
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1085

1086
        LIST_FOREACH(open_files, file, p->open_files)
×
1087
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1088

1089
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1090
}
×
1091

1092
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
224✔
1093
        int r;
224✔
1094

1095
        assert(c);
224✔
1096
        assert(f);
224✔
1097

1098
        prefix = strempty(prefix);
224✔
1099

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

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

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

1162
        if (c->root_image_options) {
224✔
1163
                fprintf(f, "%sRootImageOptions:", prefix);
×
1164
                LIST_FOREACH(mount_options, o, c->root_image_options)
×
1165
                        if (!isempty(o->options))
×
1166
                                fprintf(f, " %s:%s",
×
1167
                                        partition_designator_to_string(o->partition_designator),
1168
                                        o->options);
1169
                fprintf(f, "\n");
×
1170
        }
1171

1172
        if (c->root_hash) {
224✔
1173
                _cleanup_free_ char *encoded = NULL;
×
1174
                encoded = hexmem(c->root_hash, c->root_hash_size);
×
1175
                if (encoded)
×
1176
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1177
        }
1178

1179
        if (c->root_hash_path)
224✔
1180
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1181

1182
        if (c->root_hash_sig) {
224✔
1183
                _cleanup_free_ char *encoded = NULL;
×
1184
                ssize_t len;
×
1185
                len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded);
×
1186
                if (len)
×
1187
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1188
        }
1189

1190
        if (c->root_hash_sig_path)
224✔
1191
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1192

1193
        if (c->root_verity)
224✔
1194
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1195

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

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

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

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

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

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

1213
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
1,130✔
1214
                        fprintf(f,
10✔
1215
                                "%s%s: %s%s\n",
1216
                                prefix,
1217
                                exec_directory_type_to_string(dt),
1218
                                c->directories[dt].items[i].path,
1219
                                FLAGS_SET(c->directories[dt].items[i].flags, EXEC_DIRECTORY_READ_ONLY) ? " (ro)" : "");
10✔
1220

1221
                        STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
10✔
1222
                                fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
×
1223
                }
1224
        }
1225

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

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

1231
        if (c->nice_set)
224✔
1232
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
×
1233

1234
        if (c->oom_score_adjust_set)
224✔
1235
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
10✔
1236

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

1240
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
3,808✔
1241
                if (c->rlimit[i]) {
3,584✔
1242
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
20✔
1243
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1244
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
20✔
1245
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
20✔
1246
                }
1247

1248
        if (c->ioprio_is_set) {
224✔
1249
                _cleanup_free_ char *class_str = NULL;
×
1250

1251
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
×
1252
                if (r >= 0)
×
1253
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
×
1254

1255
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
×
1256
        }
1257

1258
        if (c->cpu_sched_set) {
224✔
1259
                _cleanup_free_ char *policy_str = NULL;
×
1260

1261
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1262
                if (r >= 0)
×
1263
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1264

1265
                fprintf(f,
×
1266
                        "%sCPUSchedulingPriority: %i\n"
1267
                        "%sCPUSchedulingResetOnFork: %s\n",
1268
                        prefix, c->cpu_sched_priority,
×
1269
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1270
        }
1271

1272
        if (c->cpu_set.set) {
224✔
1273
                _cleanup_free_ char *affinity = NULL;
×
1274

1275
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1276
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1277
        }
1278

1279
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
224✔
1280
                _cleanup_free_ char *nodes = NULL;
1✔
1281

1282
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1283
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1284
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1285
        }
1286

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

1290
        fprintf(f,
224✔
1291
                "%sStandardInput: %s\n"
1292
                "%sStandardOutput: %s\n"
1293
                "%sStandardError: %s\n",
1294
                prefix, exec_input_to_string(c->std_input),
224✔
1295
                prefix, exec_output_to_string(c->std_output),
224✔
1296
                prefix, exec_output_to_string(c->std_error));
224✔
1297

1298
        if (c->std_input == EXEC_INPUT_NAMED_FD)
224✔
1299
                fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
×
1300
        if (c->std_output == EXEC_OUTPUT_NAMED_FD)
224✔
1301
                fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
×
1302
        if (c->std_error == EXEC_OUTPUT_NAMED_FD)
224✔
1303
                fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
×
1304

1305
        if (c->std_input == EXEC_INPUT_FILE)
224✔
1306
                fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
×
1307
        if (c->std_output == EXEC_OUTPUT_FILE)
224✔
1308
                fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1309
        if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
224✔
1310
                fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1311
        if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1312
                fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1313
        if (c->std_error == EXEC_OUTPUT_FILE)
224✔
1314
                fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1315
        if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
224✔
1316
                fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1317
        if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1318
                fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1319

1320
        if (c->tty_path)
224✔
1321
                fprintf(f,
×
1322
                        "%sTTYPath: %s\n"
1323
                        "%sTTYReset: %s\n"
1324
                        "%sTTYVHangup: %s\n"
1325
                        "%sTTYVTDisallocate: %s\n"
1326
                        "%sTTYRows: %u\n"
1327
                        "%sTTYColumns: %u\n",
1328
                        prefix, c->tty_path,
1329
                        prefix, yes_no(c->tty_reset),
×
1330
                        prefix, yes_no(c->tty_vhangup),
×
1331
                        prefix, yes_no(c->tty_vt_disallocate),
×
1332
                        prefix, c->tty_rows,
×
1333
                        prefix, c->tty_cols);
×
1334

1335
        if (IN_SET(c->std_output,
224✔
1336
                   EXEC_OUTPUT_KMSG,
1337
                   EXEC_OUTPUT_JOURNAL,
1338
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1339
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1340
            IN_SET(c->std_error,
17✔
1341
                   EXEC_OUTPUT_KMSG,
1342
                   EXEC_OUTPUT_JOURNAL,
1343
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1344
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1345

1346
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
207✔
1347

1348
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
207✔
1349
                if (r >= 0)
207✔
1350
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
207✔
1351

1352
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
207✔
1353
                if (r >= 0)
207✔
1354
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
207✔
1355
        }
1356

1357
        if (c->log_level_max >= 0) {
224✔
1358
                _cleanup_free_ char *t = NULL;
1✔
1359

1360
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1361

1362
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1363
        }
1364

1365
        if (c->log_ratelimit.interval > 0)
224✔
1366
                fprintf(f,
×
1367
                        "%sLogRateLimitIntervalSec: %s\n",
1368
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1369

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

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

1376
                char *pattern;
×
1377
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1378
                        fprintf(f, " %s", pattern);
×
1379
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1380
                        fprintf(f, " ~%s", pattern);
×
1381
                fputc('\n', f);
×
1382
        }
1383

1384
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
228✔
1385
                fprintf(f, "%sLogExtraFields: ", prefix);
4✔
1386
                fwrite(field->iov_base, 1, field->iov_len, f);
4✔
1387
                fputc('\n', f);
4✔
1388
        }
1389

1390
        if (c->log_namespace)
224✔
1391
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1392

1393
        if (c->secure_bits) {
224✔
1394
                _cleanup_free_ char *str = NULL;
×
1395

1396
                r = secure_bits_to_string_alloc(c->secure_bits, &str);
×
1397
                if (r >= 0)
×
1398
                        fprintf(f, "%sSecure Bits: %s\n", prefix, str);
×
1399
        }
1400

1401
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
224✔
1402
                _cleanup_free_ char *str = NULL;
16✔
1403

1404
                r = capability_set_to_string(c->capability_bounding_set, &str);
16✔
1405
                if (r >= 0)
16✔
1406
                        fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
16✔
1407
        }
1408

1409
        if (c->capability_ambient_set != 0) {
224✔
1410
                _cleanup_free_ char *str = NULL;
×
1411

1412
                r = capability_set_to_string(c->capability_ambient_set, &str);
×
1413
                if (r >= 0)
×
1414
                        fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
×
1415
        }
1416

1417
        if (c->user)
224✔
1418
                fprintf(f, "%sUser: %s\n", prefix, c->user);
1✔
1419
        if (c->group)
224✔
1420
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1421

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

1424
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
224✔
1425

1426
        if (c->pam_name)
224✔
1427
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
×
1428

1429
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
224✔
1430
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
224✔
1431
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
224✔
1432
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
224✔
1433
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
224✔
1434
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
224✔
1435

1436
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
228✔
1437
                fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
4✔
1438
                        mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
4✔
1439
                        mount->ignore_enoent ? "-": "",
4✔
1440
                        mount->source,
1441
                        mount->destination,
1442
                        mount->recursive ? "rbind" : "norbind");
4✔
1443

1444
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
224✔
1445
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1446
                        tmpfs->path,
1447
                        isempty(tmpfs->options) ? "" : ":",
×
1448
                        strempty(tmpfs->options));
×
1449

1450
        if (c->utmp_id)
224✔
1451
                fprintf(f,
×
1452
                        "%sUtmpIdentifier: %s\n",
1453
                        prefix, c->utmp_id);
1454

1455
        if (c->selinux_context)
224✔
1456
                fprintf(f,
×
1457
                        "%sSELinuxContext: %s%s\n",
1458
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1459

1460
        if (c->apparmor_profile)
224✔
1461
                fprintf(f,
×
1462
                        "%sAppArmorProfile: %s%s\n",
1463
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1464

1465
        if (c->smack_process_label)
224✔
1466
                fprintf(f,
×
1467
                        "%sSmackProcessLabel: %s%s\n",
1468
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1469

1470
        if (c->personality != PERSONALITY_INVALID)
224✔
1471
                fprintf(f,
1✔
1472
                        "%sPersonality: %s\n",
1473
                        prefix, strna(personality_to_string(c->personality)));
1474

1475
        fprintf(f,
224✔
1476
                "%sLockPersonality: %s\n",
1477
                prefix, yes_no(c->lock_personality));
224✔
1478

1479
        if (c->syscall_filter) {
224✔
1480
                fprintf(f,
11✔
1481
                        "%sSystemCallFilter: ",
1482
                        prefix);
1483

1484
                if (!c->syscall_allow_list)
11✔
1485
                        fputc('~', f);
×
1486

1487
#if HAVE_SECCOMP
1488
                void *id, *val;
11✔
1489
                bool first = true;
11✔
1490
                HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
4,279✔
1491
                        _cleanup_free_ char *name = NULL;
4,268✔
1492
                        const char *errno_name = NULL;
4,268✔
1493
                        int num = PTR_TO_INT(val);
4,268✔
1494

1495
                        if (first)
4,268✔
1496
                                first = false;
1497
                        else
1498
                                fputc(' ', f);
4,257✔
1499

1500
                        name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
4,268✔
1501
                        fputs(strna(name), f);
4,268✔
1502

1503
                        if (num >= 0) {
4,268✔
1504
                                errno_name = seccomp_errno_or_action_to_string(num);
×
1505
                                if (errno_name)
×
1506
                                        fprintf(f, ":%s", errno_name);
×
1507
                                else
1508
                                        fprintf(f, ":%d", num);
×
1509
                        }
1510
                }
1511
#endif
1512

1513
                fputc('\n', f);
11✔
1514
        }
1515

1516
        if (c->syscall_archs) {
224✔
1517
                fprintf(f,
11✔
1518
                        "%sSystemCallArchitectures:",
1519
                        prefix);
1520

1521
#if HAVE_SECCOMP
1522
                void *id;
11✔
1523
                SET_FOREACH(id, c->syscall_archs)
22✔
1524
                        fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
11✔
1525
#endif
1526
                fputc('\n', f);
11✔
1527
        }
1528

1529
        if (exec_context_restrict_namespaces_set(c)) {
224✔
1530
                _cleanup_free_ char *s = NULL;
12✔
1531

1532
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
12✔
1533
                if (r >= 0)
12✔
1534
                        fprintf(f, "%sRestrictNamespaces: %s\n",
24✔
1535
                                prefix, strna(s));
1536
        }
1537

1538
#if HAVE_LIBBPF
1539
        if (exec_context_restrict_filesystems_set(c)) {
224✔
1540
                char *fs;
×
1541
                SET_FOREACH(fs, c->restrict_filesystems)
×
1542
                        fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
×
1543
        }
1544
#endif
1545

1546
        if (c->network_namespace_path)
224✔
1547
                fprintf(f,
×
1548
                        "%sNetworkNamespacePath: %s\n",
1549
                        prefix, c->network_namespace_path);
1550

1551
        if (c->syscall_errno > 0) {
224✔
1552
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
223✔
1553

1554
#if HAVE_SECCOMP
1555
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
223✔
1556
                if (errno_name)
223✔
1557
                        fputs(errno_name, f);
223✔
1558
                else
1559
                        fprintf(f, "%d", c->syscall_errno);
×
1560
#endif
1561
                fputc('\n', f);
223✔
1562
        }
1563

1564
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
224✔
1565
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1566
                        mount->ignore_enoent ? "-": "",
×
1567
                        mount->source,
1568
                        mount->destination);
1569
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1570
                        fprintf(f, ":%s:%s",
×
1571
                                partition_designator_to_string(o->partition_designator),
1572
                                strempty(o->options));
×
1573
                fprintf(f, "\n");
×
1574
        }
1575

1576
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
224✔
1577
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1578
                        mount->ignore_enoent ? "-": "",
×
1579
                        mount->source);
1580
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1581
                        fprintf(f, ":%s:%s",
×
1582
                                partition_designator_to_string(o->partition_designator),
1583
                                strempty(o->options));
×
1584
                fprintf(f, "\n");
×
1585
        }
1586

1587
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
224✔
1588
}
224✔
1589

1590
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1591
        assert(c);
×
1592

1593
        /* Returns true if the process forked off would run under
1594
         * an unchanged UID or as root. */
1595

1596
        if (!c->user)
×
1597
                return true;
1598

1599
        if (STR_IN_SET(c->user, "root", "0"))
×
1600
                return true;
×
1601

1602
        return false;
×
1603
}
1604

1605
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,944✔
1606
        int p;
2,944✔
1607

1608
        assert(c);
2,944✔
1609

1610
        if (c->ioprio_is_set)
2,944✔
1611
                return c->ioprio;
18✔
1612

1613
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,926✔
1614
        if (p < 0)
2,926✔
1615
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1616

1617
        return ioprio_normalize(p);
2,926✔
1618
}
1619

1620
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
35,236✔
1621
        assert(c);
35,236✔
1622

1623
        /* Explicit setting wins */
1624
        if (c->mount_apivfs >= 0)
35,236✔
1625
                return c->mount_apivfs > 0;
122✔
1626

1627
        /* Default to "yes" if root directory or image are specified */
1628
        if (exec_context_with_rootfs(c))
35,114✔
1629
                return true;
76✔
1630

1631
        return false;
1632
}
1633

1634
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
29,487✔
1635
        assert(c);
29,487✔
1636

1637
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1638
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1639
        if (c->log_namespace)
29,487✔
1640
                return true;
1641

1642
        if (c->bind_log_sockets >= 0)
29,479✔
1643
                return c->bind_log_sockets > 0;
2✔
1644

1645
        if (exec_context_get_effective_mount_apivfs(c))
29,477✔
1646
                return true;
1647

1648
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1649
        if (exec_context_with_rootfs(c) && c->private_devices)
29,399✔
1650
                return true;
×
1651

1652
        return false;
1653
}
1654

1655
void exec_context_free_log_extra_fields(ExecContext *c) {
47,611✔
1656
        assert(c);
47,611✔
1657

1658
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
47,616✔
1659
                free(field->iov_base);
5✔
1660

1661
        c->log_extra_fields = mfree(c->log_extra_fields);
47,611✔
1662
        c->n_log_extra_fields = 0;
47,611✔
1663
}
47,611✔
1664

1665
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,523✔
1666
        _cleanup_close_ int fd = -EBADF;
1,523✔
1667
        const char *path;
1,523✔
1668
        struct stat st;
1,523✔
1669
        int r;
1,523✔
1670

1671
        assert(c);
1,523✔
1672

1673
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1674
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,523✔
1675

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

1682
        path = exec_context_tty_path(c);
51✔
1683
        if (!path)
51✔
1684
                return;
1685

1686
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
51✔
1687
        if (fd < 0)
51✔
1688
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1689
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1690
                                             path);
1691

1692
        if (fstat(fd, &st) < 0)
51✔
1693
                return (void) log_warning_errno(errno, "Failed to stat TTY '%s', ignoring: %m", path);
×
1694

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

1703
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
51✔
1704
        if (r < 0)
51✔
1705
                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);
51✔
1706
}
1707

1708
int exec_context_get_clean_directories(
×
1709
                ExecContext *c,
1710
                char **prefix,
1711
                ExecCleanMask mask,
1712
                char ***ret) {
1713

1714
        _cleanup_strv_free_ char **l = NULL;
×
1715
        int r;
×
1716

1717
        assert(c);
×
1718
        assert(prefix);
×
1719
        assert(ret);
×
1720

1721
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
1722
                if (!BIT_SET(mask, t))
×
1723
                        continue;
×
1724

1725
                if (!prefix[t])
×
1726
                        continue;
×
1727

1728
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
×
1729
                        char *j;
×
1730

1731
                        j = path_join(prefix[t], i->path);
×
1732
                        if (!j)
×
1733
                                return -ENOMEM;
1734

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

1739
                        /* Also remove private directories unconditionally. */
1740
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
×
1741
                                j = path_join(prefix[t], "private", i->path);
×
1742
                                if (!j)
×
1743
                                        return -ENOMEM;
1744

1745
                                r = strv_consume(&l, j);
×
1746
                                if (r < 0)
×
1747
                                        return r;
1748
                        }
1749

1750
                        STRV_FOREACH(symlink, i->symlinks) {
×
1751
                                j = path_join(prefix[t], *symlink);
×
1752
                                if (!j)
×
1753
                                        return -ENOMEM;
1754

1755
                                r = strv_consume(&l, j);
×
1756
                                if (r < 0)
×
1757
                                        return r;
1758
                        }
1759
                }
1760
        }
1761

1762
        *ret = TAKE_PTR(l);
×
1763
        return 0;
×
1764
}
1765

1766
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,493✔
1767
        ExecCleanMask mask = 0;
1,493✔
1768

1769
        assert(c);
1,493✔
1770
        assert(ret);
1,493✔
1771

1772
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
8,958✔
1773
                if (c->directories[t].n_items > 0)
7,465✔
1774
                        mask |= 1U << t;
359✔
1775

1776
        *ret = mask;
1,493✔
1777
        return 0;
1,493✔
1778
}
1779

1780
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,472✔
1781
        int n = 0, r;
1,472✔
1782

1783
        assert(c);
1,472✔
1784

1785
        if (c->oom_score_adjust_set)
1,472✔
1786
                return c->oom_score_adjust;
426✔
1787

1788
        r = get_oom_score_adjust(&n);
1,046✔
1789
        if (r < 0)
1,046✔
1790
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1791

1792
        return n;
1,046✔
1793
}
1794

1795
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1,472✔
1796
        _cleanup_free_ char *t = NULL;
1,472✔
1797
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1,472✔
1798
        int r;
1,472✔
1799

1800
        assert(c);
1,472✔
1801

1802
        if (c->coredump_filter_set)
1,472✔
1803
                return c->coredump_filter;
×
1804

1805
        r = read_one_line_file("/proc/self/coredump_filter", &t);
1,472✔
1806
        if (r < 0)
1,472✔
1807
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1808
        else {
1809
                r = safe_atoux64(t, &n);
1,472✔
1810
                if (r < 0)
1,472✔
1811
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1812
        }
1813

1814
        return n;
1,472✔
1815
}
1816

1817
int exec_context_get_nice(const ExecContext *c) {
1,472✔
1818
        int n;
1,472✔
1819

1820
        assert(c);
1,472✔
1821

1822
        if (c->nice_set)
1,472✔
1823
                return c->nice;
6✔
1824

1825
        errno = 0;
1,466✔
1826
        n = getpriority(PRIO_PROCESS, 0);
1,466✔
1827
        if (errno > 0) {
1,466✔
1828
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1829
                n = 0;
1830
        }
1831

1832
        return n;
1833
}
1834

1835
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,472✔
1836
        int n;
1,472✔
1837

1838
        assert(c);
1,472✔
1839

1840
        if (c->cpu_sched_set)
1,472✔
1841
                return c->cpu_sched_policy;
×
1842

1843
        n = sched_getscheduler(0);
1,472✔
1844
        if (n < 0)
1,472✔
1845
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1846

1847
        return n < 0 ? SCHED_OTHER : n;
1,472✔
1848
}
1849

1850
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,472✔
1851
        struct sched_param p = {};
1,472✔
1852
        int r;
1,472✔
1853

1854
        assert(c);
1,472✔
1855

1856
        if (c->cpu_sched_set)
1,472✔
1857
                return c->cpu_sched_priority;
×
1858

1859
        r = sched_getparam(0, &p);
1,472✔
1860
        if (r < 0)
1,472✔
1861
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1862

1863
        return r >= 0 ? p.sched_priority : 0;
1,472✔
1864
}
1865

1866
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,472✔
1867
        int r;
1,472✔
1868

1869
        assert(c);
1,472✔
1870

1871
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,472✔
1872
                return c->timer_slack_nsec;
1873

1874
        r = prctl(PR_GET_TIMERSLACK);
1,472✔
1875
        if (r < 0)
1,472✔
1876
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1877

1878
        return (uint64_t) MAX(r, 0);
1,472✔
1879
}
1880

1881
bool exec_context_get_set_login_environment(const ExecContext *c) {
11,345✔
1882
        assert(c);
11,345✔
1883

1884
        if (c->set_login_environment >= 0)
11,345✔
1885
                return c->set_login_environment;
×
1886

1887
        return c->user || c->dynamic_user || c->pam_name;
20,445✔
1888
}
1889

1890
char** exec_context_get_syscall_filter(const ExecContext *c) {
1,472✔
1891
        _cleanup_strv_free_ char **l = NULL;
1,472✔
1892

1893
        assert(c);
1,472✔
1894

1895
#if HAVE_SECCOMP
1896
        void *id, *val;
1,472✔
1897
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
19,442✔
1898
                _cleanup_free_ char *name = NULL;
17,970✔
1899
                const char *e = NULL;
17,970✔
1900
                char *s;
17,970✔
1901
                int num = PTR_TO_INT(val);
17,970✔
1902

1903
                if (c->syscall_allow_list && num >= 0)
17,970✔
1904
                        /* syscall with num >= 0 in allow-list is denied. */
1905
                        continue;
×
1906

1907
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
17,970✔
1908
                if (!name)
17,970✔
1909
                        continue;
×
1910

1911
                if (num >= 0) {
17,970✔
1912
                        e = seccomp_errno_or_action_to_string(num);
×
1913
                        if (e) {
×
1914
                                s = strjoin(name, ":", e);
×
1915
                                if (!s)
×
1916
                                        return NULL;
1917
                        } else {
1918
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1919
                                        return NULL;
1920
                        }
1921
                } else
1922
                        s = TAKE_PTR(name);
17,970✔
1923

1924
                if (strv_consume(&l, s) < 0)
17,970✔
1925
                        return NULL;
1926
        }
1927

1928
        strv_sort(l);
1,472✔
1929
#endif
1930

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

1934
char** exec_context_get_syscall_archs(const ExecContext *c) {
1,472✔
1935
        _cleanup_strv_free_ char **l = NULL;
1,472✔
1936

1937
        assert(c);
1,472✔
1938

1939
#if HAVE_SECCOMP
1940
        void *id;
1,472✔
1941
        SET_FOREACH(id, c->syscall_archs) {
1,524✔
1942
                const char *name;
52✔
1943

1944
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
52✔
1945
                if (!name)
52✔
1946
                        continue;
×
1947

1948
                if (strv_extend(&l, name) < 0)
52✔
1949
                        return NULL;
×
1950
        }
1951

1952
        strv_sort(l);
1,472✔
1953
#endif
1954

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

1958
char** exec_context_get_syscall_log(const ExecContext *c) {
1,472✔
1959
        _cleanup_strv_free_ char **l = NULL;
1,472✔
1960

1961
        assert(c);
1,472✔
1962

1963
#if HAVE_SECCOMP
1964
        void *id, *val;
1,472✔
1965
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,472✔
1966
                char *name = NULL;
×
1967

1968
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1969
                if (!name)
×
1970
                        continue;
×
1971

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

1976
        strv_sort(l);
1,472✔
1977
#endif
1978

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

1982
char** exec_context_get_address_families(const ExecContext *c) {
1,472✔
1983
        _cleanup_strv_free_ char **l = NULL;
1,472✔
1984
        void *af;
1,472✔
1985

1986
        assert(c);
1,472✔
1987

1988
        SET_FOREACH(af, c->address_families) {
1,626✔
1989
                const char *name;
154✔
1990

1991
                name = af_to_name(PTR_TO_INT(af));
154✔
1992
                if (!name)
154✔
1993
                        continue;
×
1994

1995
                if (strv_extend(&l, name) < 0)
154✔
1996
                        return NULL;
×
1997
        }
1998

1999
        strv_sort(l);
1,472✔
2000

2001
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,472✔
2002
}
2003

2004
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1,472✔
2005
        assert(c);
1,472✔
2006

2007
#if HAVE_LIBBPF
2008
        char **l = set_get_strv(c->restrict_filesystems);
1,472✔
2009
        if (!l)
1,472✔
2010
                return NULL;
2011

2012
        return strv_sort(l);
1,472✔
2013
#else
2014
        return strv_new(NULL);
2015
#endif
2016
}
2017

2018
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
13,087✔
2019
        assert(c);
13,087✔
2020

2021
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
13,087✔
2022
}
2023

2024
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
14,559✔
2025
        assert(c);
14,559✔
2026

2027
        return c->restrict_filesystems_allow_list ||
14,559✔
2028
          !set_isempty(c->restrict_filesystems);
14,559✔
2029
}
2030

2031
bool exec_context_with_rootfs(const ExecContext *c) {
64,885✔
2032
        assert(c);
64,885✔
2033

2034
        /* Checks if RootDirectory= or RootImage= are used */
2035

2036
        return !empty_or_root(c->root_directory) || c->root_image;
64,885✔
2037
}
2038

2039
int exec_context_has_vpicked_extensions(const ExecContext *context) {
3✔
2040
        int r;
3✔
2041

2042
        assert(context);
3✔
2043

2044
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
3✔
2045
                r = path_uses_vpick(mi->source);
×
2046
                if (r != 0)
×
2047
                        return r;
2048
        }
2049
        STRV_FOREACH(ed, context->extension_directories) {
3✔
2050
                r = path_uses_vpick(*ed);
×
2051
                if (r != 0)
×
2052
                        return r;
2053
        }
2054

2055
        return 0;
2056
}
2057

2058
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
4,627✔
2059
        assert(s);
4,627✔
2060

2061
        *s = (ExecStatus) {
4,627✔
2062
                .pid = pid,
2063
        };
2064

2065
        if (ts)
4,627✔
2066
                s->start_timestamp = *ts;
4,627✔
2067
        else
2068
                dual_timestamp_now(&s->start_timestamp);
×
2069
}
4,627✔
2070

2071
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
2,072✔
2072
        assert(s);
2,072✔
2073

2074
        if (s->pid != pid)
2,072✔
2075
                *s = (ExecStatus) {
5✔
2076
                        .pid = pid,
2077
                };
2078

2079
        dual_timestamp_now(&s->exit_timestamp);
2,072✔
2080

2081
        s->code = code;
2,072✔
2082
        s->status = status;
2,072✔
2083

2084
        if (context && context->utmp_id)
2,072✔
2085
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2086
}
2,072✔
2087

2088
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
7,858✔
2089
        assert(s);
7,858✔
2090
        assert(ucred);
7,858✔
2091
        assert(ts);
7,858✔
2092

2093
        if (ucred->pid != s->pid)
7,858✔
2094
                *s = (ExecStatus) {
10✔
2095
                        .pid = ucred->pid,
2096
                };
2097

2098
        s->handoff_timestamp = *ts;
7,858✔
2099
}
7,858✔
2100

2101
void exec_status_reset(ExecStatus *s) {
25,014✔
2102
        assert(s);
25,014✔
2103

2104
        *s = (ExecStatus) {};
25,014✔
2105
}
25,014✔
2106

2107
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
94✔
2108
        assert(s);
94✔
2109
        assert(f);
94✔
2110

2111
        if (s->pid <= 0)
94✔
2112
                return;
2113

2114
        prefix = strempty(prefix);
10✔
2115

2116
        fprintf(f,
10✔
2117
                "%sPID: "PID_FMT"\n",
2118
                prefix, s->pid);
2119

2120
        if (dual_timestamp_is_set(&s->start_timestamp))
10✔
2121
                fprintf(f,
10✔
2122
                        "%sStart Timestamp: %s\n",
2123
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
10✔
2124

2125
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
10✔
2126
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
10✔
2127
                fprintf(f,
10✔
2128
                        "%sHandoff Timestamp: %s since start\n",
2129
                        prefix,
2130
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
20✔
2131
        else
2132
                fprintf(f,
×
2133
                        "%sHandoff Timestamp: %s\n",
2134
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
×
2135

2136
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2137

2138
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
×
2139
                        fprintf(f,
×
2140
                                "%sExit Timestamp: %s since handoff\n",
2141
                                prefix,
2142
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
×
2143
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2144
                        fprintf(f,
×
2145
                                "%sExit Timestamp: %s since start\n",
2146
                                prefix,
2147
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2148
                else
2149
                        fprintf(f,
×
2150
                                "%sExit Timestamp: %s\n",
2151
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2152

2153
                fprintf(f,
×
2154
                        "%sExit Code: %s\n"
2155
                        "%sExit Status: %i\n",
2156
                        prefix, sigchld_code_to_string(s->code),
×
2157
                        prefix, s->status);
×
2158
        }
2159
}
2160

2161
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
94✔
2162
        _cleanup_free_ char *cmd = NULL;
188✔
2163
        const char *prefix2;
94✔
2164

2165
        assert(c);
94✔
2166
        assert(f);
94✔
2167

2168
        prefix = strempty(prefix);
94✔
2169
        prefix2 = strjoina(prefix, "\t");
470✔
2170

2171
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
94✔
2172

2173
        fprintf(f,
94✔
2174
                "%sCommand Line: %s\n",
2175
                prefix, strnull(cmd));
2176

2177
        exec_status_dump(&c->exec_status, f, prefix2);
94✔
2178
}
94✔
2179

2180
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
91✔
2181
        assert(f);
91✔
2182

2183
        prefix = strempty(prefix);
91✔
2184

2185
        LIST_FOREACH(command, i, c)
185✔
2186
                exec_command_dump(i, f, prefix);
94✔
2187
}
91✔
2188

2189
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
16,489✔
2190
        ExecCommand *end;
16,489✔
2191

2192
        assert(l);
16,489✔
2193
        assert(e);
16,489✔
2194

2195
        if (*l) {
16,489✔
2196
                /* It's kind of important, that we keep the order here */
2197
                end = LIST_FIND_TAIL(command, *l);
372✔
2198
                LIST_INSERT_AFTER(command, *l, end, e);
135✔
2199
        } else
2200
                *l = e;
16,354✔
2201
}
16,489✔
2202

2203
int exec_command_set(ExecCommand *c, const char *path, ...) {
182✔
2204
        va_list ap;
182✔
2205
        char **l, *p;
182✔
2206

2207
        assert(c);
182✔
2208
        assert(path);
182✔
2209

2210
        va_start(ap, path);
182✔
2211
        l = strv_new_ap(path, ap);
182✔
2212
        va_end(ap);
182✔
2213

2214
        if (!l)
182✔
2215
                return -ENOMEM;
182✔
2216

2217
        p = strdup(path);
182✔
2218
        if (!p) {
182✔
2219
                strv_free(l);
×
2220
                return -ENOMEM;
×
2221
        }
2222

2223
        free_and_replace(c->path, p);
182✔
2224

2225
        return strv_free_and_replace(c->argv, l);
182✔
2226
}
2227

2228
int exec_command_append(ExecCommand *c, const char *path, ...) {
263✔
2229
        char **l;
263✔
2230
        va_list ap;
263✔
2231
        int r;
263✔
2232

2233
        assert(c);
263✔
2234
        assert(path);
263✔
2235

2236
        va_start(ap, path);
263✔
2237
        l = strv_new_ap(path, ap);
263✔
2238
        va_end(ap);
263✔
2239

2240
        if (!l)
263✔
2241
                return -ENOMEM;
263✔
2242

2243
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
263✔
2244
        if (r < 0)
263✔
2245
                return r;
×
2246

2247
        return 0;
2248
}
2249

2250
static char *destroy_tree(char *path) {
272✔
2251
        if (!path)
272✔
2252
                return NULL;
2253

2254
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
86✔
2255
                log_debug("Spawning process to nuke '%s'", path);
86✔
2256

2257
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
86✔
2258
        }
2259

2260
        return mfree(path);
86✔
2261
}
2262

2263
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
117,989✔
2264
        assert(rt);
117,989✔
2265

2266
        if (rt->manager)
117,989✔
2267
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
173✔
2268

2269
        rt->id = mfree(rt->id);
117,989✔
2270
        rt->tmp_dir = mfree(rt->tmp_dir);
117,989✔
2271
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
117,989✔
2272
        safe_close_pair(rt->netns_storage_socket);
117,989✔
2273
        safe_close_pair(rt->ipcns_storage_socket);
117,989✔
2274
}
117,989✔
2275

2276
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
117,961✔
2277
        if (!rt)
117,961✔
2278
                return NULL;
2279

2280
        exec_shared_runtime_done(rt);
117,961✔
2281
        return mfree(rt);
117,961✔
2282
}
2283

2284
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
174✔
2285
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
120,987✔
2286

2287
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
50✔
2288
        if (!rt)
50✔
2289
                return NULL;
2290

2291
        assert(rt->n_ref > 0);
49✔
2292
        rt->n_ref--;
49✔
2293

2294
        if (rt->n_ref > 0)
49✔
2295
                return NULL;
2296

2297
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
49✔
2298
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
49✔
2299

2300
        return exec_shared_runtime_free(rt);
49✔
2301
}
2302

2303
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
117,961✔
2304
        _cleanup_free_ char *id_copy = NULL;
235,922✔
2305
        ExecSharedRuntime *n;
117,961✔
2306

2307
        assert(ret);
117,961✔
2308

2309
        id_copy = strdup(id);
117,961✔
2310
        if (!id_copy)
117,961✔
2311
                return -ENOMEM;
2312

2313
        n = new(ExecSharedRuntime, 1);
117,961✔
2314
        if (!n)
117,961✔
2315
                return -ENOMEM;
2316

2317
        *n = (ExecSharedRuntime) {
117,961✔
2318
                .id = TAKE_PTR(id_copy),
117,961✔
2319
                .netns_storage_socket = EBADF_PAIR,
2320
                .ipcns_storage_socket = EBADF_PAIR,
2321
        };
2322

2323
        *ret = n;
117,961✔
2324
        return 0;
117,961✔
2325
}
2326

2327
static int exec_shared_runtime_add(
173✔
2328
                Manager *m,
2329
                const char *id,
2330
                char **tmp_dir,
2331
                char **var_tmp_dir,
2332
                int netns_storage_socket[2],
2333
                int ipcns_storage_socket[2],
2334
                ExecSharedRuntime **ret) {
2335

2336
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
173✔
2337
        int r;
173✔
2338

2339
        assert(m);
173✔
2340
        assert(id);
173✔
2341

2342
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2343

2344
        r = exec_shared_runtime_allocate(&rt, id);
173✔
2345
        if (r < 0)
173✔
2346
                return r;
2347

2348
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
173✔
2349
        if (r < 0)
173✔
2350
                return r;
2351

2352
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
173✔
2353
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
173✔
2354
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
173✔
2355

2356
        if (netns_storage_socket) {
173✔
2357
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
173✔
2358
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
173✔
2359
        }
2360

2361
        if (ipcns_storage_socket) {
173✔
2362
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
173✔
2363
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
173✔
2364
        }
2365

2366
        rt->manager = m;
173✔
2367

2368
        if (ret)
173✔
2369
                *ret = rt;
70✔
2370
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2371
        TAKE_PTR(rt);
173✔
2372
        return 0;
173✔
2373
}
2374

2375
static int exec_shared_runtime_make(
6,337✔
2376
                Manager *m,
2377
                const ExecContext *c,
2378
                const char *id,
2379
                ExecSharedRuntime **ret) {
2380

2381
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
6,337✔
2382
        _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
12,674✔
2383
        int r;
6,337✔
2384

2385
        assert(m);
6,337✔
2386
        assert(c);
6,337✔
2387
        assert(id);
6,337✔
2388

2389
        /* It is not necessary to create ExecSharedRuntime object. */
2390
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
6,337✔
2391
                *ret = NULL;
6,267✔
2392
                return 0;
6,267✔
2393
        }
2394

2395
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
134✔
2396
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
64✔
2397
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
2398
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2399
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
64✔
2400
                if (r < 0)
64✔
2401
                        return r;
2402
        }
2403

2404
        if (exec_needs_network_namespace(c))
70✔
2405
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
7✔
2406
                        return -errno;
×
2407

2408
        if (exec_needs_ipc_namespace(c))
70✔
2409
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
2410
                        return -errno;
×
2411

2412
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
70✔
2413
        if (r < 0)
70✔
2414
                return r;
×
2415

2416
        return 1;
2417
}
2418

2419
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
6,440✔
2420
        ExecSharedRuntime *rt;
6,440✔
2421
        int r;
6,440✔
2422

2423
        assert(m);
6,440✔
2424
        assert(id);
6,440✔
2425
        assert(ret);
6,440✔
2426

2427
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
6,440✔
2428
        if (rt)
6,440✔
2429
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2430
                goto ref;
103✔
2431

2432
        if (!create) {
6,337✔
2433
                *ret = NULL;
×
2434
                return 0;
×
2435
        }
2436

2437
        /* If not found, then create a new object. */
2438
        r = exec_shared_runtime_make(m, c, id, &rt);
6,337✔
2439
        if (r < 0)
6,337✔
2440
                return r;
2441
        if (r == 0) {
6,337✔
2442
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2443
                *ret = NULL;
6,267✔
2444
                return 0;
6,267✔
2445
        }
2446

2447
ref:
70✔
2448
        /* increment reference counter. */
2449
        rt->n_ref++;
173✔
2450
        *ret = rt;
173✔
2451
        return 1;
173✔
2452
}
2453

2454
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
73✔
2455
        ExecSharedRuntime *rt;
73✔
2456

2457
        assert(m);
73✔
2458
        assert(f);
73✔
2459
        assert(fds);
73✔
2460

2461
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
197✔
2462
                fprintf(f, "exec-runtime=%s", rt->id);
124✔
2463

2464
                if (rt->tmp_dir)
124✔
2465
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
124✔
2466

2467
                if (rt->var_tmp_dir)
124✔
2468
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
124✔
2469

2470
                if (rt->netns_storage_socket[0] >= 0) {
124✔
2471
                        int copy;
2✔
2472

2473
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2✔
2474
                        if (copy < 0)
2✔
2475
                                return copy;
×
2476

2477
                        fprintf(f, " netns-socket-0=%i", copy);
2✔
2478
                }
2479

2480
                if (rt->netns_storage_socket[1] >= 0) {
124✔
2481
                        int copy;
2✔
2482

2483
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2✔
2484
                        if (copy < 0)
2✔
2485
                                return copy;
2486

2487
                        fprintf(f, " netns-socket-1=%i", copy);
2✔
2488
                }
2489

2490
                if (rt->ipcns_storage_socket[0] >= 0) {
124✔
2491
                        int copy;
×
2492

2493
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2494
                        if (copy < 0)
×
2495
                                return copy;
2496

2497
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2498
                }
2499

2500
                if (rt->ipcns_storage_socket[1] >= 0) {
124✔
2501
                        int copy;
×
2502

2503
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2504
                        if (copy < 0)
×
2505
                                return copy;
2506

2507
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2508
                }
2509

2510
                fputc('\n', f);
124✔
2511
        }
2512

2513
        return 0;
73✔
2514
}
2515

2516
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
120,814✔
2517
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
120,814✔
2518
        ExecSharedRuntime *rt = NULL;
120,814✔
2519
        int r;
120,814✔
2520

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

2526
        assert(u);
120,814✔
2527
        assert(key);
120,814✔
2528
        assert(value);
120,814✔
2529

2530
        /* Manager manages ExecSharedRuntime objects by the unit id.
2531
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2532
        if (isempty(u->id)) {
120,814✔
2533
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
2534
                return 0;
×
2535
        }
2536

2537
        if (u->manager) {
120,814✔
2538
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
120,814✔
2539
                        return log_oom();
×
2540

2541
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
120,814✔
2542
        }
2543
        if (!rt) {
120,814✔
2544
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
117,788✔
2545
                        return log_oom();
×
2546

2547
                rt = rt_create;
117,788✔
2548
        }
2549

2550
        if (streq(key, "tmp-dir")) {
120,814✔
2551
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2552
                        return -ENOMEM;
2553

2554
        } else if (streq(key, "var-tmp-dir")) {
120,814✔
2555
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2556
                        return -ENOMEM;
2557

2558
        } else if (streq(key, "netns-socket-0")) {
120,814✔
2559

2560
                safe_close(rt->netns_storage_socket[0]);
×
2561
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2562
                if (rt->netns_storage_socket[0] < 0)
×
2563
                        return 0;
2564

2565
        } else if (streq(key, "netns-socket-1")) {
120,814✔
2566

2567
                safe_close(rt->netns_storage_socket[1]);
×
2568
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2569
                if (rt->netns_storage_socket[1] < 0)
×
2570
                        return 0;
2571
        } else
2572
                return 0;
2573

2574
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
2575
        if (rt_create && u->manager) {
×
2576
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
2577
                if (r < 0) {
×
2578
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
2579
                        return 0;
×
2580
                }
2581

2582
                rt_create->manager = u->manager;
×
2583

2584
                /* Avoid cleanup */
2585
                TAKE_PTR(rt_create);
×
2586
        }
2587

2588
        return 1;
2589
}
2590

2591
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
103✔
2592
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
103✔
2593
        char *id = NULL;
103✔
2594
        int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
103✔
2595
        const char *p, *v = ASSERT_PTR(value);
103✔
2596
        size_t n;
103✔
2597

2598
        assert(m);
103✔
2599
        assert(fds);
103✔
2600

2601
        n = strcspn(v, " ");
103✔
2602
        id = strndupa_safe(v, n);
103✔
2603
        if (v[n] != ' ')
103✔
2604
                goto finalize;
×
2605
        p = v + n + 1;
103✔
2606

2607
        v = startswith(p, "tmp-dir=");
103✔
2608
        if (v) {
103✔
2609
                n = strcspn(v, " ");
103✔
2610
                tmp_dir = strndup(v, n);
103✔
2611
                if (!tmp_dir)
103✔
2612
                        return log_oom();
×
2613
                if (v[n] != ' ')
103✔
2614
                        goto finalize;
×
2615
                p = v + n + 1;
103✔
2616
        }
2617

2618
        v = startswith(p, "var-tmp-dir=");
103✔
2619
        if (v) {
103✔
2620
                n = strcspn(v, " ");
103✔
2621
                var_tmp_dir = strndup(v, n);
103✔
2622
                if (!var_tmp_dir)
103✔
2623
                        return log_oom();
×
2624
                if (v[n] != ' ')
103✔
2625
                        goto finalize;
102✔
2626
                p = v + n + 1;
1✔
2627
        }
2628

2629
        v = startswith(p, "netns-socket-0=");
1✔
2630
        if (v) {
1✔
2631
                char *buf;
1✔
2632

2633
                n = strcspn(v, " ");
1✔
2634
                buf = strndupa_safe(v, n);
1✔
2635

2636
                netns_fdpair[0] = deserialize_fd(fds, buf);
1✔
2637
                if (netns_fdpair[0] < 0)
1✔
2638
                        return netns_fdpair[0];
2639
                if (v[n] != ' ')
1✔
2640
                        goto finalize;
×
2641
                p = v + n + 1;
1✔
2642
        }
2643

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

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

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

2659
        v = startswith(p, "ipcns-socket-0=");
×
2660
        if (v) {
×
2661
                char *buf;
×
2662

2663
                n = strcspn(v, " ");
×
2664
                buf = strndupa_safe(v, n);
×
2665

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

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

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

2681
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2682
                if (ipcns_fdpair[1] < 0)
×
2683
                        return ipcns_fdpair[1];
2684
        }
2685

2686
finalize:
×
2687
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
103✔
2688
        if (r < 0)
103✔
2689
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2690
        return 0;
2691
}
2692

2693
void exec_shared_runtime_vacuum(Manager *m) {
1,464✔
2694
        ExecSharedRuntime *rt;
1,464✔
2695

2696
        assert(m);
1,464✔
2697

2698
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2699

2700
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,567✔
2701
                if (rt->n_ref > 0)
103✔
2702
                        continue;
103✔
2703

2704
                (void) exec_shared_runtime_free(rt);
×
2705
        }
2706
}
1,464✔
2707

2708
int exec_runtime_make(
6,440✔
2709
                const Unit *unit,
2710
                const ExecContext *context,
2711
                ExecSharedRuntime *shared,
2712
                DynamicCreds *creds,
2713
                ExecRuntime **ret) {
2714
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
6,440✔
2715
        _cleanup_free_ char *ephemeral = NULL;
6,440✔
2716
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
6,440✔
2717
        int r;
6,440✔
2718

2719
        assert(unit);
6,440✔
2720
        assert(context);
6,440✔
2721
        assert(ret);
6,440✔
2722

2723
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
6,440✔
2724
                *ret = NULL;
6,266✔
2725
                return 0;
6,266✔
2726
        }
2727

2728
        if (exec_needs_ephemeral(context)) {
174✔
2729
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2730
                if (r < 0)
×
2731
                        return r;
2732

2733
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2734
                if (r < 0)
×
2735
                        return r;
2736

2737
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2738
                        return -errno;
×
2739
        }
2740

2741
        rt = new(ExecRuntime, 1);
174✔
2742
        if (!rt)
174✔
2743
                return -ENOMEM;
2744

2745
        *rt = (ExecRuntime) {
174✔
2746
                .shared = shared,
2747
                .dynamic_creds = creds,
2748
                .ephemeral_copy = TAKE_PTR(ephemeral),
174✔
2749
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
174✔
2750
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
174✔
2751
        };
2752

2753
        *ret = TAKE_PTR(rt);
174✔
2754
        return 1;
174✔
2755
}
2756

2757
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
47,631✔
2758
        if (!rt)
47,631✔
2759
                return NULL;
2760

2761
        exec_shared_runtime_unref(rt->shared);
174✔
2762
        dynamic_creds_unref(rt->dynamic_creds);
174✔
2763

2764
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
174✔
2765

2766
        safe_close_pair(rt->ephemeral_storage_socket);
174✔
2767
        return mfree(rt);
174✔
2768
}
2769

2770
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
5,118✔
2771
        if (!rt)
5,118✔
2772
                return NULL;
2773

2774
        rt->shared = exec_shared_runtime_destroy(rt->shared);
50✔
2775
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
50✔
2776
        return exec_runtime_free(rt);
50✔
2777
}
2778

2779
void exec_runtime_clear(ExecRuntime *rt) {
28✔
2780
        if (!rt)
28✔
2781
                return;
2782

2783
        safe_close_pair(rt->ephemeral_storage_socket);
28✔
2784
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
28✔
2785
}
2786

2787
void exec_params_shallow_clear(ExecParameters *p) {
2,272✔
2788
        if (!p)
2,272✔
2789
                return;
2790

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

2794
        p->environment = strv_free(p->environment);
2,272✔
2795
        p->fd_names = strv_free(p->fd_names);
2,272✔
2796
        p->files_env = strv_free(p->files_env);
2,272✔
2797
        p->fds = mfree(p->fds);
2,272✔
2798
        p->exec_fd = safe_close(p->exec_fd);
2,272✔
2799
        p->user_lookup_fd = -EBADF;
2,272✔
2800
        p->bpf_restrict_fs_map_fd = -EBADF;
2,272✔
2801
        p->unit_id = mfree(p->unit_id);
2,272✔
2802
        p->invocation_id = SD_ID128_NULL;
2,272✔
2803
        p->invocation_id_string[0] = '\0';
2,272✔
2804
        p->confirm_spawn = mfree(p->confirm_spawn);
2,272✔
2805
}
2806

2807
void exec_params_deep_clear(ExecParameters *p) {
28✔
2808
        if (!p)
28✔
2809
                return;
2810

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

2815
        close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
28✔
2816

2817
        p->cgroup_path = mfree(p->cgroup_path);
28✔
2818

2819
        if (p->prefix) {
28✔
2820
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
28✔
2821
                p->prefix = mfree(p->prefix);
28✔
2822
        }
2823

2824
        p->received_credentials_directory = mfree(p->received_credentials_directory);
28✔
2825
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
28✔
2826

2827
        if (p->idle_pipe) {
28✔
UNCOV
2828
                close_many_and_free(p->idle_pipe, 4);
×
UNCOV
2829
                p->idle_pipe = NULL;
×
2830
        }
2831

2832
        p->stdin_fd = safe_close(p->stdin_fd);
28✔
2833
        p->stdout_fd = safe_close(p->stdout_fd);
28✔
2834
        p->stderr_fd = safe_close(p->stderr_fd);
28✔
2835

2836
        p->notify_socket = mfree(p->notify_socket);
28✔
2837

2838
        open_file_free_many(&p->open_files);
28✔
2839

2840
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
28✔
2841

2842
        exec_params_shallow_clear(p);
28✔
2843
}
2844

2845
void exec_directory_done(ExecDirectory *d) {
238,045✔
2846
        if (!d)
238,045✔
2847
                return;
2848

2849
        FOREACH_ARRAY(i, d->items, d->n_items) {
239,832✔
2850
                free(i->path);
1,787✔
2851
                strv_free(i->symlinks);
1,787✔
2852
        }
2853

2854
        d->items = mfree(d->items);
238,045✔
2855
        d->n_items = 0;
238,045✔
2856
        d->mode = 0755;
238,045✔
2857
}
2858

2859
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
5,581✔
2860
        assert(d);
5,581✔
2861
        assert(path);
5,581✔
2862

2863
        FOREACH_ARRAY(i, d->items, d->n_items)
7,975✔
2864
                if (path_equal(i->path, path))
2,409✔
2865
                        return i;
2866

2867
        return NULL;
2868
}
2869

2870
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
5,581✔
2871
        _cleanup_strv_free_ char **s = NULL;
×
2872
        _cleanup_free_ char *p = NULL;
5,581✔
2873
        ExecDirectoryItem *existing;
5,581✔
2874
        int r;
5,581✔
2875

2876
        assert(d);
5,581✔
2877
        assert(path);
5,581✔
2878

2879
        existing = exec_directory_find(d, path);
5,581✔
2880
        if (existing) {
5,581✔
2881
                r = strv_extend(&existing->symlinks, symlink);
15✔
2882
                if (r < 0)
15✔
2883
                        return r;
2884

2885
                existing->flags |= flags;
15✔
2886

2887
                return 0; /* existing item is updated */
15✔
2888
        }
2889

2890
        p = strdup(path);
5,566✔
2891
        if (!p)
5,566✔
2892
                return -ENOMEM;
2893

2894
        if (symlink) {
5,566✔
2895
                s = strv_new(symlink);
6✔
2896
                if (!s)
6✔
2897
                        return -ENOMEM;
2898
        }
2899

2900
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
5,566✔
2901
                return -ENOMEM;
2902

2903
        d->items[d->n_items++] = (ExecDirectoryItem) {
5,566✔
2904
                .path = TAKE_PTR(p),
5,566✔
2905
                .symlinks = TAKE_PTR(s),
5,566✔
2906
                .flags = flags,
2907
        };
2908

2909
        return 1; /* new item is added */
5,566✔
2910
}
2911

2912
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
911✔
2913
        assert(a);
911✔
2914
        assert(b);
911✔
2915

2916
        return path_compare(a->path, b->path);
911✔
2917
}
2918

2919
void exec_directory_sort(ExecDirectory *d) {
130,576✔
2920
        assert(d);
130,576✔
2921

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

2927
        if (d->n_items <= 1)
130,576✔
2928
                return;
2929

2930
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
163✔
2931

2932
        for (size_t i = 1; i < d->n_items; i++)
710✔
2933
                for (size_t j = 0; j < i; j++)
1,822✔
2934
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,290✔
2935
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
2936
                                break;
15✔
2937
                        }
2938
}
2939

2940
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
2941
        ExecDirectoryType t;
×
2942

2943
        assert(s);
×
2944

2945
        if (streq(s, "all"))
×
2946
                return EXEC_CLEAN_ALL;
2947
        if (streq(s, "fdstore"))
×
2948
                return EXEC_CLEAN_FDSTORE;
2949

2950
        t = exec_resource_type_from_string(s);
×
2951
        if (t < 0)
×
2952
                return (ExecCleanMask) t;
2953

2954
        return 1U << t;
×
2955
}
2956

2957
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
2958
        [EXEC_INPUT_NULL]      = "null",
2959
        [EXEC_INPUT_TTY]       = "tty",
2960
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
2961
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
2962
        [EXEC_INPUT_SOCKET]    = "socket",
2963
        [EXEC_INPUT_NAMED_FD]  = "fd",
2964
        [EXEC_INPUT_DATA]      = "data",
2965
        [EXEC_INPUT_FILE]      = "file",
2966
};
2967

2968
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,352✔
2969

2970
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
2971
        [EXEC_OUTPUT_INHERIT]             = "inherit",
2972
        [EXEC_OUTPUT_NULL]                = "null",
2973
        [EXEC_OUTPUT_TTY]                 = "tty",
2974
        [EXEC_OUTPUT_KMSG]                = "kmsg",
2975
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
2976
        [EXEC_OUTPUT_JOURNAL]             = "journal",
2977
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
2978
        [EXEC_OUTPUT_SOCKET]              = "socket",
2979
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
2980
        [EXEC_OUTPUT_FILE]                = "file",
2981
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
2982
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
2983
};
2984

2985
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
32,907✔
2986

2987
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
2988
        [EXEC_UTMP_INIT]  = "init",
2989
        [EXEC_UTMP_LOGIN] = "login",
2990
        [EXEC_UTMP_USER]  = "user",
2991
};
2992

2993
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,600✔
2994

2995
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
2996
        [EXEC_PRESERVE_NO]      = "no",
2997
        [EXEC_PRESERVE_YES]     = "yes",
2998
        [EXEC_PRESERVE_RESTART] = "restart",
2999
};
3000

3001
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
17,809✔
3002

3003
/* This table maps ExecDirectoryType to the setting it is configured with in the unit */
3004
static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3005
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectory",
3006
        [EXEC_DIRECTORY_STATE]         = "StateDirectory",
3007
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectory",
3008
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectory",
3009
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectory",
3010
};
3011

3012
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
142,008✔
3013

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

3023
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3024

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

3033
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3034

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

3046
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
365✔
3047

3048
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3049
        [EXEC_KEYRING_INHERIT] = "inherit",
3050
        [EXEC_KEYRING_PRIVATE] = "private",
3051
        [EXEC_KEYRING_SHARED]  = "shared",
3052
};
3053

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