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

systemd / systemd / 16818629404

07 Aug 2025 02:34PM UTC coverage: 72.22% (+0.04%) from 72.181%
16818629404

push

github

bluca
mkosi: include ip in the main image

It's needed by TEST-85-NETWORK.

302392 of 418708 relevant lines covered (72.22%)

648945.65 hits per line

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

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

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

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

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

75
static bool is_terminal_output(ExecOutput o) {
68,597✔
76
        return IN_SET(o,
68,597✔
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,005✔
83
        assert(context);
15,005✔
84

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

88
        if (context->tty_path)
14,321✔
89
                return context->tty_path;
712✔
90

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

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

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

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

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

110
        if (!tty_path)
1,136✔
111
                tty_path = exec_context_tty_path(context);
438✔
112

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

117
        /* Fill in data from kernel command line if anything is unspecified */
118
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
1,136✔
119
                (void) proc_cmdline_tty_size(
1,104✔
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,363✔
129
            exec_context_shall_ansi_seq_reset(context) &&
426✔
130
            isatty_safe(input_fd)) {
199✔
131
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
199✔
132
                if (r < 0)
199✔
133
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
199✔
134
        }
135

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

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

143
        assert(context);
13,323✔
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,323✔
151

152
        if (parameters && parameters->stdout_fd >= 0 && isatty_safe(parameters->stdout_fd))
13,323✔
153
                fd = parameters->stdout_fd;
16✔
154
        else if (path && (context->tty_path || is_terminal_input(context->std_input) ||
13,307✔
155
                        is_terminal_output(context->std_output) || is_terminal_output(context->std_error))) {
12,276✔
156
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
698✔
157
                if (fd < 0)
698✔
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();
714✔
166
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
714✔
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))
714✔
169
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
170
        else if (lock_fd < 0)
714✔
171
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
172

173
        if (context->tty_reset)
714✔
174
                (void) terminal_reset_defensive(
185✔
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));
182✔
178

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

183
        if (!sd_id128_is_null(invocation_id)) {
1,377✔
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)
714✔
201
                (void) terminal_vhangup_fd(fd);
177✔
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);
714✔
206

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

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

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

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

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

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

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

231
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
84,416✔
232
        assert(context);
84,416✔
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,416✔
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,416✔
245
}
246

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

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

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

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

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

262
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
1,964✔
263
}
264

265
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
73,874✔
266
        assert(context);
73,874✔
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))
73,874✔
270
                return false;
271

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

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

280
        assert(context);
32,634✔
281

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

285
        if (!strv_isempty(context->read_write_paths) ||
32,616✔
286
            !strv_isempty(context->read_only_paths) ||
30,528✔
287
            !strv_isempty(context->inaccessible_paths) ||
30,515✔
288
            !strv_isempty(context->exec_paths) ||
30,490✔
289
            !strv_isempty(context->no_exec_paths))
30,490✔
290
                return true;
291

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

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

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

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

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

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

310
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
30,229✔
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,462✔
314
                return true;
315

316
        if (context->private_devices ||
28,986✔
317
            context->private_mounts > 0 ||
28,612✔
318
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,158✔
319
            context->protect_system != PROTECT_SYSTEM_NO ||
28,135✔
320
            context->protect_home != PROTECT_HOME_NO ||
28,135✔
321
            context->protect_kernel_tunables ||
28,135✔
322
            context->protect_kernel_modules ||
28,135✔
323
            context->protect_kernel_logs ||
52,027✔
324
            exec_needs_cgroup_mount(context) ||
26,012✔
325
            context->protect_proc != PROTECT_PROC_DEFAULT ||
25,994✔
326
            context->proc_subset != PROC_SUBSET_ALL ||
25,940✔
327
            context->private_bpf != PRIVATE_BPF_NO ||
51,862✔
328
            exec_needs_ipc_namespace(context) ||
51,862✔
329
            exec_needs_pid_namespace(context, params))
25,931✔
330
                return true;
3,102✔
331

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

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

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

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

351
        if (exec_context_get_effective_bind_log_sockets(context))
25,879✔
352
                return true;
353

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

359
        return false;
360
}
361

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

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

369
        if (!needs_sandboxing)
3,179✔
370
                return NULL;
371

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

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

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

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

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

388
        if (params->debug_invocation)
23,842✔
389
                return LOG_DEBUG;
390

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

394
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
11,197✔
395
        assert(context);
11,197✔
396

397
        if (!context->dynamic_user)
11,197✔
398
                return false;
399

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

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

406
        return true;
407
}
408

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

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

420
        const char *subgroup = NULL;
12,621✔
421
        char *p;
12,621✔
422

423
        assert(params);
12,621✔
424
        assert(c);
12,621✔
425
        assert(ret);
12,621✔
426

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

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

444
        if (subgroup)
656✔
445
                p = path_join(prefix, subgroup);
699✔
446
        else
447
                p = strdup(strempty(prefix));
11,930✔
448
        if (!p)
12,621✔
449
                return -ENOMEM;
450

451
        *ret = p;
12,621✔
452
        return !!subgroup;
12,621✔
453
}
454

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

458
        return c->cpu_affinity_from_numa;
1,153✔
459
}
460

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

466
        if (!DEBUG_LOGGING)
2,135✔
467
                return;
2,135✔
468

469
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
4,270✔
470

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

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

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

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

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

505
        LOG_CONTEXT_PUSH_UNIT(unit);
4,270✔
506

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

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

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

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

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

536
                cgtarget = subcgroup_path;
1✔
537
        } else
538
                cgtarget = params->cgroup_path;
2,134✔
539

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

631
void exec_context_init(ExecContext *c) {
57,591✔
632
        assert(c);
57,591✔
633

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

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

664
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
345,546✔
665
                d->mode = 0755;
287,955✔
666

667
        numa_policy_reset(&c->numa_policy);
57,591✔
668

669
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
57,591✔
670
}
57,591✔
671

672
void exec_context_done(ExecContext *c) {
45,699✔
673
        assert(c);
45,699✔
674

675
        c->environment = strv_free(c->environment);
45,699✔
676
        c->environment_files = strv_free(c->environment_files);
45,699✔
677
        c->pass_environment = strv_free(c->pass_environment);
45,699✔
678
        c->unset_environment = strv_free(c->unset_environment);
45,699✔
679

680
        rlimit_free_all(c->rlimit);
45,699✔
681

682
        for (size_t l = 0; l < 3; l++) {
182,796✔
683
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
137,097✔
684
                c->stdio_file[l] = mfree(c->stdio_file[l]);
137,097✔
685
        }
686

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

705
        c->supplementary_groups = strv_free(c->supplementary_groups);
45,699✔
706

707
        c->pam_name = mfree(c->pam_name);
45,699✔
708

709
        c->read_only_paths = strv_free(c->read_only_paths);
45,699✔
710
        c->read_write_paths = strv_free(c->read_write_paths);
45,699✔
711
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
45,699✔
712
        c->exec_paths = strv_free(c->exec_paths);
45,699✔
713
        c->no_exec_paths = strv_free(c->no_exec_paths);
45,699✔
714
        c->exec_search_path = strv_free(c->exec_search_path);
45,699✔
715

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

724
        cpu_set_done(&c->cpu_set);
45,699✔
725
        numa_policy_reset(&c->numa_policy);
45,699✔
726

727
        c->utmp_id = mfree(c->utmp_id);
45,699✔
728
        c->selinux_context = mfree(c->selinux_context);
45,699✔
729
        c->apparmor_profile = mfree(c->apparmor_profile);
45,699✔
730
        c->smack_process_label = mfree(c->smack_process_label);
45,699✔
731

732
        c->restrict_filesystems = set_free(c->restrict_filesystems);
45,699✔
733

734
        c->syscall_filter = hashmap_free(c->syscall_filter);
45,699✔
735
        c->syscall_archs = set_free(c->syscall_archs);
45,699✔
736
        c->syscall_log = hashmap_free(c->syscall_log);
45,699✔
737
        c->address_families = set_free(c->address_families);
45,699✔
738

739
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
274,194✔
740
                exec_directory_done(d);
228,495✔
741

742
        c->log_level_max = -1;
45,699✔
743

744
        exec_context_free_log_extra_fields(c);
45,699✔
745
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
45,699✔
746
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
45,699✔
747

748
        c->log_ratelimit = (RateLimit) {};
45,699✔
749

750
        c->stdin_data = mfree(c->stdin_data);
45,699✔
751
        c->stdin_data_size = 0;
45,699✔
752

753
        c->network_namespace_path = mfree(c->network_namespace_path);
45,699✔
754
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
45,699✔
755

756
        c->log_namespace = mfree(c->log_namespace);
45,699✔
757

758
        c->load_credentials = hashmap_free(c->load_credentials);
45,699✔
759
        c->set_credentials = hashmap_free(c->set_credentials);
45,699✔
760
        c->import_credentials = ordered_set_free(c->import_credentials);
45,699✔
761

762
        c->root_image_policy = image_policy_free(c->root_image_policy);
45,699✔
763
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
45,699✔
764
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
45,699✔
765

766
        c->private_hostname = mfree(c->private_hostname);
45,699✔
767
}
45,699✔
768

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

772
        if (!runtime_prefix)
5,284✔
773
                return 0;
774

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

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

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

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

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

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

803
        return 0;
804
}
805

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

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

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

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

820
        return 0;
821
}
822

823
void exec_command_done(ExecCommand *c) {
97,768✔
824
        assert(c);
97,768✔
825

826
        c->path = mfree(c->path);
97,768✔
827
        c->argv = strv_free(c->argv);
97,768✔
828
}
97,768✔
829

830
void exec_command_done_array(ExecCommand *c, size_t n) {
27,528✔
831
        FOREACH_ARRAY(i, c, n)
110,110✔
832
                exec_command_done(i);
82,582✔
833
}
27,528✔
834

835
ExecCommand* exec_command_free(ExecCommand *c) {
15,158✔
836
        if (!c)
15,158✔
837
                return NULL;
838

839
        exec_command_done(c);
15,158✔
840
        return mfree(c);
15,158✔
841
}
842

843
ExecCommand* exec_command_free_list(ExecCommand *c) {
116,116✔
844
        ExecCommand *i;
116,116✔
845

846
        while ((i = LIST_POP(command, c)))
131,274✔
847
                exec_command_free(i);
15,158✔
848

849
        return NULL;
116,116✔
850
}
851

852
void exec_command_free_array(ExecCommand **c, size_t n) {
18,143✔
853
        FOREACH_ARRAY(i, c, n)
134,244✔
854
                *i = exec_command_free_list(*i);
116,101✔
855
}
18,143✔
856

857
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
7,279✔
858
        FOREACH_ARRAY(i, c, n)
29,115✔
859
                exec_status_reset(&i->exec_status);
21,836✔
860
}
7,279✔
861

862
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
4,542✔
863
        FOREACH_ARRAY(i, c, n)
30,892✔
864
                LIST_FOREACH(command, z, *i)
28,395✔
865
                        exec_status_reset(&z->exec_status);
2,045✔
866
}
4,542✔
867

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

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

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

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

882
        switch (fd_index) {
39,222✔
883

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

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

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

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

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

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

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

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

911
        assert(c);
2,135✔
912
        assert(ret);
2,135✔
913

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

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

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

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

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

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

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

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

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

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

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

973
        *ret = TAKE_PTR(v);
2,135✔
974

975
        return 0;
2,135✔
976
}
977

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

981
        if (!tty)
328✔
982
                return true;
983

984
        tty = skip_dev_prefix(tty);
328✔
985

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

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

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

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

1000
        return ec->tty_reset ||
44,807✔
1001
                ec->tty_vhangup ||
22,338✔
1002
                ec->tty_vt_disallocate ||
22,338✔
1003
                is_terminal_input(ec->std_input) ||
22,338✔
1004
                is_terminal_output(ec->std_output) ||
44,724✔
1005
                is_terminal_output(ec->std_error);
22,090✔
1006
}
1007

1008
bool exec_context_may_touch_console(const ExecContext *ec) {
21,066✔
1009

1010
        return exec_context_may_touch_tty(ec) &&
21,394✔
1011
               tty_may_match_dev_console(exec_context_tty_path(ec));
328✔
1012
}
1013

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

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

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

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

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

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

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

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

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

1053
        prefix = strempty(prefix);
×
1054

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

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

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

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

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

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

1099
        prefix = strempty(prefix);
224✔
1100

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

1159
        if (c->private_bpf == PRIVATE_BPF_YES) {
224✔
1160
                _cleanup_free_ char
×
1161
                        *commands = bpf_delegate_commands_to_string(c->bpf_delegate_commands),
×
1162
                        *maps = bpf_delegate_maps_to_string(c->bpf_delegate_maps),
×
1163
                        *programs = bpf_delegate_programs_to_string(c->bpf_delegate_programs),
×
1164
                        *attachments = bpf_delegate_attachments_to_string(c->bpf_delegate_attachments);
×
1165

1166
                fprintf(f, "%sBPFDelegateCommands: %s\n", prefix, strna(commands));
×
1167
                fprintf(f, "%sBPFDelegateMaps: %s\n", prefix, strna(maps));
×
1168
                fprintf(f, "%sBPFDelegatePrograms: %s\n", prefix, strna(programs));
×
1169
                fprintf(f, "%sBPFDelegateAttachments: %s\n", prefix, strna(attachments));
×
1170
        }
1171

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

1175
        if (c->root_image)
224✔
1176
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1177

1178
        if (c->root_image_options) {
224✔
1179
                fprintf(f, "%sRootImageOptions:", prefix);
×
1180
                LIST_FOREACH(mount_options, o, c->root_image_options)
×
1181
                        if (!isempty(o->options))
×
1182
                                fprintf(f, " %s:%s",
×
1183
                                        partition_designator_to_string(o->partition_designator),
1184
                                        o->options);
1185
                fprintf(f, "\n");
×
1186
        }
1187

1188
        if (c->root_hash) {
224✔
1189
                _cleanup_free_ char *encoded = NULL;
×
1190
                encoded = hexmem(c->root_hash, c->root_hash_size);
×
1191
                if (encoded)
×
1192
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1193
        }
1194

1195
        if (c->root_hash_path)
224✔
1196
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1197

1198
        if (c->root_hash_sig) {
224✔
1199
                _cleanup_free_ char *encoded = NULL;
×
1200
                ssize_t len;
×
1201
                len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded);
×
1202
                if (len)
×
1203
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1204
        }
1205

1206
        if (c->root_hash_sig_path)
224✔
1207
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1208

1209
        if (c->root_verity)
224✔
1210
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1211

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

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

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

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

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

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

1229
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
1,130✔
1230
                        fprintf(f,
10✔
1231
                                "%s%s: %s%s\n",
1232
                                prefix,
1233
                                exec_directory_type_to_string(dt),
1234
                                c->directories[dt].items[i].path,
1235
                                FLAGS_SET(c->directories[dt].items[i].flags, EXEC_DIRECTORY_READ_ONLY) ? " (ro)" : "");
10✔
1236

1237
                        STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
10✔
1238
                                fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
×
1239
                }
1240
        }
1241

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

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

1247
        if (c->nice_set)
224✔
1248
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
×
1249

1250
        if (c->oom_score_adjust_set)
224✔
1251
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
10✔
1252

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

1256
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
3,808✔
1257
                if (c->rlimit[i]) {
3,584✔
1258
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
20✔
1259
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1260
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
20✔
1261
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
20✔
1262
                }
1263

1264
        if (c->ioprio_is_set) {
224✔
1265
                _cleanup_free_ char *class_str = NULL;
×
1266

1267
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
×
1268
                if (r >= 0)
×
1269
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
×
1270

1271
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
×
1272
        }
1273

1274
        if (c->cpu_sched_set) {
224✔
1275
                _cleanup_free_ char *policy_str = NULL;
×
1276

1277
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1278
                if (r >= 0)
×
1279
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1280

1281
                fprintf(f,
×
1282
                        "%sCPUSchedulingPriority: %i\n"
1283
                        "%sCPUSchedulingResetOnFork: %s\n",
1284
                        prefix, c->cpu_sched_priority,
×
1285
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1286
        }
1287

1288
        if (c->cpu_set.set) {
224✔
1289
                _cleanup_free_ char *affinity = NULL;
×
1290

1291
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1292
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1293
        }
1294

1295
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
224✔
1296
                _cleanup_free_ char *nodes = NULL;
1✔
1297

1298
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1299
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1300
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1301
        }
1302

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

1306
        fprintf(f,
224✔
1307
                "%sStandardInput: %s\n"
1308
                "%sStandardOutput: %s\n"
1309
                "%sStandardError: %s\n",
1310
                prefix, exec_input_to_string(c->std_input),
224✔
1311
                prefix, exec_output_to_string(c->std_output),
224✔
1312
                prefix, exec_output_to_string(c->std_error));
224✔
1313

1314
        if (c->std_input == EXEC_INPUT_NAMED_FD)
224✔
1315
                fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
×
1316
        if (c->std_output == EXEC_OUTPUT_NAMED_FD)
224✔
1317
                fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
×
1318
        if (c->std_error == EXEC_OUTPUT_NAMED_FD)
224✔
1319
                fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
×
1320

1321
        if (c->std_input == EXEC_INPUT_FILE)
224✔
1322
                fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
×
1323
        if (c->std_output == EXEC_OUTPUT_FILE)
224✔
1324
                fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1325
        if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
224✔
1326
                fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1327
        if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1328
                fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1329
        if (c->std_error == EXEC_OUTPUT_FILE)
224✔
1330
                fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1331
        if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
224✔
1332
                fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1333
        if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
224✔
1334
                fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1335

1336
        if (c->tty_path)
224✔
1337
                fprintf(f,
×
1338
                        "%sTTYPath: %s\n"
1339
                        "%sTTYReset: %s\n"
1340
                        "%sTTYVHangup: %s\n"
1341
                        "%sTTYVTDisallocate: %s\n"
1342
                        "%sTTYRows: %u\n"
1343
                        "%sTTYColumns: %u\n",
1344
                        prefix, c->tty_path,
1345
                        prefix, yes_no(c->tty_reset),
×
1346
                        prefix, yes_no(c->tty_vhangup),
×
1347
                        prefix, yes_no(c->tty_vt_disallocate),
×
1348
                        prefix, c->tty_rows,
×
1349
                        prefix, c->tty_cols);
×
1350

1351
        if (IN_SET(c->std_output,
224✔
1352
                   EXEC_OUTPUT_KMSG,
1353
                   EXEC_OUTPUT_JOURNAL,
1354
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1355
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1356
            IN_SET(c->std_error,
17✔
1357
                   EXEC_OUTPUT_KMSG,
1358
                   EXEC_OUTPUT_JOURNAL,
1359
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1360
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1361

1362
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
207✔
1363

1364
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
207✔
1365
                if (r >= 0)
207✔
1366
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
207✔
1367

1368
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
207✔
1369
                if (r >= 0)
207✔
1370
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
207✔
1371
        }
1372

1373
        if (c->log_level_max >= 0) {
224✔
1374
                _cleanup_free_ char *t = NULL;
1✔
1375

1376
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1377

1378
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1379
        }
1380

1381
        if (c->log_ratelimit.interval > 0)
224✔
1382
                fprintf(f,
×
1383
                        "%sLogRateLimitIntervalSec: %s\n",
1384
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1385

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

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

1392
                char *pattern;
×
1393
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1394
                        fprintf(f, " %s", pattern);
×
1395
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1396
                        fprintf(f, " ~%s", pattern);
×
1397
                fputc('\n', f);
×
1398
        }
1399

1400
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
228✔
1401
                fprintf(f, "%sLogExtraFields: ", prefix);
4✔
1402
                fwrite(field->iov_base, 1, field->iov_len, f);
4✔
1403
                fputc('\n', f);
4✔
1404
        }
1405

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

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

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

1417
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
224✔
1418
                _cleanup_free_ char *str = NULL;
16✔
1419

1420
                r = capability_set_to_string(c->capability_bounding_set, &str);
16✔
1421
                if (r >= 0)
16✔
1422
                        fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
16✔
1423
        }
1424

1425
        if (c->capability_ambient_set != 0) {
224✔
1426
                _cleanup_free_ char *str = NULL;
×
1427

1428
                r = capability_set_to_string(c->capability_ambient_set, &str);
×
1429
                if (r >= 0)
×
1430
                        fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
×
1431
        }
1432

1433
        if (c->user)
224✔
1434
                fprintf(f, "%sUser: %s\n", prefix, c->user);
1✔
1435
        if (c->group)
224✔
1436
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1437

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

1440
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
224✔
1441

1442
        if (c->pam_name)
224✔
1443
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
×
1444

1445
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
224✔
1446
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
224✔
1447
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
224✔
1448
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
224✔
1449
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
224✔
1450
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
224✔
1451

1452
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
228✔
1453
                fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
4✔
1454
                        mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
4✔
1455
                        mount->ignore_enoent ? "-": "",
4✔
1456
                        mount->source,
1457
                        mount->destination,
1458
                        mount->recursive ? "rbind" : "norbind");
4✔
1459

1460
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
224✔
1461
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1462
                        tmpfs->path,
1463
                        isempty(tmpfs->options) ? "" : ":",
×
1464
                        strempty(tmpfs->options));
×
1465

1466
        if (c->utmp_id)
224✔
1467
                fprintf(f,
×
1468
                        "%sUtmpIdentifier: %s\n",
1469
                        prefix, c->utmp_id);
1470

1471
        if (c->selinux_context)
224✔
1472
                fprintf(f,
×
1473
                        "%sSELinuxContext: %s%s\n",
1474
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1475

1476
        if (c->apparmor_profile)
224✔
1477
                fprintf(f,
×
1478
                        "%sAppArmorProfile: %s%s\n",
1479
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1480

1481
        if (c->smack_process_label)
224✔
1482
                fprintf(f,
×
1483
                        "%sSmackProcessLabel: %s%s\n",
1484
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1485

1486
        if (c->personality != PERSONALITY_INVALID)
224✔
1487
                fprintf(f,
1✔
1488
                        "%sPersonality: %s\n",
1489
                        prefix, strna(personality_to_string(c->personality)));
1490

1491
        fprintf(f,
224✔
1492
                "%sLockPersonality: %s\n",
1493
                prefix, yes_no(c->lock_personality));
224✔
1494

1495
        if (c->syscall_filter) {
224✔
1496
                fprintf(f,
11✔
1497
                        "%sSystemCallFilter: ",
1498
                        prefix);
1499

1500
                if (!c->syscall_allow_list)
11✔
1501
                        fputc('~', f);
×
1502

1503
#if HAVE_SECCOMP
1504
                void *id, *val;
11✔
1505
                bool first = true;
11✔
1506
                HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
4,279✔
1507
                        _cleanup_free_ char *name = NULL;
4,268✔
1508
                        const char *errno_name = NULL;
4,268✔
1509
                        int num = PTR_TO_INT(val);
4,268✔
1510

1511
                        if (first)
4,268✔
1512
                                first = false;
1513
                        else
1514
                                fputc(' ', f);
4,257✔
1515

1516
                        name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
4,268✔
1517
                        fputs(strna(name), f);
4,268✔
1518

1519
                        if (num >= 0) {
4,268✔
1520
                                errno_name = seccomp_errno_or_action_to_string(num);
×
1521
                                if (errno_name)
×
1522
                                        fprintf(f, ":%s", errno_name);
×
1523
                                else
1524
                                        fprintf(f, ":%d", num);
×
1525
                        }
1526
                }
1527
#endif
1528

1529
                fputc('\n', f);
11✔
1530
        }
1531

1532
        if (c->syscall_archs) {
224✔
1533
                fprintf(f,
11✔
1534
                        "%sSystemCallArchitectures:",
1535
                        prefix);
1536

1537
#if HAVE_SECCOMP
1538
                void *id;
11✔
1539
                SET_FOREACH(id, c->syscall_archs)
22✔
1540
                        fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
11✔
1541
#endif
1542
                fputc('\n', f);
11✔
1543
        }
1544

1545
        if (exec_context_restrict_namespaces_set(c)) {
224✔
1546
                _cleanup_free_ char *s = NULL;
12✔
1547

1548
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
12✔
1549
                if (r >= 0)
12✔
1550
                        fprintf(f, "%sRestrictNamespaces: %s\n",
24✔
1551
                                prefix, strna(s));
1552
        }
1553

1554
#if HAVE_LIBBPF
1555
        if (exec_context_restrict_filesystems_set(c)) {
224✔
1556
                char *fs;
×
1557
                SET_FOREACH(fs, c->restrict_filesystems)
×
1558
                        fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
×
1559
        }
1560
#endif
1561

1562
        if (c->network_namespace_path)
224✔
1563
                fprintf(f,
×
1564
                        "%sNetworkNamespacePath: %s\n",
1565
                        prefix, c->network_namespace_path);
1566

1567
        if (c->syscall_errno > 0) {
224✔
1568
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
223✔
1569

1570
#if HAVE_SECCOMP
1571
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
223✔
1572
                if (errno_name)
223✔
1573
                        fputs(errno_name, f);
223✔
1574
                else
1575
                        fprintf(f, "%d", c->syscall_errno);
×
1576
#endif
1577
                fputc('\n', f);
223✔
1578
        }
1579

1580
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
224✔
1581
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1582
                        mount->ignore_enoent ? "-": "",
×
1583
                        mount->source,
1584
                        mount->destination);
1585
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1586
                        fprintf(f, ":%s:%s",
×
1587
                                partition_designator_to_string(o->partition_designator),
1588
                                strempty(o->options));
×
1589
                fprintf(f, "\n");
×
1590
        }
1591

1592
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
224✔
1593
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1594
                        mount->ignore_enoent ? "-": "",
×
1595
                        mount->source);
1596
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1597
                        fprintf(f, ":%s:%s",
×
1598
                                partition_designator_to_string(o->partition_designator),
1599
                                strempty(o->options));
×
1600
                fprintf(f, "\n");
×
1601
        }
1602

1603
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
224✔
1604
}
224✔
1605

1606
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1607
        assert(c);
×
1608

1609
        /* Returns true if the process forked off would run under
1610
         * an unchanged UID or as root. */
1611

1612
        if (!c->user)
×
1613
                return true;
1614

1615
        if (STR_IN_SET(c->user, "root", "0"))
×
1616
                return true;
×
1617

1618
        return false;
×
1619
}
1620

1621
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,306✔
1622
        int p;
2,306✔
1623

1624
        assert(c);
2,306✔
1625

1626
        if (c->ioprio_is_set)
2,306✔
1627
                return c->ioprio;
12✔
1628

1629
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,294✔
1630
        if (p < 0)
2,294✔
1631
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1632

1633
        return ioprio_normalize(p);
2,294✔
1634
}
1635

1636
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
34,520✔
1637
        assert(c);
34,520✔
1638

1639
        /* Explicit setting wins */
1640
        if (c->mount_apivfs >= 0)
34,520✔
1641
                return c->mount_apivfs > 0;
110✔
1642

1643
        /* Default to "yes" if root directory or image are specified */
1644
        if (exec_context_with_rootfs(c))
34,410✔
1645
                return true;
62✔
1646

1647
        return false;
1648
}
1649

1650
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
29,220✔
1651
        assert(c);
29,220✔
1652

1653
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1654
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1655
        if (c->log_namespace)
29,220✔
1656
                return true;
1657

1658
        if (c->bind_log_sockets >= 0)
29,212✔
1659
                return c->bind_log_sockets > 0;
2✔
1660

1661
        if (exec_context_get_effective_mount_apivfs(c))
29,210✔
1662
                return true;
1663

1664
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1665
        if (exec_context_with_rootfs(c) && c->private_devices)
29,145✔
1666
                return true;
×
1667

1668
        return false;
1669
}
1670

1671
void exec_context_free_log_extra_fields(ExecContext *c) {
45,701✔
1672
        assert(c);
45,701✔
1673

1674
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
45,706✔
1675
                free(field->iov_base);
5✔
1676

1677
        c->log_extra_fields = mfree(c->log_extra_fields);
45,701✔
1678
        c->n_log_extra_fields = 0;
45,701✔
1679
}
45,701✔
1680

1681
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,403✔
1682
        _cleanup_close_ int fd = -EBADF;
1,403✔
1683
        const char *path;
1,403✔
1684
        struct stat st;
1,403✔
1685
        int r;
1,403✔
1686

1687
        assert(c);
1,403✔
1688

1689
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1690
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,403✔
1691

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

1698
        path = exec_context_tty_path(c);
51✔
1699
        if (!path)
51✔
1700
                return;
1701

1702
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
51✔
1703
        if (fd < 0)
51✔
1704
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1705
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1706
                                             path);
1707

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

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

1719
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
51✔
1720
        if (r < 0)
51✔
1721
                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✔
1722
}
1723

1724
int exec_context_get_clean_directories(
×
1725
                ExecContext *c,
1726
                char **prefix,
1727
                ExecCleanMask mask,
1728
                char ***ret) {
1729

1730
        _cleanup_strv_free_ char **l = NULL;
×
1731
        int r;
×
1732

1733
        assert(c);
×
1734
        assert(prefix);
×
1735
        assert(ret);
×
1736

1737
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
1738
                if (!BIT_SET(mask, t))
×
1739
                        continue;
×
1740

1741
                if (!prefix[t])
×
1742
                        continue;
×
1743

1744
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
×
1745
                        char *j;
×
1746

1747
                        j = path_join(prefix[t], i->path);
×
1748
                        if (!j)
×
1749
                                return -ENOMEM;
1750

1751
                        r = strv_consume(&l, j);
×
1752
                        if (r < 0)
×
1753
                                return r;
1754

1755
                        /* Also remove private directories unconditionally. */
1756
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
×
1757
                                j = path_join(prefix[t], "private", i->path);
×
1758
                                if (!j)
×
1759
                                        return -ENOMEM;
1760

1761
                                r = strv_consume(&l, j);
×
1762
                                if (r < 0)
×
1763
                                        return r;
1764
                        }
1765

1766
                        STRV_FOREACH(symlink, i->symlinks) {
×
1767
                                j = path_join(prefix[t], *symlink);
×
1768
                                if (!j)
×
1769
                                        return -ENOMEM;
1770

1771
                                r = strv_consume(&l, j);
×
1772
                                if (r < 0)
×
1773
                                        return r;
1774
                        }
1775
                }
1776
        }
1777

1778
        *ret = TAKE_PTR(l);
×
1779
        return 0;
×
1780
}
1781

1782
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,179✔
1783
        ExecCleanMask mask = 0;
1,179✔
1784

1785
        assert(c);
1,179✔
1786
        assert(ret);
1,179✔
1787

1788
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
7,074✔
1789
                if (c->directories[t].n_items > 0)
5,895✔
1790
                        mask |= 1U << t;
254✔
1791

1792
        *ret = mask;
1,179✔
1793
        return 0;
1,179✔
1794
}
1795

1796
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,153✔
1797
        int n = 0, r;
1,153✔
1798

1799
        assert(c);
1,153✔
1800

1801
        if (c->oom_score_adjust_set)
1,153✔
1802
                return c->oom_score_adjust;
381✔
1803

1804
        r = get_oom_score_adjust(&n);
772✔
1805
        if (r < 0)
772✔
1806
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1807

1808
        return n;
772✔
1809
}
1810

1811
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1,153✔
1812
        _cleanup_free_ char *t = NULL;
1,153✔
1813
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1,153✔
1814
        int r;
1,153✔
1815

1816
        assert(c);
1,153✔
1817

1818
        if (c->coredump_filter_set)
1,153✔
1819
                return c->coredump_filter;
×
1820

1821
        r = read_one_line_file("/proc/self/coredump_filter", &t);
1,153✔
1822
        if (r < 0)
1,153✔
1823
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1824
        else {
1825
                r = safe_atoux64(t, &n);
1,153✔
1826
                if (r < 0)
1,153✔
1827
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1828
        }
1829

1830
        return n;
1,153✔
1831
}
1832

1833
int exec_context_get_nice(const ExecContext *c) {
1,153✔
1834
        int n;
1,153✔
1835

1836
        assert(c);
1,153✔
1837

1838
        if (c->nice_set)
1,153✔
1839
                return c->nice;
4✔
1840

1841
        errno = 0;
1,149✔
1842
        n = getpriority(PRIO_PROCESS, 0);
1,149✔
1843
        if (errno > 0) {
1,149✔
1844
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1845
                n = 0;
1846
        }
1847

1848
        return n;
1849
}
1850

1851
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,153✔
1852
        int n;
1,153✔
1853

1854
        assert(c);
1,153✔
1855

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

1859
        n = sched_getscheduler(0);
1,153✔
1860
        if (n < 0)
1,153✔
1861
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1862

1863
        return n < 0 ? SCHED_OTHER : n;
1,153✔
1864
}
1865

1866
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,153✔
1867
        struct sched_param p = {};
1,153✔
1868
        int r;
1,153✔
1869

1870
        assert(c);
1,153✔
1871

1872
        if (c->cpu_sched_set)
1,153✔
1873
                return c->cpu_sched_priority;
×
1874

1875
        r = sched_getparam(0, &p);
1,153✔
1876
        if (r < 0)
1,153✔
1877
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1878

1879
        return r >= 0 ? p.sched_priority : 0;
1,153✔
1880
}
1881

1882
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,153✔
1883
        int r;
1,153✔
1884

1885
        assert(c);
1,153✔
1886

1887
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,153✔
1888
                return c->timer_slack_nsec;
1889

1890
        r = prctl(PR_GET_TIMERSLACK);
1,153✔
1891
        if (r < 0)
1,153✔
1892
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1893

1894
        return (uint64_t) MAX(r, 0);
1,153✔
1895
}
1896

1897
bool exec_context_get_set_login_environment(const ExecContext *c) {
11,016✔
1898
        assert(c);
11,016✔
1899

1900
        if (c->set_login_environment >= 0)
11,016✔
1901
                return c->set_login_environment;
×
1902

1903
        return c->user || c->dynamic_user || c->pam_name;
19,869✔
1904
}
1905

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

1909
        assert(c);
1,153✔
1910

1911
#if HAVE_SECCOMP
1912
        void *id, *val;
1,153✔
1913
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
12,478✔
1914
                _cleanup_free_ char *name = NULL;
11,325✔
1915
                const char *e = NULL;
11,325✔
1916
                char *s;
11,325✔
1917
                int num = PTR_TO_INT(val);
11,325✔
1918

1919
                if (c->syscall_allow_list && num >= 0)
11,325✔
1920
                        /* syscall with num >= 0 in allow-list is denied. */
1921
                        continue;
×
1922

1923
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
11,325✔
1924
                if (!name)
11,325✔
1925
                        continue;
×
1926

1927
                if (num >= 0) {
11,325✔
1928
                        e = seccomp_errno_or_action_to_string(num);
×
1929
                        if (e) {
×
1930
                                s = strjoin(name, ":", e);
×
1931
                                if (!s)
×
1932
                                        return NULL;
1933
                        } else {
1934
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1935
                                        return NULL;
1936
                        }
1937
                } else
1938
                        s = TAKE_PTR(name);
11,325✔
1939

1940
                if (strv_consume(&l, s) < 0)
11,325✔
1941
                        return NULL;
1942
        }
1943

1944
        strv_sort(l);
1,153✔
1945
#endif
1946

1947
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,153✔
1948
}
1949

1950
char** exec_context_get_syscall_archs(const ExecContext *c) {
1,153✔
1951
        _cleanup_strv_free_ char **l = NULL;
1,153✔
1952

1953
        assert(c);
1,153✔
1954

1955
#if HAVE_SECCOMP
1956
        void *id;
1,153✔
1957
        SET_FOREACH(id, c->syscall_archs) {
1,186✔
1958
                const char *name;
33✔
1959

1960
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
33✔
1961
                if (!name)
33✔
1962
                        continue;
×
1963

1964
                if (strv_extend(&l, name) < 0)
33✔
1965
                        return NULL;
×
1966
        }
1967

1968
        strv_sort(l);
1,153✔
1969
#endif
1970

1971
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,153✔
1972
}
1973

1974
char** exec_context_get_syscall_log(const ExecContext *c) {
1,153✔
1975
        _cleanup_strv_free_ char **l = NULL;
1,153✔
1976

1977
        assert(c);
1,153✔
1978

1979
#if HAVE_SECCOMP
1980
        void *id, *val;
1,153✔
1981
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,153✔
1982
                char *name = NULL;
×
1983

1984
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1985
                if (!name)
×
1986
                        continue;
×
1987

1988
                if (strv_consume(&l, name) < 0)
×
1989
                        return NULL;
×
1990
        }
1991

1992
        strv_sort(l);
1,153✔
1993
#endif
1994

1995
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,153✔
1996
}
1997

1998
char** exec_context_get_address_families(const ExecContext *c) {
1,153✔
1999
        _cleanup_strv_free_ char **l = NULL;
1,153✔
2000
        void *af;
1,153✔
2001

2002
        assert(c);
1,153✔
2003

2004
        SET_FOREACH(af, c->address_families) {
1,251✔
2005
                const char *name;
98✔
2006

2007
                name = af_to_name(PTR_TO_INT(af));
98✔
2008
                if (!name)
98✔
2009
                        continue;
×
2010

2011
                if (strv_extend(&l, name) < 0)
98✔
2012
                        return NULL;
×
2013
        }
2014

2015
        strv_sort(l);
1,153✔
2016

2017
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,153✔
2018
}
2019

2020
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1,153✔
2021
        assert(c);
1,153✔
2022

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

2028
        return strv_sort(l);
1,153✔
2029
#else
2030
        return strv_new(NULL);
2031
#endif
2032
}
2033

2034
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
13,024✔
2035
        assert(c);
13,024✔
2036

2037
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
13,024✔
2038
}
2039

2040
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
14,331✔
2041
        assert(c);
14,331✔
2042

2043
        return c->restrict_filesystems_allow_list ||
14,331✔
2044
          !set_isempty(c->restrict_filesystems);
14,331✔
2045
}
2046

2047
bool exec_context_with_rootfs(const ExecContext *c) {
63,806✔
2048
        assert(c);
63,806✔
2049

2050
        /* Checks if RootDirectory= or RootImage= are used */
2051

2052
        return !empty_or_root(c->root_directory) || c->root_image;
63,806✔
2053
}
2054

2055
int exec_context_has_vpicked_extensions(const ExecContext *context) {
4✔
2056
        int r;
4✔
2057

2058
        assert(context);
4✔
2059

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

2071
        return 0;
2072
}
2073

2074
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
4,228✔
2075
        assert(s);
4,228✔
2076

2077
        *s = (ExecStatus) {
4,228✔
2078
                .pid = pid,
2079
        };
2080

2081
        if (ts)
4,228✔
2082
                s->start_timestamp = *ts;
4,228✔
2083
        else
2084
                dual_timestamp_now(&s->start_timestamp);
×
2085
}
4,228✔
2086

2087
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
1,966✔
2088
        assert(s);
1,966✔
2089

2090
        if (s->pid != pid)
1,966✔
2091
                *s = (ExecStatus) {
6✔
2092
                        .pid = pid,
2093
                };
2094

2095
        dual_timestamp_now(&s->exit_timestamp);
1,966✔
2096

2097
        s->code = code;
1,966✔
2098
        s->status = status;
1,966✔
2099

2100
        if (context && context->utmp_id)
1,966✔
2101
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2102
}
1,966✔
2103

2104
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
7,382✔
2105
        assert(s);
7,382✔
2106
        assert(ucred);
7,382✔
2107
        assert(ts);
7,382✔
2108

2109
        if (ucred->pid != s->pid)
7,382✔
2110
                *s = (ExecStatus) {
7✔
2111
                        .pid = ucred->pid,
2112
                };
2113

2114
        s->handoff_timestamp = *ts;
7,382✔
2115
}
7,382✔
2116

2117
void exec_status_reset(ExecStatus *s) {
25,701✔
2118
        assert(s);
25,701✔
2119

2120
        *s = (ExecStatus) {};
25,701✔
2121
}
25,701✔
2122

2123
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
94✔
2124
        assert(s);
94✔
2125
        assert(f);
94✔
2126

2127
        if (s->pid <= 0)
94✔
2128
                return;
2129

2130
        prefix = strempty(prefix);
10✔
2131

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

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

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

2152
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2153

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

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

2177
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
94✔
2178
        _cleanup_free_ char *cmd = NULL;
188✔
2179
        const char *prefix2;
94✔
2180

2181
        assert(c);
94✔
2182
        assert(f);
94✔
2183

2184
        prefix = strempty(prefix);
94✔
2185
        prefix2 = strjoina(prefix, "\t");
470✔
2186

2187
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
94✔
2188

2189
        fprintf(f,
94✔
2190
                "%sCommand Line: %s\n",
2191
                prefix, strnull(cmd));
2192

2193
        exec_status_dump(&c->exec_status, f, prefix2);
94✔
2194
}
94✔
2195

2196
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
91✔
2197
        assert(f);
91✔
2198

2199
        prefix = strempty(prefix);
91✔
2200

2201
        LIST_FOREACH(command, i, c)
185✔
2202
                exec_command_dump(i, f, prefix);
94✔
2203
}
91✔
2204

2205
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
15,158✔
2206
        ExecCommand *end;
15,158✔
2207

2208
        assert(l);
15,158✔
2209
        assert(e);
15,158✔
2210

2211
        if (*l) {
15,158✔
2212
                /* It's kind of important, that we keep the order here */
2213
                end = LIST_FIND_TAIL(command, *l);
360✔
2214
                LIST_INSERT_AFTER(command, *l, end, e);
123✔
2215
        } else
2216
                *l = e;
15,035✔
2217
}
15,158✔
2218

2219
int exec_command_set(ExecCommand *c, const char *path, ...) {
191✔
2220
        va_list ap;
191✔
2221
        char **l, *p;
191✔
2222

2223
        assert(c);
191✔
2224
        assert(path);
191✔
2225

2226
        va_start(ap, path);
191✔
2227
        l = strv_new_ap(path, ap);
191✔
2228
        va_end(ap);
191✔
2229

2230
        if (!l)
191✔
2231
                return -ENOMEM;
191✔
2232

2233
        p = strdup(path);
191✔
2234
        if (!p) {
191✔
2235
                strv_free(l);
×
2236
                return -ENOMEM;
×
2237
        }
2238

2239
        free_and_replace(c->path, p);
191✔
2240

2241
        return strv_free_and_replace(c->argv, l);
191✔
2242
}
2243

2244
int exec_command_append(ExecCommand *c, const char *path, ...) {
278✔
2245
        char **l;
278✔
2246
        va_list ap;
278✔
2247
        int r;
278✔
2248

2249
        assert(c);
278✔
2250
        assert(path);
278✔
2251

2252
        va_start(ap, path);
278✔
2253
        l = strv_new_ap(path, ap);
278✔
2254
        va_end(ap);
278✔
2255

2256
        if (!l)
278✔
2257
                return -ENOMEM;
278✔
2258

2259
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
278✔
2260
        if (r < 0)
278✔
2261
                return r;
×
2262

2263
        return 0;
2264
}
2265

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

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

2273
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
88✔
2274
        }
2275

2276
        return mfree(path);
88✔
2277
}
2278

2279
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
94,087✔
2280
        assert(rt);
94,087✔
2281

2282
        if (rt->manager)
94,087✔
2283
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
150✔
2284

2285
        rt->id = mfree(rt->id);
94,087✔
2286
        rt->tmp_dir = mfree(rt->tmp_dir);
94,087✔
2287
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
94,087✔
2288
        safe_close_pair(rt->netns_storage_socket);
94,087✔
2289
        safe_close_pair(rt->ipcns_storage_socket);
94,087✔
2290
}
94,087✔
2291

2292
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
94,059✔
2293
        if (!rt)
94,059✔
2294
                return NULL;
2295

2296
        exec_shared_runtime_done(rt);
94,059✔
2297
        return mfree(rt);
94,059✔
2298
}
2299

2300
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
151✔
2301
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
96,452✔
2302

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

2307
        assert(rt->n_ref > 0);
50✔
2308
        rt->n_ref--;
50✔
2309

2310
        if (rt->n_ref > 0)
50✔
2311
                return NULL;
2312

2313
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
50✔
2314
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
50✔
2315

2316
        return exec_shared_runtime_free(rt);
50✔
2317
}
2318

2319
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
94,059✔
2320
        _cleanup_free_ char *id_copy = NULL;
188,118✔
2321
        ExecSharedRuntime *n;
94,059✔
2322

2323
        assert(ret);
94,059✔
2324

2325
        id_copy = strdup(id);
94,059✔
2326
        if (!id_copy)
94,059✔
2327
                return -ENOMEM;
2328

2329
        n = new(ExecSharedRuntime, 1);
94,059✔
2330
        if (!n)
94,059✔
2331
                return -ENOMEM;
2332

2333
        *n = (ExecSharedRuntime) {
94,059✔
2334
                .id = TAKE_PTR(id_copy),
94,059✔
2335
                .netns_storage_socket = EBADF_PAIR,
2336
                .ipcns_storage_socket = EBADF_PAIR,
2337
        };
2338

2339
        *ret = n;
94,059✔
2340
        return 0;
94,059✔
2341
}
2342

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

2352
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
150✔
2353
        int r;
150✔
2354

2355
        assert(m);
150✔
2356
        assert(id);
150✔
2357

2358
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2359

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

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

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

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

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

2382
        rt->manager = m;
150✔
2383

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

2391
static int exec_shared_runtime_make(
5,369✔
2392
                Manager *m,
2393
                const ExecContext *c,
2394
                const char *id,
2395
                ExecSharedRuntime **ret) {
2396

2397
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
5,369✔
2398
        _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
10,738✔
2399
        int r;
5,369✔
2400

2401
        assert(m);
5,369✔
2402
        assert(c);
5,369✔
2403
        assert(id);
5,369✔
2404

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

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

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

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

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

2432
        return 1;
2433
}
2434

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

2439
        assert(m);
5,448✔
2440
        assert(id);
5,448✔
2441
        assert(ret);
5,448✔
2442

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

2448
        if (!create) {
5,369✔
2449
                *ret = NULL;
×
2450
                return 0;
×
2451
        }
2452

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

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

2470
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
62✔
2471
        ExecSharedRuntime *rt;
62✔
2472

2473
        assert(m);
62✔
2474
        assert(f);
62✔
2475
        assert(fds);
62✔
2476

2477
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
162✔
2478
                fprintf(f, "exec-runtime=%s", rt->id);
100✔
2479

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

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

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

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

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

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

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

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

2506
                if (rt->ipcns_storage_socket[0] >= 0) {
100✔
2507
                        int copy;
×
2508

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

2513
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2514
                }
2515

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

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

2523
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2524
                }
2525

2526
                fputc('\n', f);
100✔
2527
        }
2528

2529
        return 0;
62✔
2530
}
2531

2532
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
96,302✔
2533
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
96,302✔
2534
        ExecSharedRuntime *rt = NULL;
96,302✔
2535
        int r;
96,302✔
2536

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

2542
        assert(u);
96,302✔
2543
        assert(key);
96,302✔
2544
        assert(value);
96,302✔
2545

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

2553
        if (u->manager) {
96,302✔
2554
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
96,302✔
2555
                        return log_oom();
×
2556

2557
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
96,302✔
2558
        }
2559
        if (!rt) {
96,302✔
2560
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
93,909✔
2561
                        return log_oom();
×
2562

2563
                rt = rt_create;
93,909✔
2564
        }
2565

2566
        if (streq(key, "tmp-dir")) {
96,302✔
2567
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2568
                        return -ENOMEM;
2569

2570
        } else if (streq(key, "var-tmp-dir")) {
96,302✔
2571
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2572
                        return -ENOMEM;
2573

2574
        } else if (streq(key, "netns-socket-0")) {
96,302✔
2575

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

2581
        } else if (streq(key, "netns-socket-1")) {
96,302✔
2582

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

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

2598
                rt_create->manager = u->manager;
×
2599

2600
                /* Avoid cleanup */
2601
                TAKE_PTR(rt_create);
×
2602
        }
2603

2604
        return 1;
2605
}
2606

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

2614
        assert(m);
79✔
2615
        assert(fds);
79✔
2616

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

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

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

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

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

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

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

2664
                n = strcspn(v, " ");
1✔
2665
                buf = strndupa_safe(v, n);
1✔
2666

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

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

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

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

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

2694
                n = strcspn(v, " ");
×
2695
                buf = strndupa_safe(v, n);
×
2696

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

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

2709
void exec_shared_runtime_vacuum(Manager *m) {
1,470✔
2710
        ExecSharedRuntime *rt;
1,470✔
2711

2712
        assert(m);
1,470✔
2713

2714
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2715

2716
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,549✔
2717
                if (rt->n_ref > 0)
79✔
2718
                        continue;
79✔
2719

2720
                (void) exec_shared_runtime_free(rt);
×
2721
        }
2722
}
1,470✔
2723

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

2735
        assert(unit);
5,448✔
2736
        assert(context);
5,448✔
2737
        assert(ret);
5,448✔
2738

2739
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
5,448✔
2740
                *ret = NULL;
5,297✔
2741
                return 0;
5,297✔
2742
        }
2743

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

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

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

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

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

2769
        *ret = TAKE_PTR(rt);
151✔
2770
        return 1;
151✔
2771
}
2772

2773
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
45,722✔
2774
        if (!rt)
45,722✔
2775
                return NULL;
2776

2777
        exec_shared_runtime_unref(rt->shared);
151✔
2778
        dynamic_creds_unref(rt->dynamic_creds);
151✔
2779

2780
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
151✔
2781

2782
        safe_close_pair(rt->ephemeral_storage_socket);
151✔
2783
        return mfree(rt);
151✔
2784
}
2785

2786
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
5,345✔
2787
        if (!rt)
5,345✔
2788
                return NULL;
2789

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

2795
void exec_runtime_clear(ExecRuntime *rt) {
28✔
2796
        if (!rt)
28✔
2797
                return;
2798

2799
        safe_close_pair(rt->ephemeral_storage_socket);
28✔
2800
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
28✔
2801
}
2802

2803
void exec_params_shallow_clear(ExecParameters *p) {
2,163✔
2804
        if (!p)
2,163✔
2805
                return;
2806

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

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

2823
void exec_params_deep_clear(ExecParameters *p) {
28✔
2824
        if (!p)
28✔
2825
                return;
2826

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

2831
        close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
28✔
2832

2833
        p->cgroup_path = mfree(p->cgroup_path);
28✔
2834

2835
        if (p->prefix) {
28✔
2836
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
28✔
2837
                p->prefix = mfree(p->prefix);
28✔
2838
        }
2839

2840
        p->received_credentials_directory = mfree(p->received_credentials_directory);
28✔
2841
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
28✔
2842

2843
        if (p->idle_pipe) {
28✔
2844
                close_many_and_free(p->idle_pipe, 4);
×
2845
                p->idle_pipe = NULL;
×
2846
        }
2847

2848
        p->stdin_fd = safe_close(p->stdin_fd);
28✔
2849
        p->stdout_fd = safe_close(p->stdout_fd);
28✔
2850
        p->stderr_fd = safe_close(p->stderr_fd);
28✔
2851

2852
        p->notify_socket = mfree(p->notify_socket);
28✔
2853

2854
        open_file_free_many(&p->open_files);
28✔
2855

2856
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
28✔
2857

2858
        exec_params_shallow_clear(p);
28✔
2859
}
2860

2861
void exec_directory_done(ExecDirectory *d) {
228,495✔
2862
        if (!d)
228,495✔
2863
                return;
2864

2865
        FOREACH_ARRAY(i, d->items, d->n_items) {
229,940✔
2866
                free(i->path);
1,445✔
2867
                strv_free(i->symlinks);
1,445✔
2868
        }
2869

2870
        d->items = mfree(d->items);
228,495✔
2871
        d->n_items = 0;
228,495✔
2872
        d->mode = 0755;
228,495✔
2873
}
2874

2875
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
5,111✔
2876
        assert(d);
5,111✔
2877
        assert(path);
5,111✔
2878

2879
        FOREACH_ARRAY(i, d->items, d->n_items)
7,415✔
2880
                if (path_equal(i->path, path))
2,319✔
2881
                        return i;
2882

2883
        return NULL;
2884
}
2885

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

2892
        assert(d);
5,111✔
2893
        assert(path);
5,111✔
2894

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

2901
                existing->flags |= flags;
15✔
2902

2903
                return 0; /* existing item is updated */
15✔
2904
        }
2905

2906
        p = strdup(path);
5,096✔
2907
        if (!p)
5,096✔
2908
                return -ENOMEM;
2909

2910
        if (symlink) {
5,096✔
2911
                s = strv_new(symlink);
6✔
2912
                if (!s)
6✔
2913
                        return -ENOMEM;
2914
        }
2915

2916
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
5,096✔
2917
                return -ENOMEM;
2918

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

2925
        return 1; /* new item is added */
5,096✔
2926
}
2927

2928
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
834✔
2929
        assert(a);
834✔
2930
        assert(b);
834✔
2931

2932
        return path_compare(a->path, b->path);
834✔
2933
}
2934

2935
void exec_directory_sort(ExecDirectory *d) {
124,376✔
2936
        assert(d);
124,376✔
2937

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

2943
        if (d->n_items <= 1)
124,376✔
2944
                return;
2945

2946
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
152✔
2947

2948
        for (size_t i = 1; i < d->n_items; i++)
655✔
2949
                for (size_t j = 0; j < i; j++)
1,668✔
2950
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,180✔
2951
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
2952
                                break;
15✔
2953
                        }
2954
}
2955

2956
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
2957
        ExecDirectoryType t;
×
2958

2959
        assert(s);
×
2960

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

2966
        t = exec_resource_type_from_string(s);
×
2967
        if (t < 0)
×
2968
                return (ExecCleanMask) t;
2969

2970
        return 1U << t;
×
2971
}
2972

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

2984
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
15,956✔
2985

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

3001
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
32,124✔
3002

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

3009
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,261✔
3010

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

3017
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
17,148✔
3018

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

3028
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3029

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

3038
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3039

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

3051
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
258✔
3052

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

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