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

systemd / systemd / 18638092546

18 Oct 2025 02:43PM UTC coverage: 72.264% (+0.08%) from 72.189%
18638092546

push

github

bluca
ci: add bpftool workaround to coverity too

304190 of 420941 relevant lines covered (72.26%)

1092817.28 hits per line

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

76.28
/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) {
14,566✔
69
        assert(context);
14,566✔
70

71
        if (context->stdio_as_fds)
14,566✔
72
                return NULL;
73

74
        if (context->tty_path)
13,850✔
75
                return context->tty_path;
705✔
76

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

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

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

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

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

96
        if (!tty_path)
874✔
97
                tty_path = exec_context_tty_path(context);
440✔
98

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

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

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

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

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

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

158
        if (context->tty_reset)
455✔
159
                (void) terminal_reset_defensive(
396✔
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));
393✔
163

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

265
        assert(context);
32,654✔
266

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

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

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

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

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

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

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

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

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

301
        if (context->private_devices ||
28,920✔
302
            context->private_mounts > 0 ||
28,552✔
303
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
28,099✔
304
            context->protect_system != PROTECT_SYSTEM_NO ||
28,070✔
305
            context->protect_home != PROTECT_HOME_NO ||
28,070✔
306
            context->protect_kernel_tunables ||
28,070✔
307
            context->protect_kernel_modules ||
28,070✔
308
            context->protect_kernel_logs ||
51,923✔
309
            exec_needs_cgroup_mount(context) ||
25,960✔
310
            context->protect_proc != PROTECT_PROC_DEFAULT ||
25,942✔
311
            context->proc_subset != PROC_SUBSET_ALL ||
25,888✔
312
            context->private_bpf != PRIVATE_BPF_NO ||
51,746✔
313
            exec_needs_ipc_namespace(context) ||
51,746✔
314
            exec_needs_pid_namespace(context, params))
25,873✔
315
                return true;
3,088✔
316

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

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

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

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

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

339
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
154,618✔
340
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
133,800✔
341
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,003✔
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,827✔
348
        assert(context);
3,827✔
349
        assert(params);
3,827✔
350

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

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

357
        if (!context->root_directory && !context->root_image)
3,143✔
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_with_exec_params(const ExecContext *context, const ExecParameters *params) {
23,736✔
370
        assert(params);
23,736✔
371

372
        if (params->debug_invocation)
23,736✔
373
                return LOG_DEBUG;
374

375
        return exec_log_level_max(context);
23,732✔
376
}
377

378
int exec_log_level_max(const ExecContext *context) {
23,776✔
379
        assert(context);
23,776✔
380
        return context->log_level_max < 0 ? log_get_max_level() : context->log_level_max;
23,776✔
381
}
382

383
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
11,127✔
384
        assert(context);
11,127✔
385

386
        if (!context->dynamic_user)
11,127✔
387
                return false;
388

389
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
104✔
390
                return false;
391

392
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
98✔
393
                return false;
14✔
394

395
        return true;
396
}
397

398
int exec_params_needs_control_subcgroup(const ExecParameters *params) {
2,137✔
399
        /* Keep this in sync with exec_params_get_cgroup_path(). */
400
        return FLAGS_SET(params->flags, EXEC_CGROUP_DELEGATE|EXEC_CONTROL_CGROUP|EXEC_IS_CONTROL);
2,137✔
401
}
402

403
int exec_params_get_cgroup_path(
12,567✔
404
                const ExecParameters *params,
405
                const CGroupContext *c,
406
                const char *prefix,
407
                char **ret) {
408

409
        const char *subgroup = NULL;
12,567✔
410
        char *p;
12,567✔
411

412
        assert(params);
12,567✔
413
        assert(c);
12,567✔
414
        assert(ret);
12,567✔
415

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

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

433
        if (subgroup)
654✔
434
                p = path_join(prefix, subgroup);
697✔
435
        else
436
                p = strdup(strempty(prefix));
11,878✔
437
        if (!p)
12,567✔
438
                return -ENOMEM;
439

440
        *ret = p;
12,567✔
441
        return !!subgroup;
12,567✔
442
}
443

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

447
        return c->cpu_affinity_from_numa;
1,305✔
448
}
449

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

455
        if (!DEBUG_LOGGING)
2,107✔
456
                return;
2,107✔
457

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

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

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

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

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

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

494
        LOG_CONTEXT_PUSH_UNIT(unit);
4,214✔
495

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

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

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

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

520
                        r = cg_create(subcgroup_path);
1✔
521
                        if (r < 0)
1✔
522
                                return log_unit_error_errno(unit, r, "Failed to create subcgroup '%s': %m", subcgroup_path);
×
523
                }
524

525
                cgtarget = subcgroup_path;
1✔
526
        } else
527
                cgtarget = params->cgroup_path;
2,106✔
528

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

536
        r = open_serialization_file("sd-executor-state", &f);
2,107✔
537
        if (r < 0)
2,107✔
538
                return log_unit_error_errno(unit, r, "Failed to open serialization stream: %m");
×
539

540
        fdset = fdset_new();
2,107✔
541
        if (!fdset)
2,107✔
542
                return log_oom();
×
543

544
        r = exec_serialize_invocation(f, fdset, context, command, params, runtime, cgroup_context);
2,107✔
545
        if (r < 0)
2,107✔
546
                return log_unit_error_errno(unit, r, "Failed to serialize parameters: %m");
×
547

548
        r = finish_serialization_file(f);
2,107✔
549
        if (r < 0)
2,107✔
550
                return log_unit_error_errno(unit, r, "Failed to finish serialization stream: %m");
×
551

552
        r = fd_cloexec(fileno(f), false);
2,107✔
553
        if (r < 0)
2,107✔
554
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialization fd: %m");
×
555

556
        r = fdset_cloexec(fdset, false);
2,107✔
557
        if (r < 0)
2,107✔
558
                return log_unit_error_errno(unit, r, "Failed to set O_CLOEXEC on serialized fds: %m");
×
559

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

567
        char serialization_fd_number[DECIMAL_STR_MAX(int)];
2,107✔
568
        xsprintf(serialization_fd_number, "%i", fileno(f));
2,107✔
569

570
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
2,107✔
571
        dual_timestamp start_timestamp;
2,107✔
572

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

579
        /* Record the start timestamp before we fork so that it is guaranteed to be earlier than the
580
         * handoff timestamp. */
581
        dual_timestamp_now(&start_timestamp);
2,107✔
582

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

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

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

611
        log_unit_debug(unit, "Forked %s as " PID_FMT " (%s CLONE_INTO_CGROUP)",
2,107✔
612
                       command->path, pidref.pid, r > 0 ? "via" : "without");
613

614
        exec_status_start(&command->exec_status, pidref.pid, &start_timestamp);
2,107✔
615

616
        *ret = TAKE_PIDREF(pidref);
2,107✔
617
        return 0;
2,107✔
618
}
619

620
void exec_context_init(ExecContext *c) {
59,534✔
621
        assert(c);
59,534✔
622

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

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

653
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
357,204✔
654
                d->mode = 0755;
297,670✔
655

656
        numa_policy_reset(&c->numa_policy);
59,534✔
657

658
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
59,534✔
659
}
59,534✔
660

661
void exec_context_done(ExecContext *c) {
47,697✔
662
        assert(c);
47,697✔
663

664
        c->environment = strv_free(c->environment);
47,697✔
665
        c->environment_files = strv_free(c->environment_files);
47,697✔
666
        c->pass_environment = strv_free(c->pass_environment);
47,697✔
667
        c->unset_environment = strv_free(c->unset_environment);
47,697✔
668

669
        rlimit_free_all(c->rlimit);
47,697✔
670

671
        for (size_t l = 0; l < 3; l++) {
190,788✔
672
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
143,091✔
673
                c->stdio_file[l] = mfree(c->stdio_file[l]);
143,091✔
674
        }
675

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

694
        c->supplementary_groups = strv_free(c->supplementary_groups);
47,697✔
695

696
        c->pam_name = mfree(c->pam_name);
47,697✔
697

698
        c->read_only_paths = strv_free(c->read_only_paths);
47,697✔
699
        c->read_write_paths = strv_free(c->read_write_paths);
47,697✔
700
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
47,697✔
701
        c->exec_paths = strv_free(c->exec_paths);
47,697✔
702
        c->no_exec_paths = strv_free(c->no_exec_paths);
47,697✔
703
        c->exec_search_path = strv_free(c->exec_search_path);
47,697✔
704

705
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
47,697✔
706
        c->bind_mounts = NULL;
47,697✔
707
        c->n_bind_mounts = 0;
47,697✔
708
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
47,697✔
709
        c->temporary_filesystems = NULL;
47,697✔
710
        c->n_temporary_filesystems = 0;
47,697✔
711
        c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
47,697✔
712

713
        cpu_set_done(&c->cpu_set);
47,697✔
714
        numa_policy_reset(&c->numa_policy);
47,697✔
715

716
        c->utmp_id = mfree(c->utmp_id);
47,697✔
717
        c->selinux_context = mfree(c->selinux_context);
47,697✔
718
        c->apparmor_profile = mfree(c->apparmor_profile);
47,697✔
719
        c->smack_process_label = mfree(c->smack_process_label);
47,697✔
720

721
        c->restrict_filesystems = set_free(c->restrict_filesystems);
47,697✔
722

723
        c->syscall_filter = hashmap_free(c->syscall_filter);
47,697✔
724
        c->syscall_archs = set_free(c->syscall_archs);
47,697✔
725
        c->syscall_log = hashmap_free(c->syscall_log);
47,697✔
726
        c->address_families = set_free(c->address_families);
47,697✔
727

728
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
286,182✔
729
                exec_directory_done(d);
238,485✔
730

731
        c->log_level_max = -1;
47,697✔
732

733
        exec_context_free_log_extra_fields(c);
47,697✔
734
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
47,697✔
735
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
47,697✔
736

737
        c->log_ratelimit = (RateLimit) {};
47,697✔
738

739
        c->stdin_data = mfree(c->stdin_data);
47,697✔
740
        c->stdin_data_size = 0;
47,697✔
741

742
        c->network_namespace_path = mfree(c->network_namespace_path);
47,697✔
743
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
47,697✔
744

745
        c->log_namespace = mfree(c->log_namespace);
47,697✔
746

747
        c->load_credentials = hashmap_free(c->load_credentials);
47,697✔
748
        c->set_credentials = hashmap_free(c->set_credentials);
47,697✔
749
        c->import_credentials = ordered_set_free(c->import_credentials);
47,697✔
750

751
        c->root_image_policy = image_policy_free(c->root_image_policy);
47,697✔
752
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
47,697✔
753
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
47,697✔
754

755
        c->private_hostname = mfree(c->private_hostname);
47,697✔
756
}
47,697✔
757

758
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
5,523✔
759
        assert(c);
5,523✔
760

761
        if (!runtime_prefix)
5,523✔
762
                return 0;
763

764
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
5,524✔
765
                _cleanup_free_ char *p = NULL;
1✔
766

767
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
1✔
768
                        p = path_join(runtime_prefix, "private", i->path);
×
769
                else
770
                        p = path_join(runtime_prefix, i->path);
1✔
771
                if (!p)
1✔
772
                        return -ENOMEM;
773

774
                /* We execute this synchronously, since we need to be sure this is gone when we start the
775
                 * service next. */
776
                (void) rm_rf(p, REMOVE_ROOT);
1✔
777

778
                STRV_FOREACH(symlink, i->symlinks) {
1✔
779
                        _cleanup_free_ char *symlink_abs = NULL;
×
780

781
                        if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
×
782
                                symlink_abs = path_join(runtime_prefix, "private", *symlink);
×
783
                        else
784
                                symlink_abs = path_join(runtime_prefix, *symlink);
×
785
                        if (!symlink_abs)
×
786
                                return -ENOMEM;
×
787

788
                        (void) unlink(symlink_abs);
×
789
                }
790
        }
791

792
        return 0;
793
}
794

795
int exec_context_destroy_mount_ns_dir(Unit *u) {
11,901✔
796
        _cleanup_free_ char *p = NULL;
11,901✔
797

798
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
11,901✔
799
                return 0;
800

801
        p = path_join("/run/systemd/propagate/", u->id);
2,195✔
802
        if (!p)
2,195✔
803
                return -ENOMEM;
804

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

809
        return 0;
810
}
811

812
void exec_command_done(ExecCommand *c) {
101,609✔
813
        assert(c);
101,609✔
814

815
        c->path = mfree(c->path);
101,609✔
816
        c->argv = strv_free(c->argv);
101,609✔
817
}
101,609✔
818

819
void exec_command_done_array(ExecCommand *c, size_t n) {
28,544✔
820
        FOREACH_ARRAY(i, c, n)
114,174✔
821
                exec_command_done(i);
85,630✔
822
}
28,544✔
823

824
ExecCommand* exec_command_free(ExecCommand *c) {
15,949✔
825
        if (!c)
15,949✔
826
                return NULL;
827

828
        exec_command_done(c);
15,949✔
829
        return mfree(c);
15,949✔
830
}
831

832
ExecCommand* exec_command_free_list(ExecCommand *c) {
121,928✔
833
        ExecCommand *i;
121,928✔
834

835
        while ((i = LIST_POP(command, c)))
137,877✔
836
                exec_command_free(i);
15,949✔
837

838
        return NULL;
121,928✔
839
}
840

841
void exec_command_free_array(ExecCommand **c, size_t n) {
19,123✔
842
        FOREACH_ARRAY(i, c, n)
141,036✔
843
                *i = exec_command_free_list(*i);
121,913✔
844
}
19,123✔
845

846
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
7,371✔
847
        FOREACH_ARRAY(i, c, n)
29,483✔
848
                exec_status_reset(&i->exec_status);
22,112✔
849
}
7,371✔
850

851
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
4,753✔
852
        FOREACH_ARRAY(i, c, n)
32,124✔
853
                LIST_FOREACH(command, z, *i)
29,413✔
854
                        exec_status_reset(&z->exec_status);
2,042✔
855
}
4,753✔
856

857
typedef struct InvalidEnvInfo {
858
        const Unit *unit;
859
        const char *path;
860
} InvalidEnvInfo;
861

862
static void invalid_env(const char *p, void *userdata) {
×
863
        InvalidEnvInfo *info = userdata;
×
864

865
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
866
}
×
867

868
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
39,519✔
869
        assert(c);
39,519✔
870

871
        switch (fd_index) {
39,519✔
872

873
        case STDIN_FILENO:
13,173✔
874
                if (c->std_input != EXEC_INPUT_NAMED_FD)
13,173✔
875
                        return NULL;
876

877
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
878

879
        case STDOUT_FILENO:
13,173✔
880
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
13,173✔
881
                        return NULL;
882

883
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
884

885
        case STDERR_FILENO:
13,173✔
886
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
13,173✔
887
                        return NULL;
888

889
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
890

891
        default:
892
                return NULL;
893
        }
894
}
895

896
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
2,107✔
897
        _cleanup_strv_free_ char **v = NULL;
2,107✔
898
        int r;
2,107✔
899

900
        assert(c);
2,107✔
901
        assert(ret);
2,107✔
902

903
        STRV_FOREACH(i, c->environment_files) {
2,109✔
904
                _cleanup_strv_free_ char **paths = NULL;
2✔
905
                bool ignore = false;
2✔
906
                char *fn = *i;
2✔
907

908
                if (fn[0] == '-') {
2✔
909
                        ignore = true;
1✔
910
                        fn++;
1✔
911
                }
912

913
                if (!path_is_absolute(fn)) {
2✔
914
                        if (ignore)
×
915
                                continue;
×
916
                        return -EINVAL;
917
                }
918

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

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

930
                STRV_FOREACH(path, paths) {
2✔
931
                        _cleanup_strv_free_ char **p = NULL;
1✔
932

933
                        r = load_env_file(NULL, *path, &p);
1✔
934
                        if (r < 0) {
1✔
935
                                if (ignore)
×
936
                                        continue;
×
937
                                return r;
938
                        }
939

940
                        /* Log invalid environment variables with filename */
941
                        if (p) {
1✔
942
                                InvalidEnvInfo info = {
1✔
943
                                        .unit = unit,
944
                                        .path = *path,
1✔
945
                                };
946

947
                                strv_env_clean_with_callback(p, invalid_env, &info);
1✔
948
                        }
949

950
                        if (!v)
1✔
951
                                v = TAKE_PTR(p);
1✔
952
                        else {
953
                                char **m = strv_env_merge(v, p);
×
954
                                if (!m)
×
955
                                        return -ENOMEM;
×
956

957
                                strv_free_and_replace(v, m);
×
958
                        }
959
                }
960
        }
961

962
        *ret = TAKE_PTR(v);
2,107✔
963

964
        return 0;
2,107✔
965
}
966

967
static bool tty_may_match_dev_console(const char *tty) {
223✔
968
        _cleanup_free_ char *resolved = NULL;
223✔
969

970
        if (!tty)
223✔
971
                return true;
972

973
        tty = skip_dev_prefix(tty);
223✔
974

975
        /* trivial identity? */
976
        if (streq(tty, "console"))
223✔
977
                return true;
978

979
        if (resolve_dev_console(&resolved) < 0)
39✔
980
                return true; /* if we could not resolve, assume it may */
981

982
        /* "tty0" means the active VC, so it may be the same sometimes */
983
        return path_equal(skip_dev_prefix(resolved), tty) || (streq(skip_dev_prefix(resolved), "tty0") && tty_is_vc(tty));
39✔
984
}
985

986
static bool exec_context_may_touch_tty(const ExecContext *ec) {
23,017✔
987
        assert(ec);
23,017✔
988

989
        return ec->tty_reset ||
45,825✔
990
                ec->tty_vhangup ||
22,808✔
991
                ec->tty_vt_disallocate ||
22,808✔
992
                exec_input_is_terminal(ec->std_input) ||
22,808✔
993
                ec->std_output == EXEC_OUTPUT_TTY ||
45,825✔
994
                ec->std_error == EXEC_OUTPUT_TTY;
22,759✔
995
}
996

997
bool exec_context_may_touch_console(const ExecContext *ec) {
21,631✔
998

999
        return exec_context_may_touch_tty(ec) &&
21,854✔
1000
               tty_may_match_dev_console(exec_context_tty_path(ec));
223✔
1001
}
1002

1003
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,630✔
1004
        assert(c);
1,630✔
1005

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

1013
        if (!c->tty_reset)
1,630✔
1014
                return false;
1015

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

1022
static void strv_fprintf(FILE *f, char **l) {
×
1023
        assert(f);
×
1024

1025
        STRV_FOREACH(g, l)
×
1026
                fprintf(f, " %s", *g);
×
1027
}
×
1028

1029
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
1,824✔
1030
        assert(f);
1,824✔
1031
        assert(prefix);
1,824✔
1032
        assert(name);
1,824✔
1033

1034
        if (!strv_isempty(strv)) {
1,824✔
1035
                fprintf(f, "%s%s:", prefix, name);
×
1036
                strv_fprintf(f, strv);
×
1037
                fputs("\n", f);
×
1038
        }
1039
}
1,824✔
1040

1041
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1042
        assert(p);
×
1043
        assert(f);
×
1044

1045
        prefix = strempty(prefix);
×
1046

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

1075
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1076
        strv_dump(f, prefix, "Environment", p->environment);
×
1077
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1078

1079
        LIST_FOREACH(open_files, file, p->open_files)
×
1080
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1081

1082
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1083
}
×
1084

1085
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
228✔
1086
        int r;
228✔
1087

1088
        assert(c);
228✔
1089
        assert(f);
228✔
1090

1091
        prefix = strempty(prefix);
228✔
1092

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

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

1158
                fprintf(f, "%sBPFDelegateCommands: %s\n", prefix, strna(commands));
×
1159
                fprintf(f, "%sBPFDelegateMaps: %s\n", prefix, strna(maps));
×
1160
                fprintf(f, "%sBPFDelegatePrograms: %s\n", prefix, strna(programs));
×
1161
                fprintf(f, "%sBPFDelegateAttachments: %s\n", prefix, strna(attachments));
×
1162
        }
1163

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

1167
        if (c->root_image)
228✔
1168
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1169

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

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

1187
        if (c->root_hash_path)
228✔
1188
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1189

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

1198
        if (c->root_hash_sig_path)
228✔
1199
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1200

1201
        if (c->root_verity)
228✔
1202
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1203

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

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

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

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

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

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

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

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

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

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

1239
        if (c->nice_set)
228✔
1240
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
×
1241

1242
        if (c->oom_score_adjust_set)
228✔
1243
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
10✔
1244

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

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

1256
        if (c->ioprio_is_set) {
228✔
1257
                _cleanup_free_ char *class_str = NULL;
×
1258

1259
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
×
1260
                if (r >= 0)
×
1261
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
×
1262

1263
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
×
1264
        }
1265

1266
        if (c->cpu_sched_set) {
228✔
1267
                _cleanup_free_ char *policy_str = NULL;
×
1268

1269
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1270
                if (r >= 0)
×
1271
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1272

1273
                fprintf(f,
×
1274
                        "%sCPUSchedulingPriority: %i\n"
1275
                        "%sCPUSchedulingResetOnFork: %s\n",
1276
                        prefix, c->cpu_sched_priority,
×
1277
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1278
        }
1279

1280
        if (c->cpu_set.set) {
228✔
1281
                _cleanup_free_ char *affinity = NULL;
×
1282

1283
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1284
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1285
        }
1286

1287
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
228✔
1288
                _cleanup_free_ char *nodes = NULL;
1✔
1289

1290
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1291
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1292
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1293
        }
1294

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

1298
        fprintf(f,
228✔
1299
                "%sStandardInput: %s\n"
1300
                "%sStandardOutput: %s\n"
1301
                "%sStandardError: %s\n",
1302
                prefix, exec_input_to_string(c->std_input),
228✔
1303
                prefix, exec_output_to_string(c->std_output),
228✔
1304
                prefix, exec_output_to_string(c->std_error));
228✔
1305

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

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

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

1343
        if (IN_SET(c->std_output,
228✔
1344
                   EXEC_OUTPUT_KMSG,
1345
                   EXEC_OUTPUT_JOURNAL,
1346
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1347
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1348
            IN_SET(c->std_error,
17✔
1349
                   EXEC_OUTPUT_KMSG,
1350
                   EXEC_OUTPUT_JOURNAL,
1351
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1352
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1353

1354
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
211✔
1355

1356
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
211✔
1357
                if (r >= 0)
211✔
1358
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
211✔
1359

1360
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
211✔
1361
                if (r >= 0)
211✔
1362
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
211✔
1363
        }
1364

1365
        if (c->log_level_max >= 0) {
228✔
1366
                _cleanup_free_ char *t = NULL;
1✔
1367

1368
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1369

1370
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1371
        }
1372

1373
        if (c->log_ratelimit.interval > 0)
228✔
1374
                fprintf(f,
×
1375
                        "%sLogRateLimitIntervalSec: %s\n",
1376
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1377

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

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

1384
                char *pattern;
×
1385
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1386
                        fprintf(f, " %s", pattern);
×
1387
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1388
                        fprintf(f, " ~%s", pattern);
×
1389
                fputc('\n', f);
×
1390
        }
1391

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

1398
        if (c->log_namespace)
228✔
1399
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1400

1401
        if (c->secure_bits) {
228✔
1402
                _cleanup_free_ char *str = NULL;
×
1403

1404
                r = secure_bits_to_string_alloc(c->secure_bits, &str);
×
1405
                if (r >= 0)
×
1406
                        fprintf(f, "%sSecure Bits: %s\n", prefix, str);
×
1407
        }
1408

1409
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
228✔
1410
                _cleanup_free_ char *str = NULL;
16✔
1411

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

1417
        if (c->capability_ambient_set != 0) {
228✔
1418
                _cleanup_free_ char *str = NULL;
×
1419

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

1425
        if (c->user)
228✔
1426
                fprintf(f, "%sUser: %s\n", prefix, c->user);
1✔
1427
        if (c->group)
228✔
1428
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1429

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

1432
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
228✔
1433

1434
        if (c->pam_name)
228✔
1435
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
×
1436

1437
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
228✔
1438
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
228✔
1439
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
228✔
1440
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
228✔
1441
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
228✔
1442
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
228✔
1443

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

1452
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
228✔
1453
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1454
                        tmpfs->path,
1455
                        isempty(tmpfs->options) ? "" : ":",
×
1456
                        strempty(tmpfs->options));
×
1457

1458
        if (c->utmp_id)
228✔
1459
                fprintf(f,
×
1460
                        "%sUtmpIdentifier: %s\n",
1461
                        prefix, c->utmp_id);
1462

1463
        if (c->selinux_context)
228✔
1464
                fprintf(f,
×
1465
                        "%sSELinuxContext: %s%s\n",
1466
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1467

1468
        if (c->apparmor_profile)
228✔
1469
                fprintf(f,
×
1470
                        "%sAppArmorProfile: %s%s\n",
1471
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1472

1473
        if (c->smack_process_label)
228✔
1474
                fprintf(f,
×
1475
                        "%sSmackProcessLabel: %s%s\n",
1476
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1477

1478
        if (c->personality != PERSONALITY_INVALID)
228✔
1479
                fprintf(f,
1✔
1480
                        "%sPersonality: %s\n",
1481
                        prefix, strna(personality_to_string(c->personality)));
1482

1483
        fprintf(f,
228✔
1484
                "%sLockPersonality: %s\n",
1485
                prefix, yes_no(c->lock_personality));
228✔
1486

1487
        if (c->syscall_filter) {
228✔
1488
                fprintf(f,
11✔
1489
                        "%sSystemCallFilter: ",
1490
                        prefix);
1491

1492
                if (!c->syscall_allow_list)
11✔
1493
                        fputc('~', f);
×
1494

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

1504
                                if (first)
4,268✔
1505
                                        first = false;
1506
                                else
1507
                                        fputc(' ', f);
4,257✔
1508

1509
                                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
4,268✔
1510
                                fputs(strna(name), f);
4,268✔
1511

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

1523
                fputc('\n', f);
11✔
1524
        }
1525

1526
        if (c->syscall_archs) {
228✔
1527
                fprintf(f,
11✔
1528
                        "%sSystemCallArchitectures:",
1529
                        prefix);
1530

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

1539
        if (exec_context_restrict_namespaces_set(c)) {
228✔
1540
                _cleanup_free_ char *s = NULL;
12✔
1541

1542
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
12✔
1543
                if (r >= 0)
12✔
1544
                        fprintf(f, "%sRestrictNamespaces: %s\n",
12✔
1545
                                prefix, strna(s));
1546
        }
1547

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

1556
        if (c->network_namespace_path)
228✔
1557
                fprintf(f,
×
1558
                        "%sNetworkNamespacePath: %s\n",
1559
                        prefix, c->network_namespace_path);
1560

1561
        if (c->syscall_errno > 0) {
228✔
1562
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
227✔
1563

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

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

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

1597
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
228✔
1598
}
228✔
1599

1600
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1601
        assert(c);
×
1602

1603
        /* Returns true if the process forked off would run under
1604
         * an unchanged UID or as root. */
1605

1606
        if (!c->user)
×
1607
                return true;
1608

1609
        if (STR_IN_SET(c->user, "root", "0"))
×
1610
                return true;
×
1611

1612
        return false;
×
1613
}
1614

1615
int exec_context_get_effective_ioprio(const ExecContext *c) {
2,610✔
1616
        int p;
2,610✔
1617

1618
        assert(c);
2,610✔
1619

1620
        if (c->ioprio_is_set)
2,610✔
1621
                return c->ioprio;
14✔
1622

1623
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
2,596✔
1624
        if (p < 0)
2,596✔
1625
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1626

1627
        return ioprio_normalize(p);
2,596✔
1628
}
1629

1630
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
34,819✔
1631
        assert(c);
34,819✔
1632

1633
        /* Explicit setting wins */
1634
        if (c->mount_apivfs >= 0)
34,819✔
1635
                return c->mount_apivfs > 0;
110✔
1636

1637
        /* Default to "yes" if root directory or image are specified */
1638
        if (exec_context_with_rootfs(c))
34,709✔
1639
                return true;
120✔
1640

1641
        return false;
1642
}
1643

1644
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
29,337✔
1645
        assert(c);
29,337✔
1646

1647
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1648
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1649
        if (c->log_namespace)
29,337✔
1650
                return true;
1651

1652
        if (c->bind_log_sockets >= 0)
29,329✔
1653
                return c->bind_log_sockets > 0;
2✔
1654

1655
        if (exec_context_get_effective_mount_apivfs(c))
29,327✔
1656
                return true;
1657

1658
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1659
        if (exec_context_with_rootfs(c) && c->private_devices)
29,234✔
1660
                return true;
×
1661

1662
        return false;
1663
}
1664

1665
void exec_context_free_log_extra_fields(ExecContext *c) {
47,699✔
1666
        assert(c);
47,699✔
1667

1668
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
47,704✔
1669
                free(field->iov_base);
5✔
1670

1671
        c->log_extra_fields = mfree(c->log_extra_fields);
47,699✔
1672
        c->n_log_extra_fields = 0;
47,699✔
1673
}
47,699✔
1674

1675
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
1,386✔
1676
        _cleanup_close_ int fd = -EBADF;
1,386✔
1677
        const char *path;
1,386✔
1678
        struct stat st;
1,386✔
1679
        int r;
1,386✔
1680

1681
        assert(c);
1,386✔
1682

1683
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1684
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
1,386✔
1685

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

1692
        path = exec_context_tty_path(c);
35✔
1693
        if (!path)
35✔
1694
                return;
1695

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

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

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

1713
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
35✔
1714
        if (r < 0)
35✔
1715
                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✔
1716
}
1717

1718
int exec_context_get_clean_directories(
×
1719
                ExecContext *c,
1720
                char **prefix,
1721
                ExecCleanMask mask,
1722
                char ***ret) {
1723

1724
        _cleanup_strv_free_ char **l = NULL;
×
1725
        int r;
×
1726

1727
        assert(c);
×
1728
        assert(prefix);
×
1729
        assert(ret);
×
1730

1731
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
×
1732
                if (!BIT_SET(mask, t))
×
1733
                        continue;
×
1734

1735
                if (!prefix[t])
×
1736
                        continue;
×
1737

1738
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
×
1739
                        char *j;
×
1740

1741
                        j = path_join(prefix[t], i->path);
×
1742
                        if (!j)
×
1743
                                return -ENOMEM;
1744

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

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

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

1760
                        STRV_FOREACH(symlink, i->symlinks) {
×
1761
                                j = path_join(prefix[t], *symlink);
×
1762
                                if (!j)
×
1763
                                        return -ENOMEM;
1764

1765
                                r = strv_consume(&l, j);
×
1766
                                if (r < 0)
×
1767
                                        return r;
1768
                        }
1769
                }
1770
        }
1771

1772
        *ret = TAKE_PTR(l);
×
1773
        return 0;
×
1774
}
1775

1776
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
1,289✔
1777
        ExecCleanMask mask = 0;
1,289✔
1778

1779
        assert(c);
1,289✔
1780
        assert(ret);
1,289✔
1781

1782
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
7,734✔
1783
                if (c->directories[t].n_items > 0)
6,445✔
1784
                        mask |= 1U << t;
254✔
1785

1786
        *ret = mask;
1,289✔
1787
        return 0;
1,289✔
1788
}
1789

1790
int exec_context_get_oom_score_adjust(const ExecContext *c) {
1,305✔
1791
        int n = 0, r;
1,305✔
1792

1793
        assert(c);
1,305✔
1794

1795
        if (c->oom_score_adjust_set)
1,305✔
1796
                return c->oom_score_adjust;
507✔
1797

1798
        r = get_oom_score_adjust(&n);
798✔
1799
        if (r < 0)
798✔
1800
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1801

1802
        return n;
798✔
1803
}
1804

1805
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
1,305✔
1806
        _cleanup_free_ char *t = NULL;
1,305✔
1807
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
1,305✔
1808
        int r;
1,305✔
1809

1810
        assert(c);
1,305✔
1811

1812
        if (c->coredump_filter_set)
1,305✔
1813
                return c->coredump_filter;
×
1814

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

1824
        return n;
1,305✔
1825
}
1826

1827
int exec_context_get_nice(const ExecContext *c) {
1,305✔
1828
        int n;
1,305✔
1829

1830
        assert(c);
1,305✔
1831

1832
        if (c->nice_set)
1,305✔
1833
                return c->nice;
4✔
1834

1835
        errno = 0;
1,301✔
1836
        n = getpriority(PRIO_PROCESS, 0);
1,301✔
1837
        if (errno > 0) {
1,301✔
1838
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1839
                n = 0;
1840
        }
1841

1842
        return n;
1843
}
1844

1845
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
1,305✔
1846
        int n;
1,305✔
1847

1848
        assert(c);
1,305✔
1849

1850
        if (c->cpu_sched_set)
1,305✔
1851
                return c->cpu_sched_policy;
×
1852

1853
        n = sched_getscheduler(0);
1,305✔
1854
        if (n < 0)
1,305✔
1855
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1856

1857
        return n < 0 ? SCHED_OTHER : n;
1,305✔
1858
}
1859

1860
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
1,305✔
1861
        struct sched_param p = {};
1,305✔
1862
        int r;
1,305✔
1863

1864
        assert(c);
1,305✔
1865

1866
        if (c->cpu_sched_set)
1,305✔
1867
                return c->cpu_sched_priority;
×
1868

1869
        r = sched_getparam(0, &p);
1,305✔
1870
        if (r < 0)
1,305✔
1871
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1872

1873
        return r >= 0 ? p.sched_priority : 0;
1,305✔
1874
}
1875

1876
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
1,305✔
1877
        int r;
1,305✔
1878

1879
        assert(c);
1,305✔
1880

1881
        if (c->timer_slack_nsec != NSEC_INFINITY)
1,305✔
1882
                return c->timer_slack_nsec;
1883

1884
        r = prctl(PR_GET_TIMERSLACK);
1,305✔
1885
        if (r < 0)
1,305✔
1886
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1887

1888
        return (uint64_t) MAX(r, 0);
1,305✔
1889
}
1890

1891
bool exec_context_get_set_login_environment(const ExecContext *c) {
11,109✔
1892
        assert(c);
11,109✔
1893

1894
        if (c->set_login_environment >= 0)
11,109✔
1895
                return c->set_login_environment;
×
1896

1897
        return c->user || c->dynamic_user || c->pam_name;
20,041✔
1898
}
1899

1900
char** exec_context_get_syscall_filter(const ExecContext *c) {
1,305✔
1901
        _cleanup_strv_free_ char **l = NULL;
1,305✔
1902

1903
        assert(c);
1,305✔
1904

1905
#if HAVE_SECCOMP
1906
        if (dlopen_libseccomp() < 0)
1,305✔
1907
                return strv_new(NULL);
×
1908

1909
        void *id, *val;
1,305✔
1910
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
11,832✔
1911
                _cleanup_free_ char *name = NULL;
10,527✔
1912
                const char *e = NULL;
10,527✔
1913
                char *s;
10,527✔
1914
                int num = PTR_TO_INT(val);
10,527✔
1915

1916
                if (c->syscall_allow_list && num >= 0)
10,527✔
1917
                        /* syscall with num >= 0 in allow-list is denied. */
1918
                        continue;
×
1919

1920
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
10,527✔
1921
                if (!name)
10,527✔
1922
                        continue;
×
1923

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

1937
                if (strv_consume(&l, s) < 0)
10,527✔
1938
                        return NULL;
1939
        }
1940

1941
        strv_sort(l);
1,305✔
1942
#endif
1943

1944
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,305✔
1945
}
1946

1947
char** exec_context_get_syscall_archs(const ExecContext *c) {
1,305✔
1948
        _cleanup_strv_free_ char **l = NULL;
1,305✔
1949

1950
        assert(c);
1,305✔
1951

1952
#if HAVE_SECCOMP
1953
        void *id;
1,305✔
1954
        SET_FOREACH(id, c->syscall_archs) {
1,336✔
1955
                const char *name;
31✔
1956

1957
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
31✔
1958
                if (!name)
31✔
1959
                        continue;
×
1960

1961
                if (strv_extend(&l, name) < 0)
31✔
1962
                        return NULL;
×
1963
        }
1964

1965
        strv_sort(l);
1,305✔
1966
#endif
1967

1968
        return l ? TAKE_PTR(l) : strv_new(NULL);
1,305✔
1969
}
1970

1971
char** exec_context_get_syscall_log(const ExecContext *c) {
1,305✔
1972
        _cleanup_strv_free_ char **l = NULL;
1,305✔
1973

1974
        assert(c);
1,305✔
1975

1976
#if HAVE_SECCOMP
1977
        if (dlopen_libseccomp() < 0)
1,305✔
1978
                return strv_new(NULL);
×
1979

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

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

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

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

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

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

2002
        assert(c);
1,305✔
2003

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

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

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

2015
        strv_sort(l);
1,305✔
2016

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

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

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

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

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

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

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

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

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

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

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

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

2058
        assert(context);
4✔
2059

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

2071
        return 0;
2072
}
2073

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2130
        prefix = strempty(prefix);
10✔
2131

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

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

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

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

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

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

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

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

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

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

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

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

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

2199
        prefix = strempty(prefix);
95✔
2200

2201
        LIST_FOREACH(command, i, c)
193✔
2202
                exec_command_dump(i, f, prefix);
98✔
2203
}
95✔
2204

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2263
        return 0;
2264
}
2265

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2323
        assert(ret);
94,443✔
2324

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

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

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

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

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

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

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

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

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

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

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

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

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

2382
        rt->manager = m;
150✔
2383

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

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

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

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

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

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

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

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

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

2432
        return 1;
2433
}
2434

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2529
        return 0;
63✔
2530
}
2531

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

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

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

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

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

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

2563
                rt = rt_create;
94,293✔
2564
        }
2565

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

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

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

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

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

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

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

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

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

2604
        return 1;
2605
}
2606

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2712
        assert(m);
1,496✔
2713

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2858
        exec_params_shallow_clear(p);
30✔
2859
}
2860

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

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

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

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

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

2883
        return NULL;
2884
}
2885

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

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

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

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

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

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

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

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

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

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

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

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

2935
void exec_directory_sort(ExecDirectory *d) {
130,646✔
2936
        assert(d);
130,646✔
2937

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

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

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

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

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

2959
        assert(s);
×
2960

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

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

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

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

2984
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
16,045✔
2985

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

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

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

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

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

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

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

3028
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3029

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

3038
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3039

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

3051
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
258✔
3052

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

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