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

systemd / systemd / 17703769385

12 Sep 2025 11:20PM UTC coverage: 72.266% (+0.07%) from 72.201%
17703769385

push

github

bluca
TEST-55-OOMD: Verify that ExecStopPost= runs on oom-kill

302693 of 418862 relevant lines covered (72.27%)

1057187.17 hits per line

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

76.3
/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,077✔
69
        assert(context);
15,077✔
70

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

74
        if (context->tty_path)
14,146✔
75
                return context->tty_path;
688✔
76

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

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

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

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

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

96
        if (!tty_path)
870✔
97
                tty_path = exec_context_tty_path(context);
438✔
98

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

103
        /* Fill in data from kernel command line if anything is unspecified */
104
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
870✔
105
                (void) proc_cmdline_tty_size(
828✔
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,091✔
115
            exec_context_shall_ansi_seq_reset(context) &&
404✔
116
            isatty_safe(input_fd)) {
183✔
117
                r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
183✔
118
                if (r < 0)
183✔
119
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
183✔
120
        }
121

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

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

129
        assert(context);
13,348✔
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,348✔
137

138
        if (parameters && parameters->stdout_fd >= 0 && isatty_safe(parameters->stdout_fd))
13,348✔
139
                fd = parameters->stdout_fd;
21✔
140
        else if (path && exec_context_has_tty(context)) {
13,327✔
141
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
432✔
142
                if (fd < 0)
432✔
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();
453✔
151
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
453✔
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))
453✔
154
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
155
        else if (lock_fd < 0)
453✔
156
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
157

158
        if (context->tty_reset)
453✔
159
                (void) terminal_reset_defensive(
179✔
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));
176✔
163

164
        r = exec_context_apply_tty_size(context, fd, fd, path);
453✔
165
        if (r < 0)
453✔
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)) {
871✔
169
                sd_id128_t context_id;
14✔
170

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

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

185
        if (context->tty_vhangup)
453✔
186
                (void) terminal_vhangup_fd(fd);
171✔
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);
453✔
191

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

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

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

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

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

209
        return context->private_ipc || context->ipc_namespace_path;
53,331✔
210
}
211

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

216
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
84,795✔
217
        assert(context);
84,795✔
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,795✔
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,795✔
230
}
231

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

235
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
53,369✔
236
}
237

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

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

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

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

250
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
74,745✔
251
        assert(context);
74,745✔
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,745✔
255
                return false;
256

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

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

265
        assert(context);
32,859✔
266

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

270
        if (!strv_isempty(context->read_write_paths) ||
32,833✔
271
            !strv_isempty(context->read_only_paths) ||
30,682✔
272
            !strv_isempty(context->inaccessible_paths) ||
30,675✔
273
            !strv_isempty(context->exec_paths) ||
30,656✔
274
            !strv_isempty(context->no_exec_paths))
30,656✔
275
                return true;
276

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

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

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

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

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

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

295
        if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
30,401✔
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,642✔
299
                return true;
300

301
        if (context->private_devices ||
29,166✔
302
            context->private_mounts > 0 ||
28,795✔
303
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,324✔
304
            context->protect_system != PROTECT_SYSTEM_NO ||
28,307✔
305
            context->protect_home != PROTECT_HOME_NO ||
28,307✔
306
            context->protect_kernel_tunables ||
28,307✔
307
            context->protect_kernel_modules ||
28,307✔
308
            context->protect_kernel_logs ||
52,383✔
309
            exec_needs_cgroup_mount(context) ||
26,190✔
310
            context->protect_proc != PROTECT_PROC_DEFAULT ||
26,172✔
311
            context->proc_subset != PROC_SUBSET_ALL ||
26,106✔
312
            context->private_bpf != PRIVATE_BPF_NO ||
52,194✔
313
            exec_needs_ipc_namespace(context) ||
52,194✔
314
            exec_needs_pid_namespace(context, params))
26,097✔
315
                return true;
3,121✔
316

317
        if (context->root_directory) {
26,045✔
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 &&
26,040✔
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))
26,040✔
337
                return true;
338

339
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
155,896✔
340
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
135,037✔
341
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,175✔
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,829✔
348
        assert(context);
3,829✔
349
        assert(params);
3,829✔
350

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

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

357
        if (!context->root_directory && !context->root_image)
3,155✔
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,916✔
370
        assert(context);
23,916✔
371
        assert(params);
23,916✔
372

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

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

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

382
        if (!context->dynamic_user)
11,177✔
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,145✔
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,145✔
397
}
398

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

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

408
        assert(params);
12,656✔
409
        assert(c);
12,656✔
410
        assert(ret);
12,656✔
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,656✔
423
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
695✔
424
                        subgroup = ".control";
425
                else
426
                        subgroup = c->delegate_subgroup;
652✔
427
        }
428

429
        if (subgroup)
652✔
430
                p = path_join(prefix, subgroup);
695✔
431
        else
432
                p = strdup(strempty(prefix));
11,969✔
433
        if (!p)
12,656✔
434
                return -ENOMEM;
435

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

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

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

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

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

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

456
        log_unit_struct(unit, LOG_DEBUG,
2,115✔
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,115✔
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,115✔
474
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
475
        _cleanup_fclose_ FILE *f = NULL;
2,115✔
476
        int r;
2,115✔
477

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

490
        LOG_CONTEXT_PUSH_UNIT(unit);
4,230✔
491

492
        r = exec_context_load_environment(unit, context, &params->files_env);
2,115✔
493
        if (r < 0)
2,115✔
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,115✔
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,115✔
508
        if (exec_params_needs_control_subcgroup(params)) {
2,115✔
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,114✔
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,115✔
533
        if (r < 0)
2,115✔
534
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
535

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

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

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

548
        r = fd_cloexec(fileno(f), false);
2,115✔
549
        if (r < 0)
2,115✔
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,115✔
553
        if (r < 0)
2,115✔
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,115✔
560
        if (r < 0)
2,115✔
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,115✔
564
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,115✔
565

566
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,115✔
567
        dual_timestamp start_timestamp;
2,115✔
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,115✔
572
        if (r < 0)
2,115✔
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,115✔
578

579
        /* The executor binary is pinned, to avoid compatibility problems during upgrades. */
580
        r = posix_spawn_wrapper(
2,115✔
581
                        FORMAT_PROC_FD_PATH(unit->manager->executor_fd),
2,115✔
582
                        STRV_MAKE(unit->manager->executor_path,
2,115✔
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,115✔
592

593
        if (r == -EUCLEAN && cgtarget)
2,115✔
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,115✔
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,115✔
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,115✔
608
                       command->path, pidref.pid, r > 0 ? "via" : "without");
609

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

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

616
void exec_context_init(ExecContext *c) {
57,947✔
617
        assert(c);
57,947✔
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) {
57,947✔
623
                .umask = 0022,
624
                .ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO,
57,947✔
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)
347,682✔
650
                d->mode = 0755;
289,735✔
651

652
        numa_policy_reset(&c->numa_policy);
57,947✔
653

654
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
57,947✔
655
}
57,947✔
656

657
void exec_context_done(ExecContext *c) {
46,018✔
658
        assert(c);
46,018✔
659

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

665
        rlimit_free_all(c->rlimit);
46,018✔
666

667
        for (size_t l = 0; l < 3; l++) {
184,072✔
668
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
138,054✔
669
                c->stdio_file[l] = mfree(c->stdio_file[l]);
138,054✔
670
        }
671

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

690
        c->supplementary_groups = strv_free(c->supplementary_groups);
46,018✔
691

692
        c->pam_name = mfree(c->pam_name);
46,018✔
693

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

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

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

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

717
        c->restrict_filesystems = set_free(c->restrict_filesystems);
46,018✔
718

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

724
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
276,108✔
725
                exec_directory_done(d);
230,090✔
726

727
        c->log_level_max = -1;
46,018✔
728

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

733
        c->log_ratelimit = (RateLimit) {};
46,018✔
734

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

738
        c->network_namespace_path = mfree(c->network_namespace_path);
46,018✔
739
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
46,018✔
740

741
        c->log_namespace = mfree(c->log_namespace);
46,018✔
742

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

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

751
        c->private_hostname = mfree(c->private_hostname);
46,018✔
752
}
46,018✔
753

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

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

760
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
5,257✔
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,595✔
792
        _cleanup_free_ char *p = NULL;
11,595✔
793

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

797
        p = path_join("/run/systemd/propagate/", u->id);
2,148✔
798
        if (!p)
2,148✔
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,148✔
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) {
98,279✔
809
        assert(c);
98,279✔
810

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

815
void exec_command_done_array(ExecCommand *c, size_t n) {
27,520✔
816
        FOREACH_ARRAY(i, c, n)
110,078✔
817
                exec_command_done(i);
82,558✔
818
}
27,520✔
819

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

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

828
ExecCommand* exec_command_free_list(ExecCommand *c) {
118,149✔
829
        ExecCommand *i;
118,149✔
830

831
        while ((i = LIST_POP(command, c)))
133,842✔
832
                exec_command_free(i);
15,693✔
833

834
        return NULL;
118,149✔
835
}
836

837
void exec_command_free_array(ExecCommand **c, size_t n) {
18,470✔
838
        FOREACH_ARRAY(i, c, n)
136,604✔
839
                *i = exec_command_free_list(*i);
118,134✔
840
}
18,470✔
841

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

847
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
4,514✔
848
        FOREACH_ARRAY(i, c, n)
30,700✔
849
                LIST_FOREACH(command, z, *i)
28,235✔
850
                        exec_status_reset(&z->exec_status);
2,049✔
851
}
4,514✔
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,519✔
865
        assert(c);
39,519✔
866

867
        switch (fd_index) {
39,519✔
868

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

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

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

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

881
        case STDERR_FILENO:
13,173✔
882
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
13,173✔
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,115✔
893
        _cleanup_strv_free_ char **v = NULL;
2,115✔
894
        int r;
2,115✔
895

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

899
        STRV_FOREACH(i, c->environment_files) {
2,117✔
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,115✔
959

960
        return 0;
2,115✔
961
}
962

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

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

969
        tty = skip_dev_prefix(tty);
223✔
970

971
        /* trivial identity? */
972
        if (streq(tty, "console"))
223✔
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) {
22,413✔
983
        assert(ec);
22,413✔
984

985
        return ec->tty_reset ||
44,697✔
986
                ec->tty_vhangup ||
22,284✔
987
                ec->tty_vt_disallocate ||
22,284✔
988
                exec_input_is_terminal(ec->std_input) ||
22,284✔
989
                ec->std_output == EXEC_OUTPUT_TTY ||
44,617✔
990
                ec->std_error == EXEC_OUTPUT_TTY;
22,155✔
991
}
992

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

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

999
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,395✔
1000
        assert(c);
1,395✔
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,395✔
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");
541✔
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
                void *id, *val;
11✔
1493
                bool first = true;
11✔
1494
                HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
4,279✔
1495
                        _cleanup_free_ char *name = NULL;
4,268✔
1496
                        const char *errno_name = NULL;
4,268✔
1497
                        int num = PTR_TO_INT(val);
4,268✔
1498

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

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

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

1517
                fputc('\n', f);
11✔
1518
        }
1519

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

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

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

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

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

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

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

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

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

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

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

1594
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1595
        assert(c);
×
1596

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

1600
        if (!c->user)
×
1601
                return true;
1602

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

1606
        return false;
×
1607
}
1608

1609
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,430✔
1610
        int p;
2,430✔
1611

1612
        assert(c);
2,430✔
1613

1614
        if (c->ioprio_is_set)
2,430✔
1615
                return c->ioprio;
12✔
1616

1617
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,418✔
1618
        if (p < 0)
2,418✔
1619
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1620

1621
        return ioprio_normalize(p);
2,418✔
1622
}
1623

1624
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
34,843✔
1625
        assert(c);
34,843✔
1626

1627
        /* Explicit setting wins */
1628
        if (c->mount_apivfs >= 0)
34,843✔
1629
                return c->mount_apivfs > 0;
110✔
1630

1631
        /* Default to "yes" if root directory or image are specified */
1632
        if (exec_context_with_rootfs(c))
34,733✔
1633
                return true;
66✔
1634

1635
        return false;
1636
}
1637

1638
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
29,457✔
1639
        assert(c);
29,457✔
1640

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

1646
        if (c->bind_log_sockets >= 0)
29,449✔
1647
                return c->bind_log_sockets > 0;
2✔
1648

1649
        if (exec_context_get_effective_mount_apivfs(c))
29,447✔
1650
                return true;
1651

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

1656
        return false;
1657
}
1658

1659
void exec_context_free_log_extra_fields(ExecContext *c) {
46,020✔
1660
        assert(c);
46,020✔
1661

1662
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
46,025✔
1663
                free(field->iov_base);
5✔
1664

1665
        c->log_extra_fields = mfree(c->log_extra_fields);
46,020✔
1666
        c->n_log_extra_fields = 0;
46,020✔
1667
}
46,020✔
1668

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

1675
        assert(c);
1,391✔
1676

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

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

1686
        path = exec_context_tty_path(c);
35✔
1687
        if (!path)
35✔
1688
                return;
1689

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

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

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

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

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

1718
        _cleanup_strv_free_ char **l = NULL;
×
1719
        int r;
×
1720

1721
        assert(c);
×
1722
        assert(prefix);
×
1723
        assert(ret);
×
1724

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

1729
                if (!prefix[t])
×
1730
                        continue;
×
1731

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

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

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

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

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

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

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

1766
        *ret = TAKE_PTR(l);
×
1767
        return 0;
×
1768
}
1769

1770
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,241✔
1771
        ExecCleanMask mask = 0;
1,241✔
1772

1773
        assert(c);
1,241✔
1774
        assert(ret);
1,241✔
1775

1776
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
7,446✔
1777
                if (c->directories[t].n_items > 0)
6,205✔
1778
                        mask |= 1U << t;
302✔
1779

1780
        *ret = mask;
1,241✔
1781
        return 0;
1,241✔
1782
}
1783

1784
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,215✔
1785
        int n = 0, r;
1,215✔
1786

1787
        assert(c);
1,215✔
1788

1789
        if (c->oom_score_adjust_set)
1,215✔
1790
                return c->oom_score_adjust;
430✔
1791

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

1796
        return n;
785✔
1797
}
1798

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

1804
        assert(c);
1,215✔
1805

1806
        if (c->coredump_filter_set)
1,215✔
1807
                return c->coredump_filter;
×
1808

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

1818
        return n;
1,215✔
1819
}
1820

1821
int exec_context_get_nice(const ExecContext *c) {
1,215✔
1822
        int n;
1,215✔
1823

1824
        assert(c);
1,215✔
1825

1826
        if (c->nice_set)
1,215✔
1827
                return c->nice;
4✔
1828

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

1836
        return n;
1837
}
1838

1839
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,215✔
1840
        int n;
1,215✔
1841

1842
        assert(c);
1,215✔
1843

1844
        if (c->cpu_sched_set)
1,215✔
1845
                return c->cpu_sched_policy;
×
1846

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

1851
        return n < 0 ? SCHED_OTHER : n;
1,215✔
1852
}
1853

1854
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,215✔
1855
        struct sched_param p = {};
1,215✔
1856
        int r;
1,215✔
1857

1858
        assert(c);
1,215✔
1859

1860
        if (c->cpu_sched_set)
1,215✔
1861
                return c->cpu_sched_priority;
×
1862

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

1867
        return r >= 0 ? p.sched_priority : 0;
1,215✔
1868
}
1869

1870
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,215✔
1871
        int r;
1,215✔
1872

1873
        assert(c);
1,215✔
1874

1875
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,215✔
1876
                return c->timer_slack_nsec;
1877

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

1882
        return (uint64_t) MAX(r, 0);
1,215✔
1883
}
1884

1885
bool exec_context_get_set_login_environment(const ExecContext *c) {
11,141✔
1886
        assert(c);
11,141✔
1887

1888
        if (c->set_login_environment >= 0)
11,141✔
1889
                return c->set_login_environment;
×
1890

1891
        return c->user || c->dynamic_user || c->pam_name;
20,116✔
1892
}
1893

1894
char** exec_context_get_syscall_filter(const ExecContext *c) {
1,215✔
1895
        _cleanup_strv_free_ char **l = NULL;
1,215✔
1896

1897
        assert(c);
1,215✔
1898

1899
#if HAVE_SECCOMP
1900
        void *id, *val;
1,215✔
1901
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
12,141✔
1902
                _cleanup_free_ char *name = NULL;
10,926✔
1903
                const char *e = NULL;
10,926✔
1904
                char *s;
10,926✔
1905
                int num = PTR_TO_INT(val);
10,926✔
1906

1907
                if (c->syscall_allow_list && num >= 0)
10,926✔
1908
                        /* syscall with num >= 0 in allow-list is denied. */
1909
                        continue;
×
1910

1911
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
10,926✔
1912
                if (!name)
10,926✔
1913
                        continue;
×
1914

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

1928
                if (strv_consume(&l, s) < 0)
10,926✔
1929
                        return NULL;
1930
        }
1931

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

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

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

1941
        assert(c);
1,215✔
1942

1943
#if HAVE_SECCOMP
1944
        void *id;
1,215✔
1945
        SET_FOREACH(id, c->syscall_archs) {
1,247✔
1946
                const char *name;
32✔
1947

1948
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
32✔
1949
                if (!name)
32✔
1950
                        continue;
×
1951

1952
                if (strv_extend(&l, name) < 0)
32✔
1953
                        return NULL;
×
1954
        }
1955

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

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

1962
char** exec_context_get_syscall_log(const ExecContext *c) {
1,215✔
1963
        _cleanup_strv_free_ char **l = NULL;
1,215✔
1964

1965
        assert(c);
1,215✔
1966

1967
#if HAVE_SECCOMP
1968
        void *id, *val;
1,215✔
1969
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
1,215✔
1970
                char *name = NULL;
×
1971

1972
                name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
1973
                if (!name)
×
1974
                        continue;
×
1975

1976
                if (strv_consume(&l, name) < 0)
×
1977
                        return NULL;
×
1978
        }
1979

1980
        strv_sort(l);
1,215✔
1981
#endif
1982

1983
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,215✔
1984
}
1985

1986
char** exec_context_get_address_families(const ExecContext *c) {
1,215✔
1987
        _cleanup_strv_free_ char **l = NULL;
1,215✔
1988
        void *af;
1,215✔
1989

1990
        assert(c);
1,215✔
1991

1992
        SET_FOREACH(af, c->address_families) {
1,309✔
1993
                const char *name;
94✔
1994

1995
                name = af_to_name(PTR_TO_INT(af));
94✔
1996
                if (!name)
94✔
1997
                        continue;
×
1998

1999
                if (strv_extend(&l, name) < 0)
94✔
2000
                        return NULL;
×
2001
        }
2002

2003
        strv_sort(l);
1,215✔
2004

2005
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,215✔
2006
}
2007

2008
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
1,215✔
2009
        assert(c);
1,215✔
2010

2011
#if HAVE_LIBBPF
2012
        char **l = set_get_strv(c->restrict_filesystems);
1,215✔
2013
        if (!l)
1,215✔
2014
                return NULL;
2015

2016
        return strv_sort(l);
1,215✔
2017
#else
2018
        return strv_new(NULL);
2019
#endif
2020
}
2021

2022
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
13,091✔
2023
        assert(c);
13,091✔
2024

2025
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
13,091✔
2026
}
2027

2028
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
14,358✔
2029
        assert(c);
14,358✔
2030

2031
        return c->restrict_filesystems_allow_list ||
14,358✔
2032
          !set_isempty(c->restrict_filesystems);
14,358✔
2033
}
2034

2035
bool exec_context_with_rootfs(const ExecContext *c) {
64,382✔
2036
        assert(c);
64,382✔
2037

2038
        /* Checks if RootDirectory= or RootImage= are used */
2039

2040
        return !empty_or_root(c->root_directory) || c->root_image;
64,382✔
2041
}
2042

2043
int exec_context_has_vpicked_extensions(const ExecContext *context) {
4✔
2044
        int r;
4✔
2045

2046
        assert(context);
4✔
2047

2048
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
4✔
2049
                r = path_uses_vpick(mi->source);
×
2050
                if (r != 0)
×
2051
                        return r;
2052
        }
2053
        STRV_FOREACH(ed, context->extension_directories) {
4✔
2054
                r = path_uses_vpick(*ed);
×
2055
                if (r != 0)
×
2056
                        return r;
2057
        }
2058

2059
        return 0;
2060
}
2061

2062
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
4,197✔
2063
        assert(s);
4,197✔
2064

2065
        *s = (ExecStatus) {
4,197✔
2066
                .pid = pid,
2067
        };
2068

2069
        if (ts)
4,197✔
2070
                s->start_timestamp = *ts;
4,197✔
2071
        else
2072
                dual_timestamp_now(&s->start_timestamp);
×
2073
}
4,197✔
2074

2075
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
1,946✔
2076
        assert(s);
1,946✔
2077

2078
        if (s->pid != pid)
1,946✔
2079
                *s = (ExecStatus) {
6✔
2080
                        .pid = pid,
2081
                };
2082

2083
        dual_timestamp_now(&s->exit_timestamp);
1,946✔
2084

2085
        s->code = code;
1,946✔
2086
        s->status = status;
1,946✔
2087

2088
        if (context && context->utmp_id)
1,946✔
2089
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
14✔
2090
}
1,946✔
2091

2092
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
7,312✔
2093
        assert(s);
7,312✔
2094
        assert(ucred);
7,312✔
2095
        assert(ts);
7,312✔
2096

2097
        if (ucred->pid != s->pid)
7,312✔
2098
                *s = (ExecStatus) {
7✔
2099
                        .pid = ucred->pid,
2100
                };
2101

2102
        s->handoff_timestamp = *ts;
7,312✔
2103
}
7,312✔
2104

2105
void exec_status_reset(ExecStatus *s) {
25,705✔
2106
        assert(s);
25,705✔
2107

2108
        *s = (ExecStatus) {};
25,705✔
2109
}
25,705✔
2110

2111
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
98✔
2112
        assert(s);
98✔
2113
        assert(f);
98✔
2114

2115
        if (s->pid <= 0)
98✔
2116
                return;
2117

2118
        prefix = strempty(prefix);
10✔
2119

2120
        fprintf(f,
10✔
2121
                "%sPID: "PID_FMT"\n",
2122
                prefix, s->pid);
2123

2124
        if (dual_timestamp_is_set(&s->start_timestamp))
10✔
2125
                fprintf(f,
10✔
2126
                        "%sStart Timestamp: %s\n",
2127
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
10✔
2128

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

2140
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
10✔
2141

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

2157
                fprintf(f,
×
2158
                        "%sExit Code: %s\n"
2159
                        "%sExit Status: %i\n",
2160
                        prefix, sigchld_code_to_string(s->code),
×
2161
                        prefix, s->status);
×
2162
        }
2163
}
2164

2165
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
98✔
2166
        _cleanup_free_ char *cmd = NULL;
196✔
2167
        const char *prefix2;
98✔
2168

2169
        assert(c);
98✔
2170
        assert(f);
98✔
2171

2172
        prefix = strempty(prefix);
98✔
2173
        prefix2 = strjoina(prefix, "\t");
490✔
2174

2175
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
98✔
2176

2177
        fprintf(f,
98✔
2178
                "%sCommand Line: %s\n",
2179
                prefix, strnull(cmd));
2180

2181
        exec_status_dump(&c->exec_status, f, prefix2);
98✔
2182
}
98✔
2183

2184
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
95✔
2185
        assert(f);
95✔
2186

2187
        prefix = strempty(prefix);
95✔
2188

2189
        LIST_FOREACH(command, i, c)
193✔
2190
                exec_command_dump(i, f, prefix);
98✔
2191
}
95✔
2192

2193
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
15,693✔
2194
        ExecCommand *end;
15,693✔
2195

2196
        assert(l);
15,693✔
2197
        assert(e);
15,693✔
2198

2199
        if (*l) {
15,693✔
2200
                /* It's kind of important, that we keep the order here */
2201
                end = LIST_FIND_TAIL(command, *l);
363✔
2202
                LIST_INSERT_AFTER(command, *l, end, e);
126✔
2203
        } else
2204
                *l = e;
15,567✔
2205
}
15,693✔
2206

2207
int exec_command_set(ExecCommand *c, const char *path, ...) {
188✔
2208
        va_list ap;
188✔
2209
        char **l, *p;
188✔
2210

2211
        assert(c);
188✔
2212
        assert(path);
188✔
2213

2214
        va_start(ap, path);
188✔
2215
        l = strv_new_ap(path, ap);
188✔
2216
        va_end(ap);
188✔
2217

2218
        if (!l)
188✔
2219
                return -ENOMEM;
188✔
2220

2221
        p = strdup(path);
188✔
2222
        if (!p) {
188✔
2223
                strv_free(l);
×
2224
                return -ENOMEM;
×
2225
        }
2226

2227
        free_and_replace(c->path, p);
188✔
2228

2229
        return strv_free_and_replace(c->argv, l);
188✔
2230
}
2231

2232
int exec_command_append(ExecCommand *c, const char *path, ...) {
277✔
2233
        char **l;
277✔
2234
        va_list ap;
277✔
2235
        int r;
277✔
2236

2237
        assert(c);
277✔
2238
        assert(path);
277✔
2239

2240
        va_start(ap, path);
277✔
2241
        l = strv_new_ap(path, ap);
277✔
2242
        va_end(ap);
277✔
2243

2244
        if (!l)
277✔
2245
                return -ENOMEM;
277✔
2246

2247
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates = */ false);
277✔
2248
        if (r < 0)
277✔
2249
                return r;
×
2250

2251
        return 0;
2252
}
2253

2254
static char *destroy_tree(char *path) {
251✔
2255
        if (!path)
251✔
2256
                return NULL;
2257

2258
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
88✔
2259
                log_debug("Spawning process to nuke '%s'", path);
88✔
2260

2261
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
88✔
2262
        }
2263

2264
        return mfree(path);
88✔
2265
}
2266

2267
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
94,829✔
2268
        assert(rt);
94,829✔
2269

2270
        if (rt->manager)
94,829✔
2271
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
150✔
2272

2273
        rt->id = mfree(rt->id);
94,829✔
2274
        rt->tmp_dir = mfree(rt->tmp_dir);
94,829✔
2275
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
94,829✔
2276
        safe_close_pair(rt->netns_storage_socket);
94,829✔
2277
        safe_close_pair(rt->ipcns_storage_socket);
94,829✔
2278
}
94,829✔
2279

2280
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
94,801✔
2281
        if (!rt)
94,801✔
2282
                return NULL;
2283

2284
        exec_shared_runtime_done(rt);
94,801✔
2285
        return mfree(rt);
94,801✔
2286
}
2287

2288
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
151✔
2289
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
97,194✔
2290

2291
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
51✔
2292
        if (!rt)
51✔
2293
                return NULL;
2294

2295
        assert(rt->n_ref > 0);
50✔
2296
        rt->n_ref--;
50✔
2297

2298
        if (rt->n_ref > 0)
50✔
2299
                return NULL;
2300

2301
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
50✔
2302
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
50✔
2303

2304
        return exec_shared_runtime_free(rt);
50✔
2305
}
2306

2307
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
94,801✔
2308
        _cleanup_free_ char *id_copy = NULL;
189,602✔
2309
        ExecSharedRuntime *n;
94,801✔
2310

2311
        assert(ret);
94,801✔
2312

2313
        id_copy = strdup(id);
94,801✔
2314
        if (!id_copy)
94,801✔
2315
                return -ENOMEM;
2316

2317
        n = new(ExecSharedRuntime, 1);
94,801✔
2318
        if (!n)
94,801✔
2319
                return -ENOMEM;
2320

2321
        *n = (ExecSharedRuntime) {
94,801✔
2322
                .id = TAKE_PTR(id_copy),
94,801✔
2323
                .netns_storage_socket = EBADF_PAIR,
2324
                .ipcns_storage_socket = EBADF_PAIR,
2325
        };
2326

2327
        *ret = n;
94,801✔
2328
        return 0;
94,801✔
2329
}
2330

2331
static int exec_shared_runtime_add(
150✔
2332
                Manager *m,
2333
                const char *id,
2334
                char **tmp_dir,
2335
                char **var_tmp_dir,
2336
                int netns_storage_socket[2],
2337
                int ipcns_storage_socket[2],
2338
                ExecSharedRuntime **ret) {
2339

2340
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
150✔
2341
        int r;
150✔
2342

2343
        assert(m);
150✔
2344
        assert(id);
150✔
2345

2346
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2347

2348
        r = exec_shared_runtime_allocate(&rt, id);
150✔
2349
        if (r < 0)
150✔
2350
                return r;
2351

2352
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
150✔
2353
        if (r < 0)
150✔
2354
                return r;
2355

2356
        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
150✔
2357
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
150✔
2358
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
150✔
2359

2360
        if (netns_storage_socket) {
150✔
2361
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
150✔
2362
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
150✔
2363
        }
2364

2365
        if (ipcns_storage_socket) {
150✔
2366
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
150✔
2367
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
150✔
2368
        }
2369

2370
        rt->manager = m;
150✔
2371

2372
        if (ret)
150✔
2373
                *ret = rt;
71✔
2374
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2375
        TAKE_PTR(rt);
150✔
2376
        return 0;
150✔
2377
}
2378

2379
static int exec_shared_runtime_make(
5,364✔
2380
                Manager *m,
2381
                const ExecContext *c,
2382
                const char *id,
2383
                ExecSharedRuntime **ret) {
2384

2385
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
5,364✔
2386
        _cleanup_close_pair_ int netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
10,728✔
2387
        int r;
5,364✔
2388

2389
        assert(m);
5,364✔
2390
        assert(c);
5,364✔
2391
        assert(id);
5,364✔
2392

2393
        /* It is not necessary to create ExecSharedRuntime object. */
2394
        if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
5,364✔
2395
                *ret = NULL;
5,293✔
2396
                return 0;
5,293✔
2397
        }
2398

2399
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
136✔
2400
            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
65✔
2401
              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
×
2402
               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
×
2403
                r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
65✔
2404
                if (r < 0)
65✔
2405
                        return r;
2406
        }
2407

2408
        if (exec_needs_network_namespace(c))
71✔
2409
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
7✔
2410
                        return -errno;
×
2411

2412
        if (exec_needs_ipc_namespace(c))
71✔
2413
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
2✔
2414
                        return -errno;
×
2415

2416
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
71✔
2417
        if (r < 0)
71✔
2418
                return r;
×
2419

2420
        return 1;
2421
}
2422

2423
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
5,443✔
2424
        ExecSharedRuntime *rt;
5,443✔
2425
        int r;
5,443✔
2426

2427
        assert(m);
5,443✔
2428
        assert(id);
5,443✔
2429
        assert(ret);
5,443✔
2430

2431
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
5,443✔
2432
        if (rt)
5,443✔
2433
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2434
                goto ref;
79✔
2435

2436
        if (!create) {
5,364✔
2437
                *ret = NULL;
×
2438
                return 0;
×
2439
        }
2440

2441
        /* If not found, then create a new object. */
2442
        r = exec_shared_runtime_make(m, c, id, &rt);
5,364✔
2443
        if (r < 0)
5,364✔
2444
                return r;
2445
        if (r == 0) {
5,364✔
2446
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2447
                *ret = NULL;
5,293✔
2448
                return 0;
5,293✔
2449
        }
2450

2451
ref:
71✔
2452
        /* increment reference counter. */
2453
        rt->n_ref++;
150✔
2454
        *ret = rt;
150✔
2455
        return 1;
150✔
2456
}
2457

2458
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
63✔
2459
        ExecSharedRuntime *rt;
63✔
2460

2461
        assert(m);
63✔
2462
        assert(f);
63✔
2463
        assert(fds);
63✔
2464

2465
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
163✔
2466
                fprintf(f, "exec-runtime=%s", rt->id);
100✔
2467

2468
                if (rt->tmp_dir)
100✔
2469
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
100✔
2470

2471
                if (rt->var_tmp_dir)
100✔
2472
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
100✔
2473

2474
                if (rt->netns_storage_socket[0] >= 0) {
100✔
2475
                        int copy;
2✔
2476

2477
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
2✔
2478
                        if (copy < 0)
2✔
2479
                                return copy;
×
2480

2481
                        fprintf(f, " netns-socket-0=%i", copy);
2✔
2482
                }
2483

2484
                if (rt->netns_storage_socket[1] >= 0) {
100✔
2485
                        int copy;
2✔
2486

2487
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
2✔
2488
                        if (copy < 0)
2✔
2489
                                return copy;
2490

2491
                        fprintf(f, " netns-socket-1=%i", copy);
2✔
2492
                }
2493

2494
                if (rt->ipcns_storage_socket[0] >= 0) {
100✔
2495
                        int copy;
×
2496

2497
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2498
                        if (copy < 0)
×
2499
                                return copy;
2500

2501
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2502
                }
2503

2504
                if (rt->ipcns_storage_socket[1] >= 0) {
100✔
2505
                        int copy;
×
2506

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

2511
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2512
                }
2513

2514
                fputc('\n', f);
100✔
2515
        }
2516

2517
        return 0;
63✔
2518
}
2519

2520
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
97,044✔
2521
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
97,044✔
2522
        ExecSharedRuntime *rt = NULL;
97,044✔
2523
        int r;
97,044✔
2524

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

2530
        assert(u);
97,044✔
2531
        assert(key);
97,044✔
2532
        assert(value);
97,044✔
2533

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

2541
        if (u->manager) {
97,044✔
2542
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
97,044✔
2543
                        return log_oom();
×
2544

2545
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
97,044✔
2546
        }
2547
        if (!rt) {
97,044✔
2548
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
94,651✔
2549
                        return log_oom();
×
2550

2551
                rt = rt_create;
94,651✔
2552
        }
2553

2554
        if (streq(key, "tmp-dir")) {
97,044✔
2555
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2556
                        return -ENOMEM;
2557

2558
        } else if (streq(key, "var-tmp-dir")) {
97,044✔
2559
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2560
                        return -ENOMEM;
2561

2562
        } else if (streq(key, "netns-socket-0")) {
97,044✔
2563

2564
                safe_close(rt->netns_storage_socket[0]);
×
2565
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2566
                if (rt->netns_storage_socket[0] < 0)
×
2567
                        return 0;
2568

2569
        } else if (streq(key, "netns-socket-1")) {
97,044✔
2570

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

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

2586
                rt_create->manager = u->manager;
×
2587

2588
                /* Avoid cleanup */
2589
                TAKE_PTR(rt_create);
×
2590
        }
2591

2592
        return 1;
2593
}
2594

2595
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
79✔
2596
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
79✔
2597
        char *id = NULL;
79✔
2598
        int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
79✔
2599
        const char *p, *v = ASSERT_PTR(value);
79✔
2600
        size_t n;
79✔
2601

2602
        assert(m);
79✔
2603
        assert(fds);
79✔
2604

2605
        n = strcspn(v, " ");
79✔
2606
        id = strndupa_safe(v, n);
79✔
2607
        if (v[n] != ' ')
79✔
2608
                goto finalize;
×
2609
        p = v + n + 1;
79✔
2610

2611
        v = startswith(p, "tmp-dir=");
79✔
2612
        if (v) {
79✔
2613
                n = strcspn(v, " ");
79✔
2614
                tmp_dir = strndup(v, n);
79✔
2615
                if (!tmp_dir)
79✔
2616
                        return log_oom();
×
2617
                if (v[n] != ' ')
79✔
2618
                        goto finalize;
×
2619
                p = v + n + 1;
79✔
2620
        }
2621

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

2633
        v = startswith(p, "netns-socket-0=");
1✔
2634
        if (v) {
1✔
2635
                char *buf;
1✔
2636

2637
                n = strcspn(v, " ");
1✔
2638
                buf = strndupa_safe(v, n);
1✔
2639

2640
                netns_fdpair[0] = deserialize_fd(fds, buf);
1✔
2641
                if (netns_fdpair[0] < 0)
1✔
2642
                        return netns_fdpair[0];
2643
                if (v[n] != ' ')
1✔
2644
                        goto finalize;
×
2645
                p = v + n + 1;
1✔
2646
        }
2647

2648
        v = startswith(p, "netns-socket-1=");
1✔
2649
        if (v) {
1✔
2650
                char *buf;
1✔
2651

2652
                n = strcspn(v, " ");
1✔
2653
                buf = strndupa_safe(v, n);
1✔
2654

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

2663
        v = startswith(p, "ipcns-socket-0=");
×
2664
        if (v) {
×
2665
                char *buf;
×
2666

2667
                n = strcspn(v, " ");
×
2668
                buf = strndupa_safe(v, n);
×
2669

2670
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2671
                if (ipcns_fdpair[0] < 0)
×
2672
                        return ipcns_fdpair[0];
2673
                if (v[n] != ' ')
×
2674
                        goto finalize;
×
2675
                p = v + n + 1;
×
2676
        }
2677

2678
        v = startswith(p, "ipcns-socket-1=");
×
2679
        if (v) {
×
2680
                char *buf;
×
2681

2682
                n = strcspn(v, " ");
×
2683
                buf = strndupa_safe(v, n);
×
2684

2685
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2686
                if (ipcns_fdpair[1] < 0)
×
2687
                        return ipcns_fdpair[1];
2688
        }
2689

2690
finalize:
×
2691
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
79✔
2692
        if (r < 0)
79✔
2693
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2694
        return 0;
2695
}
2696

2697
void exec_shared_runtime_vacuum(Manager *m) {
1,478✔
2698
        ExecSharedRuntime *rt;
1,478✔
2699

2700
        assert(m);
1,478✔
2701

2702
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2703

2704
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
1,557✔
2705
                if (rt->n_ref > 0)
79✔
2706
                        continue;
79✔
2707

2708
                (void) exec_shared_runtime_free(rt);
×
2709
        }
2710
}
1,478✔
2711

2712
int exec_runtime_make(
5,443✔
2713
                const Unit *unit,
2714
                const ExecContext *context,
2715
                ExecSharedRuntime *shared,
2716
                DynamicCreds *creds,
2717
                ExecRuntime **ret) {
2718
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
5,443✔
2719
        _cleanup_free_ char *ephemeral = NULL;
5,443✔
2720
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
5,443✔
2721
        int r;
5,443✔
2722

2723
        assert(unit);
5,443✔
2724
        assert(context);
5,443✔
2725
        assert(ret);
5,443✔
2726

2727
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
5,443✔
2728
                *ret = NULL;
5,292✔
2729
                return 0;
5,292✔
2730
        }
2731

2732
        if (exec_needs_ephemeral(context)) {
151✔
2733
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2734
                if (r < 0)
×
2735
                        return r;
2736

2737
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2738
                if (r < 0)
×
2739
                        return r;
2740

2741
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2742
                        return -errno;
×
2743
        }
2744

2745
        rt = new(ExecRuntime, 1);
151✔
2746
        if (!rt)
151✔
2747
                return -ENOMEM;
2748

2749
        *rt = (ExecRuntime) {
151✔
2750
                .shared = shared,
2751
                .dynamic_creds = creds,
2752
                .ephemeral_copy = TAKE_PTR(ephemeral),
151✔
2753
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
151✔
2754
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
151✔
2755
        };
2756

2757
        *ret = TAKE_PTR(rt);
151✔
2758
        return 1;
151✔
2759
}
2760

2761
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
46,041✔
2762
        if (!rt)
46,041✔
2763
                return NULL;
2764

2765
        exec_shared_runtime_unref(rt->shared);
151✔
2766
        dynamic_creds_unref(rt->dynamic_creds);
151✔
2767

2768
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
151✔
2769

2770
        safe_close_pair(rt->ephemeral_storage_socket);
151✔
2771
        return mfree(rt);
151✔
2772
}
2773

2774
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
5,317✔
2775
        if (!rt)
5,317✔
2776
                return NULL;
2777

2778
        rt->shared = exec_shared_runtime_destroy(rt->shared);
51✔
2779
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
51✔
2780
        return exec_runtime_free(rt);
51✔
2781
}
2782

2783
void exec_runtime_clear(ExecRuntime *rt) {
28✔
2784
        if (!rt)
28✔
2785
                return;
2786

2787
        safe_close_pair(rt->ephemeral_storage_socket);
28✔
2788
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
28✔
2789
}
2790

2791
void exec_params_shallow_clear(ExecParameters *p) {
2,143✔
2792
        if (!p)
2,143✔
2793
                return;
2794

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

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

2811
void exec_params_deep_clear(ExecParameters *p) {
28✔
2812
        if (!p)
28✔
2813
                return;
2814

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

2819
        close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
28✔
2820

2821
        p->cgroup_path = mfree(p->cgroup_path);
28✔
2822

2823
        if (p->prefix) {
28✔
2824
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
28✔
2825
                p->prefix = mfree(p->prefix);
28✔
2826
        }
2827

2828
        p->received_credentials_directory = mfree(p->received_credentials_directory);
28✔
2829
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
28✔
2830

2831
        if (p->idle_pipe) {
28✔
2832
                close_many_and_free(p->idle_pipe, 4);
×
2833
                p->idle_pipe = NULL;
×
2834
        }
2835

2836
        p->stdin_fd = safe_close(p->stdin_fd);
28✔
2837
        p->stdout_fd = safe_close(p->stdout_fd);
28✔
2838
        p->stderr_fd = safe_close(p->stderr_fd);
28✔
2839

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

2842
        open_file_free_many(&p->open_files);
28✔
2843

2844
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
28✔
2845

2846
        exec_params_shallow_clear(p);
28✔
2847
}
2848

2849
void exec_directory_done(ExecDirectory *d) {
230,090✔
2850
        if (!d)
230,090✔
2851
                return;
2852

2853
        FOREACH_ARRAY(i, d->items, d->n_items) {
231,599✔
2854
                free(i->path);
1,509✔
2855
                strv_free(i->symlinks);
1,509✔
2856
        }
2857

2858
        d->items = mfree(d->items);
230,090✔
2859
        d->n_items = 0;
230,090✔
2860
        d->mode = 0755;
230,090✔
2861
}
2862

2863
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
5,167✔
2864
        assert(d);
5,167✔
2865
        assert(path);
5,167✔
2866

2867
        FOREACH_ARRAY(i, d->items, d->n_items)
7,511✔
2868
                if (path_equal(i->path, path))
2,359✔
2869
                        return i;
2870

2871
        return NULL;
2872
}
2873

2874
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
5,167✔
2875
        _cleanup_strv_free_ char **s = NULL;
×
2876
        _cleanup_free_ char *p = NULL;
5,167✔
2877
        ExecDirectoryItem *existing;
5,167✔
2878
        int r;
5,167✔
2879

2880
        assert(d);
5,167✔
2881
        assert(path);
5,167✔
2882

2883
        existing = exec_directory_find(d, path);
5,167✔
2884
        if (existing) {
5,167✔
2885
                r = strv_extend(&existing->symlinks, symlink);
15✔
2886
                if (r < 0)
15✔
2887
                        return r;
2888

2889
                existing->flags |= flags;
15✔
2890

2891
                return 0; /* existing item is updated */
15✔
2892
        }
2893

2894
        p = strdup(path);
5,152✔
2895
        if (!p)
5,152✔
2896
                return -ENOMEM;
2897

2898
        if (symlink) {
5,152✔
2899
                s = strv_new(symlink);
6✔
2900
                if (!s)
6✔
2901
                        return -ENOMEM;
2902
        }
2903

2904
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
5,152✔
2905
                return -ENOMEM;
2906

2907
        d->items[d->n_items++] = (ExecDirectoryItem) {
5,152✔
2908
                .path = TAKE_PTR(p),
5,152✔
2909
                .symlinks = TAKE_PTR(s),
5,152✔
2910
                .flags = flags,
2911
        };
2912

2913
        return 1; /* new item is added */
5,152✔
2914
}
2915

2916
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
862✔
2917
        assert(a);
862✔
2918
        assert(b);
862✔
2919

2920
        return path_compare(a->path, b->path);
862✔
2921
}
2922

2923
void exec_directory_sort(ExecDirectory *d) {
127,171✔
2924
        assert(d);
127,171✔
2925

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

2931
        if (d->n_items <= 1)
127,171✔
2932
                return;
2933

2934
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
156✔
2935

2936
        for (size_t i = 1; i < d->n_items; i++)
675✔
2937
                for (size_t j = 0; j < i; j++)
1,724✔
2938
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
1,220✔
2939
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
2940
                                break;
15✔
2941
                        }
2942
}
2943

2944
ExecCleanMask exec_clean_mask_from_string(const char *s) {
×
2945
        ExecDirectoryType t;
×
2946

2947
        assert(s);
×
2948

2949
        if (streq(s, "all"))
×
2950
                return EXEC_CLEAN_ALL;
2951
        if (streq(s, "fdstore"))
×
2952
                return EXEC_CLEAN_FDSTORE;
2953

2954
        t = exec_resource_type_from_string(s);
×
2955
        if (t < 0)
×
2956
                return (ExecCleanMask) t;
2957

2958
        return 1U << t;
×
2959
}
2960

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

2972
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,053✔
2973

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

2989
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
32,320✔
2990

2991
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
2992
        [EXEC_UTMP_INIT]  = "init",
2993
        [EXEC_UTMP_LOGIN] = "login",
2994
        [EXEC_UTMP_USER]  = "user",
2995
};
2996

2997
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
15,340✔
2998

2999
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
3000
        [EXEC_PRESERVE_NO]      = "no",
3001
        [EXEC_PRESERVE_YES]     = "yes",
3002
        [EXEC_PRESERVE_RESTART] = "restart",
3003
};
3004

3005
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
17,317✔
3006

3007
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
3008
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3009
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
3010
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
3011
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
3012
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
3013
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
3014
};
3015

3016
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3017

3018
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3019
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
3020
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
3021
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
3022
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
3023
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
3024
};
3025

3026
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3027

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

3039
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
306✔
3040

3041
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3042
        [EXEC_KEYRING_INHERIT] = "inherit",
3043
        [EXEC_KEYRING_PRIVATE] = "private",
3044
        [EXEC_KEYRING_SHARED]  = "shared",
3045
};
3046

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