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

systemd / systemd / 18481385265

13 Oct 2025 10:49PM UTC coverage: 72.262% (-0.03%) from 72.296%
18481385265

push

github

web-flow
core/service: properly handle freezer action -> watchdog propagation (#39222)

7 of 20 new or added lines in 3 files covered. (35.0%)

261 existing lines in 32 files now uncovered.

303483 of 419977 relevant lines covered (72.26%)

1062005.72 hits per line

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

76.25
/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
const char* exec_context_tty_path(const ExecContext *context) {
15,029✔
69
        assert(context);
15,029✔
70

71
        if (context->stdio_as_fds)
15,029✔
72
                return NULL;
73

74
        if (context->tty_path)
14,082✔
75
                return context->tty_path;
697✔
76

77
        return "/dev/console";
78
}
79

80
int exec_context_apply_tty_size(
1,443✔
81
                const ExecContext *context,
82
                int input_fd,
83
                int output_fd,
84
                const char *tty_path) {
85

86
        unsigned rows, cols;
1,443✔
87
        int r;
1,443✔
88

89
        assert(context);
1,443✔
90
        assert(input_fd >= 0);
1,443✔
91
        assert(output_fd >= 0);
1,443✔
92

93
        if (!isatty_safe(output_fd))
1,443✔
94
                return 0;
1,443✔
95

96
        if (!tty_path)
889✔
97
                tty_path = exec_context_tty_path(context);
446✔
98

99
        /* Preferably use explicitly configured data */
100
        rows = context->tty_rows;
889✔
101
        cols = context->tty_cols;
889✔
102

103
        /* Fill in data from kernel command line if anything is unspecified */
104
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
889✔
105
                (void) proc_cmdline_tty_size(
847✔
106
                                tty_path,
107
                                rows == UINT_MAX ? &rows : NULL,
108
                                cols == UINT_MAX ? &cols : NULL);
109

110
        /* If we got nothing so far and we are talking to a physical device, then let's query dimensions from
111
         * the ANSI terminal driver. Note that we will not bother with this in case terminal reset via ansi
112
         * sequences is not enabled, as the DSR logic relies on ANSI sequences after all, and if we shall not
113
         * use those during initialization we need to skip it. */
114
        if (rows == UINT_MAX && cols == UINT_MAX &&
1,114✔
115
            exec_context_shall_ansi_seq_reset(context) &&
412✔
116
            isatty_safe(input_fd)) {
187✔
117
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
187✔
118
                if (r < 0)
187✔
119
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
187✔
120
        }
121

122
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
889✔
123
}
124

125
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *parameters, sd_id128_t invocation_id) {
13,261✔
126
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
26,522✔
127
        int fd, r;
13,261✔
128

129
        assert(context);
13,261✔
130

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

136
        const char *path = exec_context_tty_path(context);
13,261✔
137

138
        if (parameters && parameters->stdout_fd >= 0 && isatty_safe(parameters->stdout_fd))
13,261✔
139
                fd = parameters->stdout_fd;
21✔
140
        else if (path && exec_context_has_tty(context)) {
13,240✔
141
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
443✔
142
                if (fd < 0)
443✔
UNCOV
143
                        return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path);
×
144
        } else
145
                return;   /* nothing to do */
146

147
        /* Take a synchronization lock for the duration of the setup that we do here.
148
         * systemd-vconsole-setup.service also takes the lock to avoid being interrupted. We open a new fd
149
         * that will be closed automatically, and operate on it for convenience. */
150
        lock_fd = lock_dev_console();
464✔
151
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
464✔
152
                log_debug_errno(lock_fd, "No privileges to lock /dev/console, proceeding without lock: %m");
×
153
        else if (ERRNO_IS_NEG_DEVICE_ABSENT(lock_fd))
464✔
154
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
155
        else if (lock_fd < 0)
464✔
156
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
157

158
        if (context->tty_reset)
464✔
159
                (void) terminal_reset_defensive(
405✔
160
                                fd,
161
                                TERMINAL_RESET_SWITCH_TO_TEXT |
162
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
402✔
163

164
        r = exec_context_apply_tty_size(context, fd, fd, path);
464✔
165
        if (r < 0)
464✔
166
                log_debug_errno(r, "Failed to configure TTY dimensions, ignoring: %m");
×
167

168
        if (!sd_id128_is_null(invocation_id) && exec_context_shall_ansi_seq_reset(context)) {
890✔
169
                sd_id128_t context_id;
24✔
170

171
                r = osc_context_id_from_invocation_id(invocation_id, &context_id);
24✔
172
                if (r < 0)
24✔
173
                        log_debug_errno(r, "Failed to derive context ID from invocation ID, ignoring: %m");
24✔
174
                else {
175
                        _cleanup_free_ char *seq = NULL;
24✔
176

177
                        r = osc_context_close(context_id, &seq);
24✔
178
                        if (r < 0)
24✔
179
                                log_debug_errno(r, "Failed to acquire OSC close sequence, ignoring: %m");
×
180
                        else
181
                                (void) loop_write(fd, seq, SIZE_MAX);
24✔
182
                }
183
        }
184

185
        if (context->tty_vhangup)
464✔
186
                (void) terminal_vhangup_fd(fd);
173✔
187

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

192
        if (context->tty_vt_disallocate && path)
464✔
193
                (void) vt_disallocate(path);
93✔
194
}
195

196
bool exec_needs_network_namespace(const ExecContext *context) {
58,410✔
197
        assert(context);
58,410✔
198

199
        return context->private_network || context->network_namespace_path;
58,410✔
200
}
201

202
static bool exec_needs_ephemeral(const ExecContext *context) {
5,527✔
203
        return (context->root_image || context->root_directory) && context->root_ephemeral;
5,527✔
204
}
205

206
bool exec_needs_ipc_namespace(const ExecContext *context) {
52,990✔
207
        assert(context);
52,990✔
208

209
        return context->private_ipc || context->ipc_namespace_path;
52,990✔
210
}
211

212
static bool needs_cgroup_namespace(ProtectControlGroups i) {
137,089✔
213
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
137,089✔
214
}
215

216
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
84,112✔
217
        assert(context);
84,112✔
218

219
        /* If cgroup namespace is configured via ProtectControlGroups=private or strict but we can't actually
220
         * use cgroup namespace, we ignore the setting and do not unshare the namespace.
221
         * ProtectControlGroups=private and strict get downgraded to no and yes respectively. This ensures
222
         * that strict always gets a read-only mount of /sys/fs/cgroup/. */
223
        if (needs_cgroup_namespace(context->protect_control_groups) && !namespace_type_supported(NAMESPACE_CGROUP)) {
84,112✔
224
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_PRIVATE)
×
225
                        return PROTECT_CONTROL_GROUPS_NO;
226
                if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_STRICT)
×
227
                        return PROTECT_CONTROL_GROUPS_YES;
228
        }
229
        return context->protect_control_groups;
84,112✔
230
}
231

232
bool exec_needs_cgroup_namespace(const ExecContext *context) {
52,977✔
233
        assert(context);
52,977✔
234

235
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
52,977✔
236
}
237

238
bool exec_needs_cgroup_mount(const ExecContext *context) {
27,197✔
239
        assert(context);
27,197✔
240

241
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
27,197✔
242
}
243

244
bool exec_is_cgroup_mount_read_only(const ExecContext *context) {
1,969✔
245
        assert(context);
1,969✔
246

247
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
1,969✔
248
}
249

250
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
74,979✔
251
        assert(context);
74,979✔
252

253
        /* PID namespaces don't really make sense for control processes so let's not use them for those. */
254
        if (params && FLAGS_SET(params->flags, EXEC_IS_CONTROL))
74,979✔
255
                return false;
256

257
        return context->private_pids != PRIVATE_PIDS_NO && namespace_type_supported(NAMESPACE_PID);
67,440✔
258
}
259

260
bool exec_needs_mount_namespace(
32,598✔
261
                const ExecContext *context,
262
                const ExecParameters *params,
263
                const ExecRuntime *runtime) {
264

265
        assert(context);
32,598✔
266

267
        if (context->root_image)
32,598✔
268
                return true;
269

270
        if (!strv_isempty(context->read_write_paths) ||
32,572✔
271
            !strv_isempty(context->read_only_paths) ||
30,415✔
272
            !strv_isempty(context->inaccessible_paths) ||
30,402✔
273
            !strv_isempty(context->exec_paths) ||
30,377✔
274
            !strv_isempty(context->no_exec_paths))
30,377✔
275
                return true;
276

277
        if (context->n_bind_mounts > 0)
30,377✔
278
                return true;
279

280
        if (context->n_temporary_filesystems > 0)
30,320✔
281
                return true;
282

283
        if (context->n_mount_images > 0)
30,161✔
284
                return true;
285

286
        if (context->n_extension_images > 0)
30,138✔
287
                return true;
288

289
        if (!strv_isempty(context->extension_directories))
30,127✔
290
                return true;
291

292
        if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
30,122✔
293
                return true;
294

295
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
30,122✔
296
                return true;
297

298
        if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
29,367✔
299
                return true;
300

301
        if (context->private_devices ||
28,889✔
302
            context->private_mounts > 0 ||
28,521✔
303
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,057✔
304
            context->protect_system != PROTECT_SYSTEM_NO ||
28,034✔
305
            context->protect_home != PROTECT_HOME_NO ||
28,034✔
306
            context->protect_kernel_tunables ||
28,034✔
307
            context->protect_kernel_modules ||
28,034✔
308
            context->protect_kernel_logs ||
51,851✔
309
            exec_needs_cgroup_mount(context) ||
25,924✔
310
            context->protect_proc != PROTECT_PROC_DEFAULT ||
25,906✔
311
            context->proc_subset != PROC_SUBSET_ALL ||
25,846✔
312
            context->private_bpf != PRIVATE_BPF_NO ||
51,662✔
313
            exec_needs_ipc_namespace(context) ||
51,662✔
314
            exec_needs_pid_namespace(context, params))
25,831✔
315
                return true;
3,105✔
316

317
        if (context->root_directory) {
25,784✔
318
                if (exec_context_get_effective_mount_apivfs(context))
5✔
319
                        return true;
320

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

325
                        if (context->directories[t].n_items > 0)
×
326
                                return true;
327
                }
328
        }
329

330
        if (context->dynamic_user &&
25,779✔
331
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
332
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
333
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
334
                return true;
335

336
        if (exec_context_get_effective_bind_log_sockets(context))
25,779✔
337
                return true;
338

339
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
154,330✔
340
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
133,482✔
341
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
4,925✔
342
                                return true;
343

344
        return false;
345
}
346

347
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
3,816✔
348
        assert(context);
3,816✔
349
        assert(params);
3,816✔
350

351
        if (!params->notify_socket)
3,816✔
352
                return NULL;
353

354
        if (!needs_sandboxing)
3,141✔
355
                return NULL;
356

357
        if (!context->root_directory && !context->root_image)
3,141✔
358
                return NULL;
359

360
        if (!exec_context_get_effective_mount_apivfs(context))
×
361
                return NULL;
362

363
        if (!FLAGS_SET(params->flags, EXEC_APPLY_CHROOT))
×
364
                return NULL;
×
365

366
        return "/run/host/notify";
367
}
368

369
int exec_log_level_max(const ExecContext *context, const ExecParameters *params) {
23,746✔
370
        assert(context);
23,746✔
371
        assert(params);
23,746✔
372

373
        if (params->debug_invocation)
23,746✔
374
                return LOG_DEBUG;
375

376
        return context->log_level_max < 0 ? log_get_max_level() : context->log_level_max;
23,742✔
377
}
378

379
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
11,127✔
380
        assert(context);
11,127✔
381

382
        if (!context->dynamic_user)
11,127✔
383
                return false;
384

385
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
104✔
386
                return false;
387

388
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
98✔
389
                return false;
14✔
390

391
        return true;
392
}
393

394
int exec_params_needs_control_subcgroup(const ExecParameters *params) {
2,147✔
395
        /* Keep this in sync with exec_params_get_cgroup_path(). */
396
        return FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE|EXEC_CONTROL_CGROUP|EXEC_IS_CONTROL);
2,147✔
397
}
398

399
int exec_params_get_cgroup_path(
12,572✔
400
                const ExecParameters *params,
401
                const CGroupContext *c,
402
                const char *prefix,
403
                char **ret) {
404

405
        const char *subgroup = NULL;
12,572✔
406
        char *p;
12,572✔
407

408
        assert(params);
12,572✔
409
        assert(c);
12,572✔
410
        assert(ret);
12,572✔
411

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

421
        /* Keep this in sync with exec_params_needs_control_subcgroup(). */
422
        if (FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE) && (FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP) || c->delegate_subgroup)) {
12,572✔
423
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
697✔
424
                        subgroup = ".control";
425
                else
426
                        subgroup = c->delegate_subgroup;
654✔
427
        }
428

429
        if (subgroup)
654✔
430
                p = path_join(prefix, subgroup);
697✔
431
        else
432
                p = strdup(strempty(prefix));
11,883✔
433
        if (!p)
12,572✔
434
                return -ENOMEM;
435

436
        *ret = p;
12,572✔
437
        return !!subgroup;
12,572✔
438
}
439

440
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
1,196✔
441
        assert(c);
1,196✔
442

443
        return c->cpu_affinity_from_numa;
1,196✔
444
}
445

446
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
2,117✔
447
        assert(unit);
2,117✔
448
        assert(msg);
2,117✔
449
        assert(executable);
2,117✔
450

451
        if (!DEBUG_LOGGING)
2,117✔
452
                return;
2,117✔
453

454
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
4,234✔
455

456
        log_unit_struct(unit, LOG_DEBUG,
2,117✔
457
                        LOG_ITEM("EXECUTABLE=%s", executable),
458
                        LOG_UNIT_MESSAGE(unit, "%s: %s", msg, strnull(cmdline)),
459
                        LOG_UNIT_INVOCATION_ID(unit));
460
}
461

462
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
463

464
int exec_spawn(
2,117✔
465
                Unit *unit,
466
                ExecCommand *command,
467
                const ExecContext *context,
468
                ExecParameters *params,
469
                ExecRuntime *runtime,
470
                const CGroupContext *cgroup_context,
471
                PidRef *ret) {
472

473
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL;
2,117✔
474
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
475
        _cleanup_fclose_ FILE *f = NULL;
2,117✔
476
        int r;
2,117✔
477

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

490
        LOG_CONTEXT_PUSH_UNIT(unit);
4,234✔
491

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

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

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

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

516
                        r = cg_create(subcgroup_path);
1✔
517
                        if (r < 0)
1✔
518
                                return log_unit_error_errno(unit, r, "Failed to create subcgroup '%s': %m", subcgroup_path);
×
519
                }
520

521
                cgtarget = subcgroup_path;
1✔
522
        } else
523
                cgtarget = params->cgroup_path;
2,116✔
524

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

532
        r = open_serialization_file("sd-executor-state", &f);
2,117✔
533
        if (r < 0)
2,117✔
534
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
535

536
        fdset = fdset_new();
2,117✔
537
        if (!fdset)
2,117✔
538
                return log_oom();
×
539

540
        r = exec_serialize_invocation(f, fdset, context, command, params, runtime, cgroup_context);
2,117✔
541
        if (r < 0)
2,117✔
542
                return log_unit_error_errno(unit, r, "Failed to serialize parameters: %m");
×
543

544
        r = finish_serialization_file(f);
2,117✔
545
        if (r < 0)
2,117✔
546
                return log_unit_error_errno(unit, r, "Failed to finish serialization stream: %m");
×
547

548
        r = fd_cloexec(fileno(f), false);
2,117✔
549
        if (r < 0)
2,117✔
550
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m");
×
551

552
        r = fdset_cloexec(fdset, false);
2,117✔
553
        if (r < 0)
2,117✔
554
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m");
×
555

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

563
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,117✔
564
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,117✔
565

566
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,117✔
567
        dual_timestamp start_timestamp;
2,117✔
568

569
        /* Restore the original ambient capability set the manager was started with to pass it to
570
         * sd-executor. */
571
        r = capability_ambient_set_apply(unit->manager->saved_ambient_set, /* also_inherit= */ false);
2,117✔
572
        if (r < 0)
2,117✔
573
                return log_unit_error_errno(unit, r, "Failed to apply the starting ambient set: %m");
×
574

575
        /* Record the start timestamp before we fork so that it is guaranteed to be earlier than the
576
         * handoff timestamp. */
577
        dual_timestamp_now(&start_timestamp);
2,117✔
578

579
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
580
        r = posix_spawn_wrapper(
2,117✔
581
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
2,117✔
582
                        STRV_MAKE(unit->manager->executor_path,
2,117✔
583
                                  "--deserialize", serialization_fd_number,
584
                                  "--log-level", max_log_levels,
585
                                  "--log-target", log_target_to_string(manager_get_executor_log_target(unit->manager))),
586
                        environ,
587
                        cgtarget,
588
                        &pidref);
589

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

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

607
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
2,117✔
608
                       command->path, pidref.pid, r > 0 ? "via" : "without");
609

610
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,117✔
611

612
        *ret = TAKE_PIDREF(pidref);
2,117✔
613
        return 0;
2,117✔
614
}
615

616
void exec_context_init(ExecContext *c) {
59,428✔
617
        assert(c);
59,428✔
618

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

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

649
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
356,568✔
650
                d->mode = 0755;
297,140✔
651

652
        numa_policy_reset(&c->numa_policy);
59,428✔
653

654
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
59,428✔
655
}
59,428✔
656

657
void exec_context_done(ExecContext *c) {
47,584✔
658
        assert(c);
47,584✔
659

660
        c->environment = strv_free(c->environment);
47,584✔
661
        c->environment_files = strv_free(c->environment_files);
47,584✔
662
        c->pass_environment = strv_free(c->pass_environment);
47,584✔
663
        c->unset_environment = strv_free(c->unset_environment);
47,584✔
664

665
        rlimit_free_all(c->rlimit);
47,584✔
666

667
        for (size_t l = 0; l < 3; l++) {
190,336✔
668
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
142,752✔
669
                c->stdio_file[l] = mfree(c->stdio_file[l]);
142,752✔
670
        }
671

672
        c->working_directory = mfree(c->working_directory);
47,584✔
673
        c->root_directory = mfree(c->root_directory);
47,584✔
674
        c->root_image = mfree(c->root_image);
47,584✔
675
        c->root_image_options = mount_options_free_all(c->root_image_options);
47,584✔
676
        c->root_hash = mfree(c->root_hash);
47,584✔
677
        c->root_hash_size = 0;
47,584✔
678
        c->root_hash_path = mfree(c->root_hash_path);
47,584✔
679
        c->root_hash_sig = mfree(c->root_hash_sig);
47,584✔
680
        c->root_hash_sig_size = 0;
47,584✔
681
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
47,584✔
682
        c->root_verity = mfree(c->root_verity);
47,584✔
683
        c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
47,584✔
684
        c->extension_directories = strv_free(c->extension_directories);
47,584✔
685
        c->tty_path = mfree(c->tty_path);
47,584✔
686
        c->syslog_identifier = mfree(c->syslog_identifier);
47,584✔
687
        c->user = mfree(c->user);
47,584✔
688
        c->group = mfree(c->group);
47,584✔
689

690
        c->supplementary_groups = strv_free(c->supplementary_groups);
47,584✔
691

692
        c->pam_name = mfree(c->pam_name);
47,584✔
693

694
        c->read_only_paths = strv_free(c->read_only_paths);
47,584✔
695
        c->read_write_paths = strv_free(c->read_write_paths);
47,584✔
696
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
47,584✔
697
        c->exec_paths = strv_free(c->exec_paths);
47,584✔
698
        c->no_exec_paths = strv_free(c->no_exec_paths);
47,584✔
699
        c->exec_search_path = strv_free(c->exec_search_path);
47,584✔
700

701
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
47,584✔
702
        c->bind_mounts = NULL;
47,584✔
703
        c->n_bind_mounts = 0;
47,584✔
704
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
47,584✔
705
        c->temporary_filesystems = NULL;
47,584✔
706
        c->n_temporary_filesystems = 0;
47,584✔
707
        c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
47,584✔
708

709
        cpu_set_done(&c->cpu_set);
47,584✔
710
        numa_policy_reset(&c->numa_policy);
47,584✔
711

712
        c->utmp_id = mfree(c->utmp_id);
47,584✔
713
        c->selinux_context = mfree(c->selinux_context);
47,584✔
714
        c->apparmor_profile = mfree(c->apparmor_profile);
47,584✔
715
        c->smack_process_label = mfree(c->smack_process_label);
47,584✔
716

717
        c->restrict_filesystems = set_free(c->restrict_filesystems);
47,584✔
718

719
        c->syscall_filter = hashmap_free(c->syscall_filter);
47,584✔
720
        c->syscall_archs = set_free(c->syscall_archs);
47,584✔
721
        c->syscall_log = hashmap_free(c->syscall_log);
47,584✔
722
        c->address_families = set_free(c->address_families);
47,584✔
723

724
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
285,504✔
725
                exec_directory_done(d);
237,920✔
726

727
        c->log_level_max = -1;
47,584✔
728

729
        exec_context_free_log_extra_fields(c);
47,584✔
730
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
47,584✔
731
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
47,584✔
732

733
        c->log_ratelimit = (RateLimit) {};
47,584✔
734

735
        c->stdin_data = mfree(c->stdin_data);
47,584✔
736
        c->stdin_data_size = 0;
47,584✔
737

738
        c->network_namespace_path = mfree(c->network_namespace_path);
47,584✔
739
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
47,584✔
740

741
        c->log_namespace = mfree(c->log_namespace);
47,584✔
742

743
        c->load_credentials = hashmap_free(c->load_credentials);
47,584✔
744
        c->set_credentials = hashmap_free(c->set_credentials);
47,584✔
745
        c->import_credentials = ordered_set_free(c->import_credentials);
47,584✔
746

747
        c->root_image_policy = image_policy_free(c->root_image_policy);
47,584✔
748
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
47,584✔
749
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
47,584✔
750

751
        c->private_hostname = mfree(c->private_hostname);
47,584✔
752
}
47,584✔
753

754
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
5,512✔
755
        assert(c);
5,512✔
756

757
        if (!runtime_prefix)
5,512✔
758
                return 0;
759

760
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
5,513✔
761
                _cleanup_free_ char *p = NULL;
1✔
762

763
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
1✔
764
                        p = path_join(runtime_prefix, "private", i->path);
×
765
                else
766
                        p = path_join(runtime_prefix, i->path);
1✔
767
                if (!p)
1✔
768
                        return -ENOMEM;
769

770
                /* We execute this synchronously, since we need to be sure this is gone when we start the
771
                 * service next. */
772
                (void) rm_rf(p, REMOVE_ROOT);
1✔
773

774
                STRV_FOREACH(symlink, i->symlinks) {
1✔
775
                        _cleanup_free_ char *symlink_abs = NULL;
×
776

777
                        if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
×
778
                                symlink_abs = path_join(runtime_prefix, "private", *symlink);
×
779
                        else
780
                                symlink_abs = path_join(runtime_prefix, *symlink);
×
781
                        if (!symlink_abs)
×
782
                                return -ENOMEM;
×
783

784
                        (void) unlink(symlink_abs);
×
785
                }
786
        }
787

788
        return 0;
789
}
790

791
int exec_context_destroy_mount_ns_dir(Unit *u) {
11,874✔
792
        _cleanup_free_ char *p = NULL;
11,874✔
793

794
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
11,874✔
795
                return 0;
796

797
        p = path_join("/run/systemd/propagate/", u->id);
2,210✔
798
        if (!p)
2,210✔
799
                return -ENOMEM;
800

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

805
        return 0;
806
}
807

808
void exec_command_done(ExecCommand *c) {
101,280✔
809
        assert(c);
101,280✔
810

811
        c->path = mfree(c->path);
101,280✔
812
        c->argv = strv_free(c->argv);
101,280✔
813
}
101,280✔
814

815
void exec_command_done_array(ExecCommand *c, size_t n) {
28,435✔
816
        FOREACH_ARRAY(i, c, n)
113,738✔
817
                exec_command_done(i);
85,303✔
818
}
28,435✔
819

820
ExecCommand* exec_command_free(ExecCommand *c) {
15,949✔
821
        if (!c)
15,949✔
822
                return NULL;
823

824
        exec_command_done(c);
15,949✔
825
        return mfree(c);
15,949✔
826
}
827

828
ExecCommand* exec_command_free_list(ExecCommand *c) {
121,914✔
829
        ExecCommand *i;
121,914✔
830

831
        while ((i = LIST_POP(command, c)))
137,863✔
832
                exec_command_free(i);
15,949✔
833

834
        return NULL;
121,914✔
835
}
836

837
void exec_command_free_array(ExecCommand **c, size_t n) {
19,121✔
838
        FOREACH_ARRAY(i, c, n)
141,020✔
839
                *i = exec_command_free_list(*i);
121,899✔
840
}
19,121✔
841

842
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
7,321✔
843
        FOREACH_ARRAY(i, c, n)
29,283✔
844
                exec_status_reset(&i->exec_status);
21,962✔
845
}
7,321✔
846

847
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
4,767✔
848
        FOREACH_ARRAY(i, c, n)
32,214✔
849
                LIST_FOREACH(command, z, *i)
29,493✔
850
                        exec_status_reset(&z->exec_status);
2,046✔
851
}
4,767✔
852

853
typedef struct InvalidEnvInfo {
854
        const Unit *unit;
855
        const char *path;
856
} InvalidEnvInfo;
857

858
static void invalid_env(const char *p, void *userdata) {
×
859
        InvalidEnvInfo *info = userdata;
×
860

861
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
862
}
×
863

864
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
39,207✔
865
        assert(c);
39,207✔
866

867
        switch (fd_index) {
39,207✔
868

869
        case STDIN_FILENO:
13,069✔
870
                if (c->std_input != EXEC_INPUT_NAMED_FD)
13,069✔
871
                        return NULL;
872

873
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
874

875
        case STDOUT_FILENO:
13,069✔
876
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
13,069✔
877
                        return NULL;
878

879
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
880

881
        case STDERR_FILENO:
13,069✔
882
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
13,069✔
883
                        return NULL;
884

885
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
886

887
        default:
888
                return NULL;
889
        }
890
}
891

892
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
2,117✔
893
        _cleanup_strv_free_ char **v = NULL;
2,117✔
894
        int r;
2,117✔
895

896
        assert(c);
2,117✔
897
        assert(ret);
2,117✔
898

899
        STRV_FOREACH(i, c->environment_files) {
2,119✔
900
                _cleanup_strv_free_ char **paths = NULL;
2✔
901
                bool ignore = false;
2✔
902
                char *fn = *i;
2✔
903

904
                if (fn[0] == '-') {
2✔
905
                        ignore = true;
1✔
906
                        fn++;
1✔
907
                }
908

909
                if (!path_is_absolute(fn)) {
2✔
910
                        if (ignore)
×
911
                                continue;
×
912
                        return -EINVAL;
913
                }
914

915
                /* Filename supports globbing, take all matching files */
916
                r = safe_glob(fn, /* flags = */ 0, &paths);
2✔
917
                if (r < 0) {
2✔
918
                        if (ignore)
1✔
919
                                continue;
1✔
920
                        return r;
921
                }
922

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

926
                STRV_FOREACH(path, paths) {
2✔
927
                        _cleanup_strv_free_ char **p = NULL;
1✔
928

929
                        r = load_env_file(NULL, *path, &p);
1✔
930
                        if (r < 0) {
1✔
931
                                if (ignore)
×
932
                                        continue;
×
933
                                return r;
934
                        }
935

936
                        /* Log invalid environment variables with filename */
937
                        if (p) {
1✔
938
                                InvalidEnvInfo info = {
1✔
939
                                        .unit = unit,
940
                                        .path = *path,
1✔
941
                                };
942

943
                                strv_env_clean_with_callback(p, invalid_env, &info);
1✔
944
                        }
945

946
                        if (!v)
1✔
947
                                v = TAKE_PTR(p);
1✔
948
                        else {
949
                                char **m = strv_env_merge(v, p);
×
950
                                if (!m)
×
951
                                        return -ENOMEM;
×
952

953
                                strv_free_and_replace(v, m);
×
954
                        }
955
                }
956
        }
957

958
        *ret = TAKE_PTR(v);
2,117✔
959

960
        return 0;
2,117✔
961
}
962

963
static bool tty_may_match_dev_console(const char *tty) {
229✔
964
        _cleanup_free_ char *resolved = NULL;
229✔
965

966
        if (!tty)
229✔
967
                return true;
968

969
        tty = skip_dev_prefix(tty);
229✔
970

971
        /* trivial identity? */
972
        if (streq(tty, "console"))
229✔
973
                return true;
974

975
        if (resolve_dev_console(&resolved) < 0)
39✔
976
                return true; /* if we could not resolve, assume it may */
977

978
        /* "tty0" means the active VC, so it may be the same sometimes */
979
        return path_equal(skip_dev_prefix(resolved), tty) || (streq(skip_dev_prefix(resolved), "tty0") && tty_is_vc(tty));
39✔
980
}
981

982
static bool exec_context_may_touch_tty(const ExecContext *ec) {
23,023✔
983
        assert(ec);
23,023✔
984

985
        return ec->tty_reset ||
45,828✔
986
                ec->tty_vhangup ||
22,805✔
987
                ec->tty_vt_disallocate ||
22,805✔
988
                exec_input_is_terminal(ec->std_input) ||
22,805✔
989
                ec->std_output == EXEC_OUTPUT_TTY ||
45,828✔
990
                ec->std_error == EXEC_OUTPUT_TTY;
22,756✔
991
}
992

993
bool exec_context_may_touch_console(const ExecContext *ec) {
21,634✔
994

995
        return exec_context_may_touch_tty(ec) &&
21,863✔
996
               tty_may_match_dev_console(exec_context_tty_path(ec));
229✔
997
}
998

999
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,644✔
1000
        assert(c);
1,644✔
1001

1002
        /* Determines whether ANSI sequences shall be used during any terminal initialisation:
1003
         *
1004
         * 1. If the reset logic is enabled at all, this is an immediate no.
1005
         *
1006
         * 2. If $TERM is set to anything other than "dumb", it's a yes.
1007
         */
1008

1009
        if (!c->tty_reset)
1,644✔
1010
                return false;
1011

1012
        /* FIXME:
1013
         * On invocation, we generate $TERM based on settings for StandardOutput= and friends and the kernel
1014
         * command line options, or propagate $TERM from the service manager. See setup_term_environment(). */
1015
        return !streq_ptr(strv_env_get(c->environment, "TERM"), "dumb");
997✔
1016
}
1017

1018
static void strv_fprintf(FILE *f, char **l) {
×
1019
        assert(f);
×
1020

1021
        STRV_FOREACH(g, l)
×
1022
                fprintf(f, " %s", *g);
×
1023
}
×
1024

1025
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
1,824✔
1026
        assert(f);
1,824✔
1027
        assert(prefix);
1,824✔
1028
        assert(name);
1,824✔
1029

1030
        if (!strv_isempty(strv)) {
1,824✔
1031
                fprintf(f, "%s%s:", prefix, name);
×
1032
                strv_fprintf(f, strv);
×
1033
                fputs("\n", f);
×
1034
        }
1035
}
1,824✔
1036

1037
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1038
        assert(p);
×
1039
        assert(f);
×
1040

1041
        prefix = strempty(prefix);
×
1042

1043
        fprintf(f,
×
1044
                "%sRuntimeScope: %s\n"
1045
                "%sExecFlags: %u\n"
1046
                "%sSELinuxContextNetwork: %s\n"
1047
                "%sCgroupSupportedMask: %u\n"
1048
                "%sCgroupPath: %s\n"
1049
                "%sCrededentialsDirectory: %s\n"
1050
                "%sEncryptedCredentialsDirectory: %s\n"
1051
                "%sConfirmSpawn: %s\n"
1052
                "%sShallConfirmSpawn: %s\n"
1053
                "%sWatchdogUSec: " USEC_FMT "\n"
1054
                "%sNotifySocket: %s\n"
1055
                "%sDebugInvocation: %s\n"
1056
                "%sFallbackSmackProcessLabel: %s\n",
1057
                prefix, runtime_scope_to_string(p->runtime_scope),
×
1058
                prefix, p->flags,
×
1059
                prefix, yes_no(p->selinux_context_net),
×
1060
                prefix, p->cgroup_supported,
×
1061
                prefix, p->cgroup_path,
×
1062
                prefix, strempty(p->received_credentials_directory),
×
1063
                prefix, strempty(p->received_encrypted_credentials_directory),
×
1064
                prefix, strempty(p->confirm_spawn),
×
1065
                prefix, yes_no(p->shall_confirm_spawn),
×
1066
                prefix, p->watchdog_usec,
×
1067
                prefix, strempty(p->notify_socket),
×
1068
                prefix, yes_no(p->debug_invocation),
×
1069
                prefix, strempty(p->fallback_smack_process_label));
×
1070

1071
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1072
        strv_dump(f, prefix, "Environment", p->environment);
×
1073
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1074

1075
        LIST_FOREACH(open_files, file, p->open_files)
×
1076
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1077

1078
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1079
}
×
1080

1081
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
228✔
1082
        int r;
228✔
1083

1084
        assert(c);
228✔
1085
        assert(f);
228✔
1086

1087
        prefix = strempty(prefix);
228✔
1088

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

1147
        if (c->private_bpf == PRIVATE_BPF_YES) {
228✔
1148
                _cleanup_free_ char
×
1149
                        *commands = bpf_delegate_commands_to_string(c->bpf_delegate_commands),
×
1150
                        *maps = bpf_delegate_maps_to_string(c->bpf_delegate_maps),
×
1151
                        *programs = bpf_delegate_programs_to_string(c->bpf_delegate_programs),
×
1152
                        *attachments = bpf_delegate_attachments_to_string(c->bpf_delegate_attachments);
×
1153

1154
                fprintf(f, "%sBPFDelegateCommands: %s\n", prefix, strna(commands));
×
1155
                fprintf(f, "%sBPFDelegateMaps: %s\n", prefix, strna(maps));
×
1156
                fprintf(f, "%sBPFDelegatePrograms: %s\n", prefix, strna(programs));
×
1157
                fprintf(f, "%sBPFDelegateAttachments: %s\n", prefix, strna(attachments));
×
1158
        }
1159

1160
        if (c->set_login_environment >= 0)
228✔
1161
                fprintf(f, "%sSetLoginEnvironment: %s\n", prefix, yes_no(c->set_login_environment > 0));
2✔
1162

1163
        if (c->root_image)
228✔
1164
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1165

1166
        if (c->root_image_options) {
228✔
1167
                fprintf(f, "%sRootImageOptions:", prefix);
×
1168
                LIST_FOREACH(mount_options, o, c->root_image_options)
×
1169
                        if (!isempty(o->options))
×
1170
                                fprintf(f, " %s:%s",
×
1171
                                        partition_designator_to_string(o->partition_designator),
1172
                                        o->options);
1173
                fprintf(f, "\n");
×
1174
        }
1175

1176
        if (c->root_hash) {
228✔
1177
                _cleanup_free_ char *encoded = NULL;
×
1178
                encoded = hexmem(c->root_hash, c->root_hash_size);
×
1179
                if (encoded)
×
1180
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1181
        }
1182

1183
        if (c->root_hash_path)
228✔
1184
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1185

1186
        if (c->root_hash_sig) {
228✔
1187
                _cleanup_free_ char *encoded = NULL;
×
1188
                ssize_t len;
×
1189
                len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded);
×
1190
                if (len)
×
1191
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1192
        }
1193

1194
        if (c->root_hash_sig_path)
228✔
1195
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1196

1197
        if (c->root_verity)
228✔
1198
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1199

1200
        STRV_FOREACH(e, c->environment)
228✔
1201
                fprintf(f, "%sEnvironment: %s\n", prefix, *e);
×
1202

1203
        STRV_FOREACH(e, c->environment_files)
228✔
1204
                fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
×
1205

1206
        STRV_FOREACH(e, c->pass_environment)
238✔
1207
                fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
10✔
1208

1209
        STRV_FOREACH(e, c->unset_environment)
228✔
1210
                fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
×
1211

1212
        fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
228✔
1213

1214
        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
1,368✔
1215
                fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode);
1,140✔
1216

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

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

1230
        fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC));
228✔
1231

1232
        if (c->memory_ksm >= 0)
228✔
1233
                fprintf(f, "%sMemoryKSM: %s\n", prefix, yes_no(c->memory_ksm > 0));
2✔
1234

1235
        if (c->nice_set)
228✔
1236
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
×
1237

1238
        if (c->oom_score_adjust_set)
228✔
1239
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
10✔
1240

1241
        if (c->coredump_filter_set)
228✔
1242
                fprintf(f, "%sCoredumpFilter: 0x%"PRIx64"\n", prefix, c->coredump_filter);
×
1243

1244
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
3,876✔
1245
                if (c->rlimit[i]) {
3,648✔
1246
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
20✔
1247
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1248
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
20✔
1249
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
20✔
1250
                }
1251

1252
        if (c->ioprio_is_set) {
228✔
1253
                _cleanup_free_ char *class_str = NULL;
×
1254

1255
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
×
1256
                if (r >= 0)
×
1257
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
×
1258

1259
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
×
1260
        }
1261

1262
        if (c->cpu_sched_set) {
228✔
1263
                _cleanup_free_ char *policy_str = NULL;
×
1264

1265
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1266
                if (r >= 0)
×
1267
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1268

1269
                fprintf(f,
×
1270
                        "%sCPUSchedulingPriority: %i\n"
1271
                        "%sCPUSchedulingResetOnFork: %s\n",
1272
                        prefix, c->cpu_sched_priority,
×
1273
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1274
        }
1275

1276
        if (c->cpu_set.set) {
228✔
1277
                _cleanup_free_ char *affinity = NULL;
×
1278

1279
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1280
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1281
        }
1282

1283
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
228✔
1284
                _cleanup_free_ char *nodes = NULL;
1✔
1285

1286
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1287
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1288
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1289
        }
1290

1291
        if (c->timer_slack_nsec != NSEC_INFINITY)
228✔
1292
                fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
1✔
1293

1294
        fprintf(f,
228✔
1295
                "%sStandardInput: %s\n"
1296
                "%sStandardOutput: %s\n"
1297
                "%sStandardError: %s\n",
1298
                prefix, exec_input_to_string(c->std_input),
228✔
1299
                prefix, exec_output_to_string(c->std_output),
228✔
1300
                prefix, exec_output_to_string(c->std_error));
228✔
1301

1302
        if (c->std_input == EXEC_INPUT_NAMED_FD)
228✔
1303
                fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
×
1304
        if (c->std_output == EXEC_OUTPUT_NAMED_FD)
228✔
1305
                fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
×
1306
        if (c->std_error == EXEC_OUTPUT_NAMED_FD)
228✔
1307
                fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
×
1308

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

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

1339
        if (IN_SET(c->std_output,
228✔
1340
                   EXEC_OUTPUT_KMSG,
1341
                   EXEC_OUTPUT_JOURNAL,
1342
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1343
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1344
            IN_SET(c->std_error,
17✔
1345
                   EXEC_OUTPUT_KMSG,
1346
                   EXEC_OUTPUT_JOURNAL,
1347
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1348
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1349

1350
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
211✔
1351

1352
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
211✔
1353
                if (r >= 0)
211✔
1354
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
211✔
1355

1356
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
211✔
1357
                if (r >= 0)
211✔
1358
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
211✔
1359
        }
1360

1361
        if (c->log_level_max >= 0) {
228✔
1362
                _cleanup_free_ char *t = NULL;
1✔
1363

1364
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1365

1366
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1367
        }
1368

1369
        if (c->log_ratelimit.interval > 0)
228✔
1370
                fprintf(f,
×
1371
                        "%sLogRateLimitIntervalSec: %s\n",
1372
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1373

1374
        if (c->log_ratelimit.burst > 0)
228✔
1375
                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit.burst);
×
1376

1377
        if (!set_isempty(c->log_filter_allowed_patterns) || !set_isempty(c->log_filter_denied_patterns)) {
228✔
1378
                fprintf(f, "%sLogFilterPatterns:", prefix);
×
1379

1380
                char *pattern;
×
1381
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1382
                        fprintf(f, " %s", pattern);
×
1383
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1384
                        fprintf(f, " ~%s", pattern);
×
1385
                fputc('\n', f);
×
1386
        }
1387

1388
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
232✔
1389
                fprintf(f, "%sLogExtraFields: ", prefix);
4✔
1390
                fwrite(field->iov_base, 1, field->iov_len, f);
4✔
1391
                fputc('\n', f);
4✔
1392
        }
1393

1394
        if (c->log_namespace)
228✔
1395
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1396

1397
        if (c->secure_bits) {
228✔
1398
                _cleanup_free_ char *str = NULL;
×
1399

1400
                r = secure_bits_to_string_alloc(c->secure_bits, &str);
×
1401
                if (r >= 0)
×
1402
                        fprintf(f, "%sSecure Bits: %s\n", prefix, str);
×
1403
        }
1404

1405
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
228✔
1406
                _cleanup_free_ char *str = NULL;
16✔
1407

1408
                r = capability_set_to_string(c->capability_bounding_set, &str);
16✔
1409
                if (r >= 0)
16✔
1410
                        fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
16✔
1411
        }
1412

1413
        if (c->capability_ambient_set != 0) {
228✔
1414
                _cleanup_free_ char *str = NULL;
×
1415

1416
                r = capability_set_to_string(c->capability_ambient_set, &str);
×
1417
                if (r >= 0)
×
1418
                        fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
×
1419
        }
1420

1421
        if (c->user)
228✔
1422
                fprintf(f, "%sUser: %s\n", prefix, c->user);
1✔
1423
        if (c->group)
228✔
1424
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1425

1426
        fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
455✔
1427

1428
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
228✔
1429

1430
        if (c->pam_name)
228✔
1431
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
×
1432

1433
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
228✔
1434
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
228✔
1435
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
228✔
1436
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
228✔
1437
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
228✔
1438
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
228✔
1439

1440
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
232✔
1441
                fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
4✔
1442
                        mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
4✔
1443
                        mount->ignore_enoent ? "-": "",
4✔
1444
                        mount->source,
1445
                        mount->destination,
1446
                        mount->recursive ? "rbind" : "norbind");
4✔
1447

1448
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
228✔
1449
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1450
                        tmpfs->path,
1451
                        isempty(tmpfs->options) ? "" : ":",
×
1452
                        strempty(tmpfs->options));
×
1453

1454
        if (c->utmp_id)
228✔
1455
                fprintf(f,
×
1456
                        "%sUtmpIdentifier: %s\n",
1457
                        prefix, c->utmp_id);
1458

1459
        if (c->selinux_context)
228✔
1460
                fprintf(f,
×
1461
                        "%sSELinuxContext: %s%s\n",
1462
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1463

1464
        if (c->apparmor_profile)
228✔
1465
                fprintf(f,
×
1466
                        "%sAppArmorProfile: %s%s\n",
1467
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1468

1469
        if (c->smack_process_label)
228✔
1470
                fprintf(f,
×
1471
                        "%sSmackProcessLabel: %s%s\n",
1472
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1473

1474
        if (c->personality != PERSONALITY_INVALID)
228✔
1475
                fprintf(f,
1✔
1476
                        "%sPersonality: %s\n",
1477
                        prefix, strna(personality_to_string(c->personality)));
1478

1479
        fprintf(f,
228✔
1480
                "%sLockPersonality: %s\n",
1481
                prefix, yes_no(c->lock_personality));
228✔
1482

1483
        if (c->syscall_filter) {
228✔
1484
                fprintf(f,
11✔
1485
                        "%sSystemCallFilter: ",
1486
                        prefix);
1487

1488
                if (!c->syscall_allow_list)
11✔
1489
                        fputc('~', f);
×
1490

1491
#if HAVE_SECCOMP
1492
                if (dlopen_libseccomp() >= 0) {
11✔
1493
                        void *id, *val;
11✔
1494
                        bool first = true;
11✔
1495
                        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
4,279✔
1496
                                _cleanup_free_ char *name = NULL;
4,268✔
1497
                                const char *errno_name = NULL;
4,268✔
1498
                                int num = PTR_TO_INT(val);
4,268✔
1499

1500
                                if (first)
4,268✔
1501
                                        first = false;
1502
                                else
1503
                                        fputc(' ', f);
4,257✔
1504

1505
                                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
4,268✔
1506
                                fputs(strna(name), f);
4,268✔
1507

1508
                                if (num >= 0) {
4,268✔
1509
                                        errno_name = seccomp_errno_or_action_to_string(num);
×
1510
                                        if (errno_name)
×
1511
                                                fprintf(f, ":%s", errno_name);
×
1512
                                        else
1513
                                                fprintf(f, ":%d", num);
×
1514
                                }
1515
                        }
1516
                }
1517
#endif
1518

1519
                fputc('\n', f);
11✔
1520
        }
1521

1522
        if (c->syscall_archs) {
228✔
1523
                fprintf(f,
11✔
1524
                        "%sSystemCallArchitectures:",
1525
                        prefix);
1526

1527
#if HAVE_SECCOMP
1528
                void *id;
11✔
1529
                SET_FOREACH(id, c->syscall_archs)
22✔
1530
                        fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
11✔
1531
#endif
1532
                fputc('\n', f);
11✔
1533
        }
1534

1535
        if (exec_context_restrict_namespaces_set(c)) {
228✔
1536
                _cleanup_free_ char *s = NULL;
12✔
1537

1538
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
12✔
1539
                if (r >= 0)
12✔
1540
                        fprintf(f, "%sRestrictNamespaces: %s\n",
24✔
1541
                                prefix, strna(s));
1542
        }
1543

1544
#if HAVE_LIBBPF
1545
        if (exec_context_restrict_filesystems_set(c)) {
228✔
1546
                char *fs;
×
1547
                SET_FOREACH(fs, c->restrict_filesystems)
×
1548
                        fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
×
1549
        }
1550
#endif
1551

1552
        if (c->network_namespace_path)
228✔
1553
                fprintf(f,
×
1554
                        "%sNetworkNamespacePath: %s\n",
1555
                        prefix, c->network_namespace_path);
1556

1557
        if (c->syscall_errno > 0) {
228✔
1558
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
227✔
1559

1560
#if HAVE_SECCOMP
1561
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
227✔
1562
                if (errno_name)
227✔
1563
                        fputs(errno_name, f);
227✔
1564
                else
1565
                        fprintf(f, "%d", c->syscall_errno);
×
1566
#endif
1567
                fputc('\n', f);
227✔
1568
        }
1569

1570
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
228✔
1571
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1572
                        mount->ignore_enoent ? "-": "",
×
1573
                        mount->source,
1574
                        mount->destination);
1575
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1576
                        fprintf(f, ":%s:%s",
×
1577
                                partition_designator_to_string(o->partition_designator),
1578
                                strempty(o->options));
×
1579
                fprintf(f, "\n");
×
1580
        }
1581

1582
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
228✔
1583
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1584
                        mount->ignore_enoent ? "-": "",
×
1585
                        mount->source);
1586
                LIST_FOREACH(mount_options, o, mount->mount_options)
×
1587
                        fprintf(f, ":%s:%s",
×
1588
                                partition_designator_to_string(o->partition_designator),
1589
                                strempty(o->options));
×
1590
                fprintf(f, "\n");
×
1591
        }
1592

1593
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
228✔
1594
}
228✔
1595

1596
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1597
        assert(c);
×
1598

1599
        /* Returns true if the process forked off would run under
1600
         * an unchanged UID or as root. */
1601

1602
        if (!c->user)
×
1603
                return true;
1604

1605
        if (STR_IN_SET(c->user, "root", "0"))
×
1606
                return true;
×
1607

1608
        return false;
×
1609
}
1610

1611
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,392✔
1612
        int p;
2,392✔
1613

1614
        assert(c);
2,392✔
1615

1616
        if (c->ioprio_is_set)
2,392✔
1617
                return c->ioprio;
12✔
1618

1619
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,380✔
1620
        if (p < 0)
2,380✔
1621
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1622

1623
        return ioprio_normalize(p);
2,380✔
1624
}
1625

1626
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
34,529✔
1627
        assert(c);
34,529✔
1628

1629
        /* Explicit setting wins */
1630
        if (c->mount_apivfs >= 0)
34,529✔
1631
                return c->mount_apivfs > 0;
110✔
1632

1633
        /* Default to "yes" if root directory or image are specified */
1634
        if (exec_context_with_rootfs(c))
34,419✔
1635
                return true;
78✔
1636

1637
        return false;
1638
}
1639

1640
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
29,172✔
1641
        assert(c);
29,172✔
1642

1643
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1644
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1645
        if (c->log_namespace)
29,172✔
1646
                return true;
1647

1648
        if (c->bind_log_sockets >= 0)
29,164✔
1649
                return c->bind_log_sockets > 0;
2✔
1650

1651
        if (exec_context_get_effective_mount_apivfs(c))
29,162✔
1652
                return true;
1653

1654
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1655
        if (exec_context_with_rootfs(c) && c->private_devices)
29,089✔
1656
                return true;
×
1657

1658
        return false;
1659
}
1660

1661
void exec_context_free_log_extra_fields(ExecContext *c) {
47,586✔
1662
        assert(c);
47,586✔
1663

1664
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
47,591✔
1665
                free(field->iov_base);
5✔
1666

1667
        c->log_extra_fields = mfree(c->log_extra_fields);
47,586✔
1668
        c->n_log_extra_fields = 0;
47,586✔
1669
}
47,586✔
1670

1671
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,389✔
1672
        _cleanup_close_ int fd = -EBADF;
1,389✔
1673
        const char *path;
1,389✔
1674
        struct stat st;
1,389✔
1675
        int r;
1,389✔
1676

1677
        assert(c);
1,389✔
1678

1679
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1680
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,389✔
1681

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

1688
        path = exec_context_tty_path(c);
38✔
1689
        if (!path)
38✔
1690
                return;
1691

1692
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
38✔
1693
        if (fd < 0)
38✔
1694
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1695
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1696
                                             path);
1697

1698
        if (fstat(fd, &st) < 0)
38✔
1699
                return (void) log_warning_errno(errno, "Failed to stat TTY '%s', ignoring: %m", path);
×
1700

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

1709
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
38✔
1710
        if (r < 0)
38✔
1711
                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);
38✔
1712
}
1713

1714
int exec_context_get_clean_directories(
×
1715
                ExecContext *c,
1716
                char **prefix,
1717
                ExecCleanMask mask,
1718
                char ***ret) {
1719

1720
        _cleanup_strv_free_ char **l = NULL;
×
1721
        int r;
×
1722

1723
        assert(c);
×
1724
        assert(prefix);
×
1725
        assert(ret);
×
1726

1727
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
1728
                if (!BIT_SET(mask, t))
×
1729
                        continue;
×
1730

1731
                if (!prefix[t])
×
1732
                        continue;
×
1733

1734
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
×
1735
                        char *j;
×
1736

1737
                        j = path_join(prefix[t], i->path);
×
1738
                        if (!j)
×
1739
                                return -ENOMEM;
1740

1741
                        r = strv_consume(&l, j);
×
1742
                        if (r < 0)
×
1743
                                return r;
1744

1745
                        /* Also remove private directories unconditionally. */
1746
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
×
1747
                                j = path_join(prefix[t], "private", i->path);
×
1748
                                if (!j)
×
1749
                                        return -ENOMEM;
1750

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

1756
                        STRV_FOREACH(symlink, i->symlinks) {
×
1757
                                j = path_join(prefix[t], *symlink);
×
1758
                                if (!j)
×
1759
                                        return -ENOMEM;
1760

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

1768
        *ret = TAKE_PTR(l);
×
1769
        return 0;
×
1770
}
1771

1772
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,224✔
1773
        ExecCleanMask mask = 0;
1,224✔
1774

1775
        assert(c);
1,224✔
1776
        assert(ret);
1,224✔
1777

1778
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
7,344✔
1779
                if (c->directories[t].n_items > 0)
6,120✔
1780
                        mask |= 1U << t;
224✔
1781

1782
        *ret = mask;
1,224✔
1783
        return 0;
1,224✔
1784
}
1785

1786
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,196✔
1787
        int n = 0, r;
1,196✔
1788

1789
        assert(c);
1,196✔
1790

1791
        if (c->oom_score_adjust_set)
1,196✔
1792
                return c->oom_score_adjust;
409✔
1793

1794
        r = get_oom_score_adjust(&n);
787✔
1795
        if (r < 0)
787✔
1796
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1797

1798
        return n;
787✔
1799
}
1800

1801
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1,196✔
1802
        _cleanup_free_ char *t = NULL;
1,196✔
1803
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1,196✔
1804
        int r;
1,196✔
1805

1806
        assert(c);
1,196✔
1807

1808
        if (c->coredump_filter_set)
1,196✔
1809
                return c->coredump_filter;
×
1810

1811
        r = read_one_line_file("/proc/self/coredump_filter", &t);
1,196✔
1812
        if (r < 0)
1,196✔
1813
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1814
        else {
1815
                r = safe_atoux64(t, &n);
1,196✔
1816
                if (r < 0)
1,196✔
1817
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1818
        }
1819

1820
        return n;
1,196✔
1821
}
1822

1823
int exec_context_get_nice(const ExecContext *c) {
1,196✔
1824
        int n;
1,196✔
1825

1826
        assert(c);
1,196✔
1827

1828
        if (c->nice_set)
1,196✔
1829
                return c->nice;
4✔
1830

1831
        errno = 0;
1,192✔
1832
        n = getpriority(PRIO_PROCESS, 0);
1,192✔
1833
        if (errno > 0) {
1,192✔
1834
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1835
                n = 0;
1836
        }
1837

1838
        return n;
1839
}
1840

1841
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,196✔
1842
        int n;
1,196✔
1843

1844
        assert(c);
1,196✔
1845

1846
        if (c->cpu_sched_set)
1,196✔
1847
                return c->cpu_sched_policy;
×
1848

1849
        n = sched_getscheduler(0);
1,196✔
1850
        if (n < 0)
1,196✔
1851
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1852

1853
        return n < 0 ? SCHED_OTHER : n;
1,196✔
1854
}
1855

1856
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,196✔
1857
        struct sched_param p = {};
1,196✔
1858
        int r;
1,196✔
1859

1860
        assert(c);
1,196✔
1861

1862
        if (c->cpu_sched_set)
1,196✔
1863
                return c->cpu_sched_priority;
×
1864

1865
        r = sched_getparam(0, &p);
1,196✔
1866
        if (r < 0)
1,196✔
1867
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1868

1869
        return r >= 0 ? p.sched_priority : 0;
1,196✔
1870
}
1871

1872
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,196✔
1873
        int r;
1,196✔
1874

1875
        assert(c);
1,196✔
1876

1877
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,196✔
1878
                return c->timer_slack_nsec;
1879

1880
        r = prctl(PR_GET_TIMERSLACK);
1,196✔
1881
        if (r < 0)
1,196✔
1882
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1883

1884
        return (uint64_t) MAX(r, 0);
1,196✔
1885
}
1886

1887
bool exec_context_get_set_login_environment(const ExecContext *c) {
11,045✔
1888
        assert(c);
11,045✔
1889

1890
        if (c->set_login_environment >= 0)
11,045✔
1891
                return c->set_login_environment;
×
1892

1893
        return c->user || c->dynamic_user || c->pam_name;
19,915✔
1894
}
1895

1896
char** exec_context_get_syscall_filter(const ExecContext *c) {
1,196✔
1897
        _cleanup_strv_free_ char **l = NULL;
1,196✔
1898

1899
        assert(c);
1,196✔
1900

1901
#if HAVE_SECCOMP
1902
        if (dlopen_libseccomp() < 0)
1,196✔
1903
                return strv_new(NULL);
×
1904

1905
        void *id, *val;
1,196✔
1906
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
12,122✔
1907
                _cleanup_free_ char *name = NULL;
10,926✔
1908
                const char *e = NULL;
10,926✔
1909
                char *s;
10,926✔
1910
                int num = PTR_TO_INT(val);
10,926✔
1911

1912
                if (c->syscall_allow_list && num >= 0)
10,926✔
1913
                        /* syscall with num >= 0 in allow-list is denied. */
1914
                        continue;
×
1915

1916
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
10,926✔
1917
                if (!name)
10,926✔
1918
                        continue;
×
1919

1920
                if (num >= 0) {
10,926✔
1921
                        e = seccomp_errno_or_action_to_string(num);
×
1922
                        if (e) {
×
1923
                                s = strjoin(name, ":", e);
×
1924
                                if (!s)
×
1925
                                        return NULL;
1926
                        } else {
1927
                                if (asprintf(&s, "%s:%d", name, num) < 0)
×
1928
                                        return NULL;
1929
                        }
1930
                } else
1931
                        s = TAKE_PTR(name);
10,926✔
1932

1933
                if (strv_consume(&l, s) < 0)
10,926✔
1934
                        return NULL;
1935
        }
1936

1937
        strv_sort(l);
1,196✔
1938
#endif
1939

1940
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,196✔
1941
}
1942

1943
char** exec_context_get_syscall_archs(const ExecContext *c) {
1,196✔
1944
        _cleanup_strv_free_ char **l = NULL;
1,196✔
1945

1946
        assert(c);
1,196✔
1947

1948
#if HAVE_SECCOMP
1949
        void *id;
1,196✔
1950
        SET_FOREACH(id, c->syscall_archs) {
1,228✔
1951
                const char *name;
32✔
1952

1953
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
32✔
1954
                if (!name)
32✔
1955
                        continue;
×
1956

1957
                if (strv_extend(&l, name) < 0)
32✔
1958
                        return NULL;
×
1959
        }
1960

1961
        strv_sort(l);
1,196✔
1962
#endif
1963

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

1967
char** exec_context_get_syscall_log(const ExecContext *c) {
1,196✔
1968
        _cleanup_strv_free_ char **l = NULL;
1,196✔
1969

1970
        assert(c);
1,196✔
1971

1972
#if HAVE_SECCOMP
1973
        if (dlopen_libseccomp() < 0)
1,196✔
1974
                return strv_new(NULL);
×
1975

1976
        void *id, *val;
1,196✔
1977
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,196✔
1978
                char *name = NULL;
×
1979

1980
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1981
                if (!name)
×
1982
                        continue;
×
1983

1984
                if (strv_consume(&l, name) < 0)
×
1985
                        return NULL;
×
1986
        }
1987

1988
        strv_sort(l);
1,196✔
1989
#endif
1990

1991
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,196✔
1992
}
1993

1994
char** exec_context_get_address_families(const ExecContext *c) {
1,196✔
1995
        _cleanup_strv_free_ char **l = NULL;
1,196✔
1996
        void *af;
1,196✔
1997

1998
        assert(c);
1,196✔
1999

2000
        SET_FOREACH(af, c->address_families) {
1,290✔
2001
                const char *name;
94✔
2002

2003
                name = af_to_name(PTR_TO_INT(af));
94✔
2004
                if (!name)
94✔
2005
                        continue;
×
2006

2007
                if (strv_extend(&l, name) < 0)
94✔
2008
                        return NULL;
×
2009
        }
2010

2011
        strv_sort(l);
1,196✔
2012

2013
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,196✔
2014
}
2015

2016
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1,196✔
2017
        assert(c);
1,196✔
2018

2019
#if HAVE_LIBBPF
2020
        char **l = set_get_strv(c->restrict_filesystems);
1,196✔
2021
        if (!l)
1,196✔
2022
                return NULL;
2023

2024
        return strv_sort(l);
1,196✔
2025
#else
2026
        return strv_new(NULL);
2027
#endif
2028
}
2029

2030
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
13,020✔
2031
        assert(c);
13,020✔
2032

2033
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
13,020✔
2034
}
2035

2036
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
14,285✔
2037
        assert(c);
14,285✔
2038

2039
        return c->restrict_filesystems_allow_list ||
14,285✔
2040
          !set_isempty(c->restrict_filesystems);
14,285✔
2041
}
2042

2043
bool exec_context_with_rootfs(const ExecContext *c) {
63,763✔
2044
        assert(c);
63,763✔
2045

2046
        /* Checks if RootDirectory= or RootImage= are used */
2047

2048
        return !empty_or_root(c->root_directory) || c->root_image;
63,763✔
2049
}
2050

2051
int exec_context_has_vpicked_extensions(const ExecContext *context) {
4✔
2052
        int r;
4✔
2053

2054
        assert(context);
4✔
2055

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

2067
        return 0;
2068
}
2069

2070
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
4,198✔
2071
        assert(s);
4,198✔
2072

2073
        *s = (ExecStatus) {
4,198✔
2074
                .pid = pid,
2075
        };
2076

2077
        if (ts)
4,198✔
2078
                s->start_timestamp = *ts;
4,198✔
2079
        else
2080
                dual_timestamp_now(&s->start_timestamp);
×
2081
}
4,198✔
2082

2083
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
1,949✔
2084
        assert(s);
1,949✔
2085

2086
        if (s->pid != pid)
1,949✔
2087
                *s = (ExecStatus) {
4✔
2088
                        .pid = pid,
2089
                };
2090

2091
        dual_timestamp_now(&s->exit_timestamp);
1,949✔
2092

2093
        s->code = code;
1,949✔
2094
        s->status = status;
1,949✔
2095

2096
        if (context && context->utmp_id)
1,949✔
2097
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2098
}
1,949✔
2099

2100
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
7,312✔
2101
        assert(s);
7,312✔
2102
        assert(ucred);
7,312✔
2103
        assert(ts);
7,312✔
2104

2105
        if (ucred->pid != s->pid)
7,312✔
2106
                *s = (ExecStatus) {
7✔
2107
                        .pid = ucred->pid,
2108
                };
2109

2110
        s->handoff_timestamp = *ts;
7,312✔
2111
}
7,312✔
2112

2113
void exec_status_reset(ExecStatus *s) {
25,814✔
2114
        assert(s);
25,814✔
2115

2116
        *s = (ExecStatus) {};
25,814✔
2117
}
25,814✔
2118

2119
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
98✔
2120
        assert(s);
98✔
2121
        assert(f);
98✔
2122

2123
        if (s->pid <= 0)
98✔
2124
                return;
2125

2126
        prefix = strempty(prefix);
10✔
2127

2128
        fprintf(f,
10✔
2129
                "%sPID: "PID_FMT"\n",
2130
                prefix, s->pid);
2131

2132
        if (dual_timestamp_is_set(&s->start_timestamp))
10✔
2133
                fprintf(f,
10✔
2134
                        "%sStart Timestamp: %s\n",
2135
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
10✔
2136

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

2148
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2149

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

2165
                fprintf(f,
×
2166
                        "%sExit Code: %s\n"
2167
                        "%sExit Status: %i\n",
2168
                        prefix, sigchld_code_to_string(s->code),
×
2169
                        prefix, s->status);
×
2170
        }
2171
}
2172

2173
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
98✔
2174
        _cleanup_free_ char *cmd = NULL;
196✔
2175
        const char *prefix2;
98✔
2176

2177
        assert(c);
98✔
2178
        assert(f);
98✔
2179

2180
        prefix = strempty(prefix);
98✔
2181
        prefix2 = strjoina(prefix, "\t");
490✔
2182

2183
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
98✔
2184

2185
        fprintf(f,
98✔
2186
                "%sCommand Line: %s\n",
2187
                prefix, strnull(cmd));
2188

2189
        exec_status_dump(&c->exec_status, f, prefix2);
98✔
2190
}
98✔
2191

2192
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
95✔
2193
        assert(f);
95✔
2194

2195
        prefix = strempty(prefix);
95✔
2196

2197
        LIST_FOREACH(command, i, c)
193✔
2198
                exec_command_dump(i, f, prefix);
98✔
2199
}
95✔
2200

2201
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
15,949✔
2202
        ExecCommand *end;
15,949✔
2203

2204
        assert(l);
15,949✔
2205
        assert(e);
15,949✔
2206

2207
        if (*l) {
15,949✔
2208
                /* It's kind of important, that we keep the order here */
2209
                end = LIST_FIND_TAIL(command, *l);
363✔
2210
                LIST_INSERT_AFTER(command, *l, end, e);
126✔
2211
        } else
2212
                *l = e;
15,823✔
2213
}
15,949✔
2214

2215
int exec_command_set(ExecCommand *c, const char *path, ...) {
192✔
2216
        va_list ap;
192✔
2217
        char **l, *p;
192✔
2218

2219
        assert(c);
192✔
2220
        assert(path);
192✔
2221

2222
        va_start(ap, path);
192✔
2223
        l = strv_new_ap(path, ap);
192✔
2224
        va_end(ap);
192✔
2225

2226
        if (!l)
192✔
2227
                return -ENOMEM;
192✔
2228

2229
        p = strdup(path);
192✔
2230
        if (!p) {
192✔
2231
                strv_free(l);
×
2232
                return -ENOMEM;
×
2233
        }
2234

2235
        free_and_replace(c->path, p);
192✔
2236

2237
        return strv_free_and_replace(c->argv, l);
192✔
2238
}
2239

2240
int exec_command_append(ExecCommand *c, const char *path, ...) {
277✔
2241
        char **l;
277✔
2242
        va_list ap;
277✔
2243
        int r;
277✔
2244

2245
        assert(c);
277✔
2246
        assert(path);
277✔
2247

2248
        va_start(ap, path);
277✔
2249
        l = strv_new_ap(path, ap);
277✔
2250
        va_end(ap);
277✔
2251

2252
        if (!l)
277✔
2253
                return -ENOMEM;
277✔
2254

2255
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
277✔
2256
        if (r < 0)
277✔
2257
                return r;
×
2258

2259
        return 0;
2260
}
2261

2262
static char *destroy_tree(char *path) {
251✔
2263
        if (!path)
251✔
2264
                return NULL;
2265

2266
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
88✔
2267
                log_debug("Spawning process to nuke '%s'", path);
88✔
2268

2269
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
88✔
2270
        }
2271

2272
        return mfree(path);
88✔
2273
}
2274

2275
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
94,472✔
2276
        assert(rt);
94,472✔
2277

2278
        if (rt->manager)
94,472✔
2279
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
150✔
2280

2281
        rt->id = mfree(rt->id);
94,472✔
2282
        rt->tmp_dir = mfree(rt->tmp_dir);
94,472✔
2283
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
94,472✔
2284
        safe_close_pair(rt->netns_storage_socket);
94,472✔
2285
        safe_close_pair(rt->ipcns_storage_socket);
94,472✔
2286
}
94,472✔
2287

2288
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
94,444✔
2289
        if (!rt)
94,444✔
2290
                return NULL;
2291

2292
        exec_shared_runtime_done(rt);
94,444✔
2293
        return mfree(rt);
94,444✔
2294
}
2295

2296
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
151✔
2297
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
96,837✔
2298

2299
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
51✔
2300
        if (!rt)
51✔
2301
                return NULL;
2302

2303
        assert(rt->n_ref > 0);
50✔
2304
        rt->n_ref--;
50✔
2305

2306
        if (rt->n_ref > 0)
50✔
2307
                return NULL;
2308

2309
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
50✔
2310
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
50✔
2311

2312
        return exec_shared_runtime_free(rt);
50✔
2313
}
2314

2315
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
94,444✔
2316
        _cleanup_free_ char *id_copy = NULL;
188,888✔
2317
        ExecSharedRuntime *n;
94,444✔
2318

2319
        assert(ret);
94,444✔
2320

2321
        id_copy = strdup(id);
94,444✔
2322
        if (!id_copy)
94,444✔
2323
                return -ENOMEM;
2324

2325
        n = new(ExecSharedRuntime, 1);
94,444✔
2326
        if (!n)
94,444✔
2327
                return -ENOMEM;
2328

2329
        *n = (ExecSharedRuntime) {
94,444✔
2330
                .id = TAKE_PTR(id_copy),
94,444✔
2331
                .netns_storage_socket = EBADF_PAIR,
2332
                .ipcns_storage_socket = EBADF_PAIR,
2333
        };
2334

2335
        *ret = n;
94,444✔
2336
        return 0;
94,444✔
2337
}
2338

2339
static int exec_shared_runtime_add(
150✔
2340
                Manager *m,
2341
                const char *id,
2342
                char **tmp_dir,
2343
                char **var_tmp_dir,
2344
                int netns_storage_socket[2],
2345
                int ipcns_storage_socket[2],
2346
                ExecSharedRuntime **ret) {
2347

2348
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
150✔
2349
        int r;
150✔
2350

2351
        assert(m);
150✔
2352
        assert(id);
150✔
2353

2354
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2355

2356
        r = exec_shared_runtime_allocate(&rt, id);
150✔
2357
        if (r < 0)
150✔
2358
                return r;
2359

2360
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
150✔
2361
        if (r < 0)
150✔
2362
                return r;
2363

2364
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
150✔
2365
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
150✔
2366
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
150✔
2367

2368
        if (netns_storage_socket) {
150✔
2369
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
150✔
2370
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
150✔
2371
        }
2372

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

2378
        rt->manager = m;
150✔
2379

2380
        if (ret)
150✔
2381
                *ret = rt;
71✔
2382
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2383
        TAKE_PTR(rt);
150✔
2384
        return 0;
150✔
2385
}
2386

2387
static int exec_shared_runtime_make(
5,448✔
2388
                Manager *m,
2389
                const ExecContext *c,
2390
                const char *id,
2391
                ExecSharedRuntime **ret) {
2392

2393
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
5,448✔
2394
        _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
10,896✔
2395
        int r;
5,448✔
2396

2397
        assert(m);
5,448✔
2398
        assert(c);
5,448✔
2399
        assert(id);
5,448✔
2400

2401
        /* It is not necessary to create ExecSharedRuntime object. */
2402
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
5,448✔
2403
                *ret = NULL;
5,377✔
2404
                return 0;
5,377✔
2405
        }
2406

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

2416
        if (exec_needs_network_namespace(c))
71✔
2417
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
7✔
2418
                        return -errno;
×
2419

2420
        if (exec_needs_ipc_namespace(c))
71✔
2421
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
2422
                        return -errno;
×
2423

2424
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
71✔
2425
        if (r < 0)
71✔
2426
                return r;
×
2427

2428
        return 1;
2429
}
2430

2431
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
5,527✔
2432
        ExecSharedRuntime *rt;
5,527✔
2433
        int r;
5,527✔
2434

2435
        assert(m);
5,527✔
2436
        assert(id);
5,527✔
2437
        assert(ret);
5,527✔
2438

2439
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
5,527✔
2440
        if (rt)
5,527✔
2441
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2442
                goto ref;
79✔
2443

2444
        if (!create) {
5,448✔
2445
                *ret = NULL;
×
2446
                return 0;
×
2447
        }
2448

2449
        /* If not found, then create a new object. */
2450
        r = exec_shared_runtime_make(m, c, id, &rt);
5,448✔
2451
        if (r < 0)
5,448✔
2452
                return r;
2453
        if (r == 0) {
5,448✔
2454
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2455
                *ret = NULL;
5,377✔
2456
                return 0;
5,377✔
2457
        }
2458

2459
ref:
71✔
2460
        /* increment reference counter. */
2461
        rt->n_ref++;
150✔
2462
        *ret = rt;
150✔
2463
        return 1;
150✔
2464
}
2465

2466
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
63✔
2467
        ExecSharedRuntime *rt;
63✔
2468

2469
        assert(m);
63✔
2470
        assert(f);
63✔
2471
        assert(fds);
63✔
2472

2473
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
163✔
2474
                fprintf(f, "exec-runtime=%s", rt->id);
100✔
2475

2476
                if (rt->tmp_dir)
100✔
2477
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
100✔
2478

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

2482
                if (rt->netns_storage_socket[0] >= 0) {
100✔
2483
                        int copy;
2✔
2484

2485
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2✔
2486
                        if (copy < 0)
2✔
2487
                                return copy;
×
2488

2489
                        fprintf(f, " netns-socket-0=%i", copy);
2✔
2490
                }
2491

2492
                if (rt->netns_storage_socket[1] >= 0) {
100✔
2493
                        int copy;
2✔
2494

2495
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2✔
2496
                        if (copy < 0)
2✔
2497
                                return copy;
2498

2499
                        fprintf(f, " netns-socket-1=%i", copy);
2✔
2500
                }
2501

2502
                if (rt->ipcns_storage_socket[0] >= 0) {
100✔
2503
                        int copy;
×
2504

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

2509
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2510
                }
2511

2512
                if (rt->ipcns_storage_socket[1] >= 0) {
100✔
2513
                        int copy;
×
2514

2515
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2516
                        if (copy < 0)
×
2517
                                return copy;
2518

2519
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2520
                }
2521

2522
                fputc('\n', f);
100✔
2523
        }
2524

2525
        return 0;
63✔
2526
}
2527

2528
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
96,687✔
2529
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
96,687✔
2530
        ExecSharedRuntime *rt = NULL;
96,687✔
2531
        int r;
96,687✔
2532

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

2538
        assert(u);
96,687✔
2539
        assert(key);
96,687✔
2540
        assert(value);
96,687✔
2541

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

2549
        if (u->manager) {
96,687✔
2550
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
96,687✔
2551
                        return log_oom();
×
2552

2553
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
96,687✔
2554
        }
2555
        if (!rt) {
96,687✔
2556
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
94,294✔
2557
                        return log_oom();
×
2558

2559
                rt = rt_create;
94,294✔
2560
        }
2561

2562
        if (streq(key, "tmp-dir")) {
96,687✔
2563
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2564
                        return -ENOMEM;
2565

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

2570
        } else if (streq(key, "netns-socket-0")) {
96,687✔
2571

2572
                safe_close(rt->netns_storage_socket[0]);
×
2573
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2574
                if (rt->netns_storage_socket[0] < 0)
×
2575
                        return 0;
2576

2577
        } else if (streq(key, "netns-socket-1")) {
96,687✔
2578

2579
                safe_close(rt->netns_storage_socket[1]);
×
2580
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2581
                if (rt->netns_storage_socket[1] < 0)
×
2582
                        return 0;
2583
        } else
2584
                return 0;
2585

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

2594
                rt_create->manager = u->manager;
×
2595

2596
                /* Avoid cleanup */
2597
                TAKE_PTR(rt_create);
×
2598
        }
2599

2600
        return 1;
2601
}
2602

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

2610
        assert(m);
79✔
2611
        assert(fds);
79✔
2612

2613
        n = strcspn(v, " ");
79✔
2614
        id = strndupa_safe(v, n);
79✔
2615
        if (v[n] != ' ')
79✔
2616
                goto finalize;
×
2617
        p = v + n + 1;
79✔
2618

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

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

2641
        v = startswith(p, "netns-socket-0=");
1✔
2642
        if (v) {
1✔
2643
                char *buf;
1✔
2644

2645
                n = strcspn(v, " ");
1✔
2646
                buf = strndupa_safe(v, n);
1✔
2647

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

2656
        v = startswith(p, "netns-socket-1=");
1✔
2657
        if (v) {
1✔
2658
                char *buf;
1✔
2659

2660
                n = strcspn(v, " ");
1✔
2661
                buf = strndupa_safe(v, n);
1✔
2662

2663
                netns_fdpair[1] = deserialize_fd(fds, buf);
1✔
2664
                if (netns_fdpair[1] < 0)
1✔
2665
                        return netns_fdpair[1];
2666
                if (v[n] != ' ')
1✔
2667
                        goto finalize;
1✔
2668
                p = v + n + 1;
×
2669
        }
2670

2671
        v = startswith(p, "ipcns-socket-0=");
×
2672
        if (v) {
×
2673
                char *buf;
×
2674

2675
                n = strcspn(v, " ");
×
2676
                buf = strndupa_safe(v, n);
×
2677

2678
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2679
                if (ipcns_fdpair[0] < 0)
×
2680
                        return ipcns_fdpair[0];
2681
                if (v[n] != ' ')
×
2682
                        goto finalize;
×
2683
                p = v + n + 1;
×
2684
        }
2685

2686
        v = startswith(p, "ipcns-socket-1=");
×
2687
        if (v) {
×
2688
                char *buf;
×
2689

2690
                n = strcspn(v, " ");
×
2691
                buf = strndupa_safe(v, n);
×
2692

2693
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2694
                if (ipcns_fdpair[1] < 0)
×
2695
                        return ipcns_fdpair[1];
2696
        }
2697

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

2705
void exec_shared_runtime_vacuum(Manager *m) {
1,496✔
2706
        ExecSharedRuntime *rt;
1,496✔
2707

2708
        assert(m);
1,496✔
2709

2710
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2711

2712
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,575✔
2713
                if (rt->n_ref > 0)
79✔
2714
                        continue;
79✔
2715

2716
                (void) exec_shared_runtime_free(rt);
×
2717
        }
2718
}
1,496✔
2719

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

2731
        assert(unit);
5,527✔
2732
        assert(context);
5,527✔
2733
        assert(ret);
5,527✔
2734

2735
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
5,527✔
2736
                *ret = NULL;
5,376✔
2737
                return 0;
5,376✔
2738
        }
2739

2740
        if (exec_needs_ephemeral(context)) {
151✔
2741
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2742
                if (r < 0)
×
2743
                        return r;
2744

2745
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2746
                if (r < 0)
×
2747
                        return r;
2748

2749
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2750
                        return -errno;
×
2751
        }
2752

2753
        rt = new(ExecRuntime, 1);
151✔
2754
        if (!rt)
151✔
2755
                return -ENOMEM;
2756

2757
        *rt = (ExecRuntime) {
151✔
2758
                .shared = shared,
2759
                .dynamic_creds = creds,
2760
                .ephemeral_copy = TAKE_PTR(ephemeral),
151✔
2761
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
151✔
2762
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
151✔
2763
        };
2764

2765
        *ret = TAKE_PTR(rt);
151✔
2766
        return 1;
151✔
2767
}
2768

2769
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
47,607✔
2770
        if (!rt)
47,607✔
2771
                return NULL;
2772

2773
        exec_shared_runtime_unref(rt->shared);
151✔
2774
        dynamic_creds_unref(rt->dynamic_creds);
151✔
2775

2776
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
151✔
2777

2778
        safe_close_pair(rt->ephemeral_storage_socket);
151✔
2779
        return mfree(rt);
151✔
2780
}
2781

2782
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
5,573✔
2783
        if (!rt)
5,573✔
2784
                return NULL;
2785

2786
        rt->shared = exec_shared_runtime_destroy(rt->shared);
51✔
2787
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
51✔
2788
        return exec_runtime_free(rt);
51✔
2789
}
2790

2791
void exec_runtime_clear(ExecRuntime *rt) {
28✔
2792
        if (!rt)
28✔
2793
                return;
2794

2795
        safe_close_pair(rt->ephemeral_storage_socket);
28✔
2796
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
28✔
2797
}
2798

2799
void exec_params_shallow_clear(ExecParameters *p) {
2,145✔
2800
        if (!p)
2,145✔
2801
                return;
2802

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

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

2819
void exec_params_deep_clear(ExecParameters *p) {
28✔
2820
        if (!p)
28✔
2821
                return;
2822

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

2827
        close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
28✔
2828

2829
        p->cgroup_path = mfree(p->cgroup_path);
28✔
2830

2831
        if (p->prefix) {
28✔
2832
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
28✔
2833
                p->prefix = mfree(p->prefix);
28✔
2834
        }
2835

2836
        p->received_credentials_directory = mfree(p->received_credentials_directory);
28✔
2837
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
28✔
2838

2839
        if (p->idle_pipe) {
28✔
UNCOV
2840
                close_many_and_free(p->idle_pipe, 4);
×
UNCOV
2841
                p->idle_pipe = NULL;
×
2842
        }
2843

2844
        p->stdin_fd = safe_close(p->stdin_fd);
28✔
2845
        p->stdout_fd = safe_close(p->stdout_fd);
28✔
2846
        p->stderr_fd = safe_close(p->stderr_fd);
28✔
2847

2848
        p->notify_socket = mfree(p->notify_socket);
28✔
2849

2850
        open_file_free_many(&p->open_files);
28✔
2851

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

2854
        exec_params_shallow_clear(p);
28✔
2855
}
2856

2857
void exec_directory_done(ExecDirectory *d) {
237,920✔
2858
        if (!d)
237,920✔
2859
                return;
2860

2861
        FOREACH_ARRAY(i, d->items, d->n_items) {
239,415✔
2862
                free(i->path);
1,495✔
2863
                strv_free(i->symlinks);
1,495✔
2864
        }
2865

2866
        d->items = mfree(d->items);
237,920✔
2867
        d->n_items = 0;
237,920✔
2868
        d->mode = 0755;
237,920✔
2869
}
2870

2871
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
5,137✔
2872
        assert(d);
5,137✔
2873
        assert(path);
5,137✔
2874

2875
        FOREACH_ARRAY(i, d->items, d->n_items)
7,471✔
2876
                if (path_equal(i->path, path))
2,349✔
2877
                        return i;
2878

2879
        return NULL;
2880
}
2881

2882
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
5,137✔
2883
        _cleanup_strv_free_ char **s = NULL;
×
2884
        _cleanup_free_ char *p = NULL;
5,137✔
2885
        ExecDirectoryItem *existing;
5,137✔
2886
        int r;
5,137✔
2887

2888
        assert(d);
5,137✔
2889
        assert(path);
5,137✔
2890

2891
        existing = exec_directory_find(d, path);
5,137✔
2892
        if (existing) {
5,137✔
2893
                r = strv_extend(&existing->symlinks, symlink);
15✔
2894
                if (r < 0)
15✔
2895
                        return r;
2896

2897
                existing->flags |= flags;
15✔
2898

2899
                return 0; /* existing item is updated */
15✔
2900
        }
2901

2902
        p = strdup(path);
5,122✔
2903
        if (!p)
5,122✔
2904
                return -ENOMEM;
2905

2906
        if (symlink) {
5,122✔
2907
                s = strv_new(symlink);
6✔
2908
                if (!s)
6✔
2909
                        return -ENOMEM;
2910
        }
2911

2912
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
5,122✔
2913
                return -ENOMEM;
2914

2915
        d->items[d->n_items++] = (ExecDirectoryItem) {
5,122✔
2916
                .path = TAKE_PTR(p),
5,122✔
2917
                .symlinks = TAKE_PTR(s),
5,122✔
2918
                .flags = flags,
2919
        };
2920

2921
        return 1; /* new item is added */
5,122✔
2922
}
2923

2924
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
862✔
2925
        assert(a);
862✔
2926
        assert(b);
862✔
2927

2928
        return path_compare(a->path, b->path);
862✔
2929
}
2930

2931
void exec_directory_sort(ExecDirectory *d) {
130,541✔
2932
        assert(d);
130,541✔
2933

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

2939
        if (d->n_items <= 1)
130,541✔
2940
                return;
2941

2942
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
156✔
2943

2944
        for (size_t i = 1; i < d->n_items; i++)
675✔
2945
                for (size_t j = 0; j < i; j++)
1,724✔
2946
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,220✔
2947
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
2948
                                break;
15✔
2949
                        }
2950
}
2951

2952
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
2953
        ExecDirectoryType t;
×
2954

2955
        assert(s);
×
2956

2957
        if (streq(s, "all"))
×
2958
                return EXEC_CLEAN_ALL;
2959
        if (streq(s, "fdstore"))
×
2960
                return EXEC_CLEAN_FDSTORE;
2961

2962
        t = exec_resource_type_from_string(s);
×
2963
        if (t < 0)
×
2964
                return (ExecCleanMask) t;
2965

2966
        return 1U << t;
×
2967
}
2968

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

2980
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
15,951✔
2981

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

2997
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
32,116✔
2998

2999
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
3000
        [EXEC_UTMP_INIT]  = "init",
3001
        [EXEC_UTMP_LOGIN] = "login",
3002
        [EXEC_UTMP_USER]  = "user",
3003
};
3004

3005
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,238✔
3006

3007
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
3008
        [EXEC_PRESERVE_NO]      = "no",
3009
        [EXEC_PRESERVE_YES]     = "yes",
3010
        [EXEC_PRESERVE_RESTART] = "restart",
3011
};
3012

3013
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
17,192✔
3014

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

3024
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3025

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

3034
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3035

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

3047
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
228✔
3048

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

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