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

systemd / systemd / 28066554192

23 Jun 2026 10:04PM UTC coverage: 72.703% (-0.3%) from 72.966%
28066554192

push

github

bluca
test: skip fdstore tests if test-fdstore is not available

When the test suite is run in the "standalone" mode, the minimal
container might not contain the test-fdstore binary that's needed for a
couple of tests. Since installing systemd-tests into the minimal
container pulls in a lot of other dependencies, let's just skip the
affected tests instead to avoid this.

339059 of 466364 relevant lines covered (72.7%)

1294926.64 hits per line

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

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

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

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

70
const char* exec_context_tty_path(const ExecContext *context) {
16,733✔
71
        assert(context);
16,733✔
72

73
        if (context->stdio_as_fds)
16,733✔
74
                return NULL;
75

76
        if (context->tty_path)
15,786✔
77
                return context->tty_path;
771✔
78

79
        return "/dev/console";
80
}
81

82
int exec_context_apply_tty_size(
1,168✔
83
                const ExecContext *context,
84
                int input_fd,
85
                int output_fd,
86
                const char *tty_path) {
87

88
        unsigned rows, cols;
1,168✔
89
        int r;
1,168✔
90

91
        assert(context);
1,168✔
92
        assert(input_fd >= 0);
1,168✔
93
        assert(output_fd >= 0);
1,168✔
94

95
        if (!isatty_safe(output_fd))
1,168✔
96
                return 0;
1,168✔
97

98
        if (!tty_path)
558✔
99
                tty_path = exec_context_tty_path(context);
285✔
100

101
        /* Preferably use explicitly configured data */
102
        rows = context->tty_rows;
558✔
103
        cols = context->tty_cols;
558✔
104

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

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

124
        return terminal_set_size_fd(output_fd, tty_path, rows, cols);
558✔
125
}
126

127
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *parameters, sd_id128_t invocation_id) {
15,264✔
128
        _cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
30,528✔
129
        int fd, r;
15,264✔
130

131
        assert(context);
15,264✔
132

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

138
        const char *path = exec_context_tty_path(context);
15,264✔
139

140
        if (parameters && parameters->stdout_fd >= 0 && isatty_safe(parameters->stdout_fd))
15,264✔
141
                fd = parameters->stdout_fd;
26✔
142
        else if (path && exec_context_has_tty(context)) {
15,238✔
143
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
273✔
144
                if (fd < 0)
273✔
145
                        return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path);
14,965✔
146
        } else
147
                return;   /* nothing to do */
148

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

160
        if (context->tty_reset)
299✔
161
                (void) terminal_reset_defensive(
236✔
162
                                fd,
163
                                TERMINAL_RESET_SWITCH_TO_TEXT |
164
                                (exec_context_shall_ansi_seq_reset(context) ? TERMINAL_RESET_FORCE_ANSI_SEQ : TERMINAL_RESET_AVOID_ANSI_SEQ));
232✔
165

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

170
        if (!sd_id128_is_null(invocation_id) && exec_context_shall_ansi_seq_reset(context)) {
559✔
171
                sd_id128_t context_id;
24✔
172

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

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

187
        if (context->tty_vhangup)
299✔
188
                (void) terminal_vhangup_fd(fd);
109✔
189

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

194
        if (context->tty_vt_disallocate && path)
299✔
195
                (void) vt_disallocate(path);
59✔
196
}
197

198
bool exec_needs_network_namespace(const ExecContext *context) {
90,755✔
199
        assert(context);
90,755✔
200

201
        return context->private_network || context->network_namespace_path;
90,755✔
202
}
203

204
static bool exec_needs_ephemeral(const ExecContext *context) {
22,030✔
205
        return (context->root_image || context->root_directory) && context->root_ephemeral;
22,030✔
206
}
207

208
bool exec_needs_ipc_namespace(const ExecContext *context) {
84,273✔
209
        assert(context);
84,273✔
210

211
        return context->private_ipc || context->ipc_namespace_path;
84,273✔
212
}
213

214
static bool needs_cgroup_namespace(ProtectControlGroups i) {
164,360✔
215
        return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
164,360✔
216
}
217

218
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
103,031✔
219
        assert(context);
103,031✔
220

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

234
bool exec_needs_cgroup_namespace(const ExecContext *context) {
61,329✔
235
        assert(context);
61,329✔
236

237
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
61,329✔
238
}
239

240
bool exec_needs_cgroup_mount(const ExecContext *context) {
37,373✔
241
        assert(context);
37,373✔
242

243
        return exec_get_protect_control_groups(context) != PROTECT_CONTROL_GROUPS_NO;
37,373✔
244
}
245

246
bool exec_is_cgroup_mount_read_only(const ExecContext *context) {
2,165✔
247
        assert(context);
2,165✔
248

249
        return IN_SET(exec_get_protect_control_groups(context), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
2,165✔
250
}
251

252
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
137,435✔
253
        assert(context);
137,435✔
254

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

259
        return context->private_pids != PRIVATE_PIDS_NO && namespace_type_supported(NAMESPACE_PID);
124,127✔
260
}
261

262
bool exec_needs_mount_namespace(const ExecContext *context, const ExecParameters *params) {
43,079✔
263
        assert(context);
43,079✔
264

265
        if (context->root_image ||
43,079✔
266
            context->root_mstack)
42,984✔
267
                return true;
268

269
        if (context->root_directory_as_fd)
42,964✔
270
                return true;
271

272
        if (!strv_isempty(context->read_write_paths) ||
42,958✔
273
            !strv_isempty(context->read_only_paths) ||
40,333✔
274
            !strv_isempty(context->inaccessible_paths) ||
40,317✔
275
            !strv_isempty(context->exec_paths) ||
40,284✔
276
            !strv_isempty(context->no_exec_paths))
40,284✔
277
                return true;
278

279
        if (context->n_bind_mounts > 0)
40,284✔
280
                return true;
281

282
        if (context->n_temporary_filesystems > 0)
40,216✔
283
                return true;
284

285
        if (context->n_mount_images > 0)
40,057✔
286
                return true;
287

288
        if (context->n_extension_images > 0)
40,028✔
289
                return true;
290

291
        if (!strv_isempty(context->extension_directories))
40,003✔
292
                return true;
293

294
        if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
39,998✔
295
                return true;
296

297
        if (context->private_devices ||
39,998✔
298
            context->private_tmp != PRIVATE_TMP_NO || /* no need to check for private_var_tmp here, private_tmp is never demoted to "no" */
38,439✔
299
            context->private_mounts > 0 ||
38,341✔
300
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
37,702✔
301
            context->protect_system != PROTECT_SYSTEM_NO ||
37,663✔
302
            context->protect_home != PROTECT_HOME_NO ||
37,663✔
303
            context->protect_kernel_tunables ||
37,663✔
304
            context->protect_kernel_modules ||
37,663✔
305
            context->protect_kernel_logs ||
71,791✔
306
            exec_needs_cgroup_mount(context) ||
35,894✔
307
            context->protect_proc != PROTECT_PROC_DEFAULT ||
35,835✔
308
            context->proc_subset != PROC_SUBSET_ALL ||
35,749✔
309
            context->private_bpf != PRIVATE_BPF_NO ||
71,480✔
310
            exec_needs_ipc_namespace(context) ||
71,480✔
311
            exec_needs_pid_namespace(context, params))
35,740✔
312
                return true;
313

314
        if (context->root_directory) {
35,659✔
315
                if (exec_context_get_effective_mount_apivfs(context))
22✔
316
                        return true;
317

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

322
                        if (context->directories[t].n_items > 0)
×
323
                                return true;
324
                }
325
        }
326

327
        if (context->dynamic_user &&
35,637✔
328
            (context->directories[EXEC_DIRECTORY_STATE].n_items > 0 ||
×
329
             context->directories[EXEC_DIRECTORY_CACHE].n_items > 0 ||
×
330
             context->directories[EXEC_DIRECTORY_LOGS].n_items > 0))
×
331
                return true;
332

333
        if (exec_context_get_effective_bind_log_sockets(context))
35,637✔
334
                return true;
335

336
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
213,430✔
337
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
183,070✔
338
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,271✔
339
                                return true;
340

341
        return false;
342
}
343

344
const char* exec_get_private_notify_socket_path(const ExecContext *context, const ExecParameters *params, bool needs_sandboxing) {
4,201✔
345
        assert(context);
4,201✔
346
        assert(params);
4,201✔
347

348
        if (!params->notify_socket)
4,201✔
349
                return NULL;
350

351
        if (!needs_sandboxing)
3,429✔
352
                return NULL;
353

354
        if (!exec_context_with_rootfs(context))
3,429✔
355
                return NULL;
356

357
        if (!exec_context_get_effective_mount_apivfs(context))
×
358
                return NULL;
359

360
        if (!FLAGS_SET(params->flags, EXEC_APPLY_CHROOT))
×
361
                return NULL;
×
362

363
        return "/run/host/notify";
364
}
365

366
int exec_log_level_max_with_exec_params(const ExecContext *context, const ExecParameters *params) {
24,632✔
367
        assert(params);
24,632✔
368

369
        if (params->debug_invocation)
24,632✔
370
                return LOG_DEBUG;
371

372
        return exec_log_level_max(context);
24,628✔
373
}
374

375
int exec_log_level_max(const ExecContext *context) {
26,507✔
376
        assert(context);
26,507✔
377
        return context->log_level_max < 0 ? log_get_max_level() : context->log_level_max;
26,507✔
378
}
379

380
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
10,423✔
381
        assert(context);
10,423✔
382

383
        if (!context->dynamic_user)
10,423✔
384
                return false;
385

386
        if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
107✔
387
                return false;
388

389
        if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
101✔
390
                return false;
17✔
391

392
        return true;
393
}
394

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

400
int exec_params_get_cgroup_path(
13,098✔
401
                const ExecParameters *params,
402
                const CGroupContext *c,
403
                const char *prefix,
404
                char **ret) {
405

406
        const char *subgroup = NULL;
13,098✔
407
        char *p;
13,098✔
408

409
        assert(params);
13,098✔
410
        assert(c);
13,098✔
411
        assert(ret);
13,098✔
412

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

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

430
        if (subgroup)
724✔
431
                p = path_join(prefix, subgroup);
787✔
432
        else
433
                p = strdup(strempty(prefix));
12,319✔
434
        if (!p)
13,098✔
435
                return -ENOMEM;
436

437
        *ret = p;
13,098✔
438
        return !!subgroup;
13,098✔
439
}
440

441
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) {
7,943✔
442
        assert(c);
7,943✔
443

444
        return c->cpu_affinity_from_numa;
7,943✔
445
}
446

447
static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
4,560✔
448
        assert(unit);
4,560✔
449
        assert(msg);
4,560✔
450
        assert(executable);
4,560✔
451

452
        if (!DEBUG_LOGGING)
4,560✔
453
                return;
4,560✔
454

455
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
9,120✔
456

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

463
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret);
464

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

474
        _cleanup_free_ char *subcgroup_path = NULL, *max_log_levels = NULL;
4,560✔
475
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
476
        _cleanup_fclose_ FILE *f = NULL;
4,560✔
477
        int r;
4,560✔
478

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

490
        LOG_CONTEXT_PUSH_UNIT(unit);
9,120✔
491

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

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

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

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

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

521
                cgtarget = subcgroup_path;
9✔
522
        } else
523
                cgtarget = params->cgroup_path;
4,551✔
524

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

616
void exec_context_init(ExecContext *c) {
118,698✔
617
        assert(c);
118,698✔
618

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

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

649
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
712,188✔
650
                d->mode = 0755;
593,490✔
651

652
        numa_policy_reset(&c->numa_policy);
118,698✔
653

654
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
118,698✔
655
}
118,698✔
656

657
void exec_context_done(ExecContext *c) {
106,417✔
658
        assert(c);
106,417✔
659

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

665
        rlimit_free_all(c->rlimit);
106,417✔
666

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

672
        c->working_directory = mfree(c->working_directory);
106,417✔
673
        c->root_directory = mfree(c->root_directory);
106,417✔
674
        c->root_image = mfree(c->root_image);
106,417✔
675
        c->root_image_options = mount_options_free_all(c->root_image_options);
106,417✔
676
        iovec_done(&c->root_hash);
106,417✔
677
        c->root_hash_path = mfree(c->root_hash_path);
106,417✔
678
        iovec_done(&c->root_hash_sig);
106,417✔
679
        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
106,417✔
680
        c->root_verity = mfree(c->root_verity);
106,417✔
681
        c->root_mstack = mfree(c->root_mstack);
106,417✔
682
        c->tty_path = mfree(c->tty_path);
106,417✔
683
        c->syslog_identifier = mfree(c->syslog_identifier);
106,417✔
684
        c->user = mfree(c->user);
106,417✔
685
        c->group = mfree(c->group);
106,417✔
686

687
        c->supplementary_groups = strv_free(c->supplementary_groups);
106,417✔
688

689
        c->pam_name = mfree(c->pam_name);
106,417✔
690

691
        c->read_only_paths = strv_free(c->read_only_paths);
106,417✔
692
        c->read_write_paths = strv_free(c->read_write_paths);
106,417✔
693
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
106,417✔
694
        c->exec_paths = strv_free(c->exec_paths);
106,417✔
695
        c->no_exec_paths = strv_free(c->no_exec_paths);
106,417✔
696
        c->exec_search_path = strv_free(c->exec_search_path);
106,417✔
697

698
        bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
106,417✔
699
        c->bind_mounts = NULL;
106,417✔
700
        c->n_bind_mounts = 0;
106,417✔
701
        mount_image_free_array(c->mount_images, c->n_mount_images);
106,417✔
702
        c->mount_images = NULL;
106,417✔
703
        c->n_mount_images = 0;
106,417✔
704
        mount_image_free_array(c->extension_images, c->n_extension_images);
106,417✔
705
        c->extension_images = NULL;
106,417✔
706
        c->n_extension_images = 0;
106,417✔
707
        c->extension_directories = strv_free(c->extension_directories);
106,417✔
708
        temporary_filesystem_free_many(c->temporary_filesystems, c->n_temporary_filesystems);
106,417✔
709
        c->temporary_filesystems = NULL;
106,417✔
710
        c->n_temporary_filesystems = 0;
106,417✔
711

712
        cpu_set_done(&c->cpu_set);
106,417✔
713
        numa_policy_reset(&c->numa_policy);
106,417✔
714

715
        c->utmp_id = mfree(c->utmp_id);
106,417✔
716
        c->selinux_context = mfree(c->selinux_context);
106,417✔
717
        c->apparmor_profile = mfree(c->apparmor_profile);
106,417✔
718
        c->smack_process_label = mfree(c->smack_process_label);
106,417✔
719

720
        c->restrict_filesystems = set_free(c->restrict_filesystems);
106,417✔
721

722
        c->syscall_filter = hashmap_free(c->syscall_filter);
106,417✔
723
        c->syscall_archs = set_free(c->syscall_archs);
106,417✔
724
        c->syscall_log = hashmap_free(c->syscall_log);
106,417✔
725
        c->address_families = set_free(c->address_families);
106,417✔
726

727
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
638,502✔
728
                exec_directory_done(d);
532,085✔
729

730
        c->log_level_max = -1;
106,417✔
731

732
        exec_context_free_log_extra_fields(c);
106,417✔
733
        c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
106,417✔
734
        c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
106,417✔
735

736
        c->log_ratelimit = (RateLimit) {};
106,417✔
737

738
        c->stdin_data = mfree(c->stdin_data);
106,417✔
739
        c->stdin_data_size = 0;
106,417✔
740

741
        c->user_namespace_path = mfree(c->user_namespace_path);
106,417✔
742
        c->network_namespace_path = mfree(c->network_namespace_path);
106,417✔
743
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
106,417✔
744

745
        c->log_namespace = mfree(c->log_namespace);
106,417✔
746

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

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

755
        c->private_hostname = mfree(c->private_hostname);
106,417✔
756
}
106,417✔
757

758
int exec_context_apply_environment(
260✔
759
                Unit *u,
760
                ExecContext *c,
761
                char **env,
762
                UnitWriteFlags flags) {
763

764
        assert(u);
260✔
765
        assert(c);
260✔
766

767
        if (strv_length(env) > ENVIRONMENT_ASSIGNMENTS_MAX)
260✔
768
                return -E2BIG;
769
        if (!strv_env_is_valid(env))
260✔
770
                return -EINVAL;
771

772
        if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
259✔
773
                if (strv_isempty(env)) {
130✔
774
                        c->environment = strv_free(c->environment);
×
775
                        unit_write_setting(u, flags, "Environment", "Environment=");
×
776
                } else {
777
                        _cleanup_free_ char *joined = unit_concat_strv(env, UNIT_ESCAPE_SPECIFIERS|UNIT_ESCAPE_C);
260✔
778
                        if (!joined)
130✔
779
                                return -ENOMEM;
780

781
                        char **e = strv_env_merge(c->environment, env);
130✔
782
                        if (!e)
130✔
783
                                return -ENOMEM;
784

785
                        strv_free_and_replace(c->environment, e);
130✔
786
                        unit_write_settingf(u, flags, "Environment", "Environment=%s", joined);
130✔
787
                }
788
        }
789

790
        return 0;
791
}
792

793
int exec_context_apply_set_credential(
20✔
794
                Unit *u,
795
                ExecContext *c,
796
                const char *id,
797
                const void *data,
798
                size_t size,
799
                bool encrypted,
800
                UnitWriteFlags flags,
801
                const char **reterr_message) {
802

803
        int r;
20✔
804

805
        assert(u);
20✔
806
        assert(c);
20✔
807
        assert(id);
20✔
808
        assert(data || size == 0);
20✔
809

810
        if (!credential_name_valid(id)) {
20✔
811
                if (reterr_message)
1✔
812
                        *reterr_message = "Credential ID is invalid";
1✔
813
                return -EINVAL;
20✔
814
        }
815

816
        if (UNIT_WRITE_FLAGS_NOOP(flags))
19✔
817
                return 0;
818

819
        _cleanup_free_ void *copy = memdup(data, size);
20✔
820
        if (!copy)
10✔
821
                return -ENOMEM;
822

823
        _cleanup_free_ char *escaped_id = specifier_escape(id);
20✔
824
        if (!escaped_id)
10✔
825
                return -ENOMEM;
826

827
        _cleanup_free_ char *escaped_value = cescape_length(data, size);
20✔
828
        if (!escaped_value)
10✔
829
                return -ENOMEM;
830

831
        r = exec_context_put_set_credential(c, id, TAKE_PTR(copy), size, encrypted);
10✔
832
        if (r < 0)
10✔
833
                return r;
834

835
        const char *name = encrypted ? "SetCredentialEncrypted" : "SetCredential";
10✔
836
        unit_write_settingf(u, flags, name, "%s=%s:%s", name, escaped_id, escaped_value);
10✔
837
        return 0;
838
}
839

840
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
8,458✔
841
        assert(c);
8,458✔
842

843
        if (!runtime_prefix)
8,458✔
844
                return 0;
845

846
        FOREACH_ARRAY(i, c->directories[EXEC_DIRECTORY_RUNTIME].items, c->directories[EXEC_DIRECTORY_RUNTIME].n_items) {
8,476✔
847
                _cleanup_free_ char *p = NULL;
18✔
848

849
                if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
18✔
850
                        p = path_join(runtime_prefix, "private", i->path);
×
851
                else
852
                        p = path_join(runtime_prefix, i->path);
18✔
853
                if (!p)
18✔
854
                        return -ENOMEM;
855

856
                /* We execute this synchronously, since we need to be sure this is gone when we start the
857
                 * service next. */
858
                (void) rm_rf(p, REMOVE_ROOT);
18✔
859

860
                STRV_FOREACH(symlink, i->symlinks) {
18✔
861
                        _cleanup_free_ char *symlink_abs = NULL;
×
862

863
                        if (exec_directory_is_private(c, EXEC_DIRECTORY_RUNTIME))
×
864
                                symlink_abs = path_join(runtime_prefix, "private", *symlink);
×
865
                        else
866
                                symlink_abs = path_join(runtime_prefix, *symlink);
×
867
                        if (!symlink_abs)
×
868
                                return -ENOMEM;
×
869

870
                        (void) unlink(symlink_abs);
×
871
                }
872
        }
873

874
        return 0;
875
}
876

877
int exec_context_destroy_mount_ns_dir(Unit *u) {
16,435✔
878
        _cleanup_free_ char *p = NULL;
16,435✔
879

880
        if (!u || !MANAGER_IS_SYSTEM(u->manager))
16,435✔
881
                return 0;
882

883
        p = path_join("/run/systemd/propagate/", u->id);
4,409✔
884
        if (!p)
4,409✔
885
                return -ENOMEM;
886

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

891
        return 0;
892
}
893

894
void exec_command_done(ExecCommand *c) {
206,490✔
895
        assert(c);
206,490✔
896

897
        c->path = mfree(c->path);
206,490✔
898
        c->argv = strv_free(c->argv);
206,490✔
899
}
206,490✔
900

901
void exec_command_done_array(ExecCommand *c, size_t n) {
55,578✔
902
        FOREACH_ARRAY(i, c, n)
222,310✔
903
                exec_command_done(i);
166,732✔
904
}
55,578✔
905

906
ExecCommand* exec_command_free(ExecCommand *c) {
39,724✔
907
        if (!c)
39,724✔
908
                return NULL;
909

910
        exec_command_done(c);
39,724✔
911
        return mfree(c);
39,724✔
912
}
913

914
ExecCommand* exec_command_free_list(ExecCommand *c) {
361,086✔
915
        ExecCommand *i;
361,086✔
916

917
        while ((i = LIST_POP(command, c)))
400,810✔
918
                exec_command_free(i);
39,724✔
919

920
        return NULL;
361,086✔
921
}
922

923
void exec_command_free_array(ExecCommand **c, size_t n) {
50,804✔
924
        FOREACH_ARRAY(i, c, n)
410,949✔
925
                *i = exec_command_free_list(*i);
360,145✔
926
}
50,804✔
927

928
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
8,987✔
929
        FOREACH_ARRAY(i, c, n)
35,947✔
930
                exec_status_reset(&i->exec_status);
26,960✔
931
}
8,987✔
932

933
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
7,929✔
934
        FOREACH_ARRAY(i, c, n)
58,083✔
935
                LIST_FOREACH(command, z, *i)
54,720✔
936
                        exec_status_reset(&z->exec_status);
4,566✔
937
}
7,929✔
938

939
typedef struct InvalidEnvInfo {
940
        const Unit *unit;
941
        const char *path;
942
} InvalidEnvInfo;
943

944
static void invalid_env(const char *p, void *userdata) {
×
945
        InvalidEnvInfo *info = userdata;
×
946

947
        log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
×
948
}
×
949

950
const char* exec_context_fdname(const ExecContext *c, int fd_index) {
60,777✔
951
        assert(c);
60,777✔
952

953
        switch (fd_index) {
60,777✔
954

955
        case STDIN_FILENO:
20,259✔
956
                if (c->std_input != EXEC_INPUT_NAMED_FD)
20,259✔
957
                        return NULL;
958

959
                return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
×
960

961
        case STDOUT_FILENO:
20,259✔
962
                if (c->std_output != EXEC_OUTPUT_NAMED_FD)
20,259✔
963
                        return NULL;
964

965
                return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
×
966

967
        case STDERR_FILENO:
20,259✔
968
                if (c->std_error != EXEC_OUTPUT_NAMED_FD)
20,259✔
969
                        return NULL;
970

971
                return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
×
972

973
        default:
974
                return NULL;
975
        }
976
}
977

978
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
4,560✔
979
        _cleanup_strv_free_ char **v = NULL;
4,560✔
980
        int r;
4,560✔
981

982
        assert(c);
4,560✔
983
        assert(ret);
4,560✔
984

985
        STRV_FOREACH(i, c->environment_files) {
4,564✔
986
                _cleanup_strv_free_ char **paths = NULL;
4✔
987
                bool ignore = false;
4✔
988
                char *fn = *i;
4✔
989

990
                if (fn[0] == '-') {
4✔
991
                        ignore = true;
3✔
992
                        fn++;
3✔
993
                }
994

995
                if (!path_is_absolute(fn)) {
4✔
996
                        if (ignore)
×
997
                                continue;
×
998
                        return -EINVAL;
999
                }
1000

1001
                /* Filename supports globbing, take all matching files */
1002
                r = safe_glob(fn, /* flags= */ 0, &paths);
4✔
1003
                if (r < 0) {
4✔
1004
                        if (ignore)
3✔
1005
                                continue;
3✔
1006
                        return r;
1007
                }
1008

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

1012
                STRV_FOREACH(path, paths) {
2✔
1013
                        _cleanup_strv_free_ char **p = NULL;
1✔
1014

1015
                        r = load_env_file(NULL, *path, &p);
1✔
1016
                        if (r < 0) {
1✔
1017
                                if (ignore)
×
1018
                                        continue;
×
1019
                                return r;
1020
                        }
1021

1022
                        /* Log invalid environment variables with filename */
1023
                        if (p) {
1✔
1024
                                InvalidEnvInfo info = {
1✔
1025
                                        .unit = unit,
1026
                                        .path = *path,
1✔
1027
                                };
1028

1029
                                strv_env_clean_with_callback(p, invalid_env, &info);
1✔
1030
                        }
1031

1032
                        if (!v)
1✔
1033
                                v = TAKE_PTR(p);
1✔
1034
                        else {
1035
                                char **m = strv_env_merge(v, p);
×
1036
                                if (!m)
×
1037
                                        return -ENOMEM;
×
1038

1039
                                strv_free_and_replace(v, m);
×
1040
                        }
1041
                }
1042
        }
1043

1044
        *ret = TAKE_PTR(v);
4,560✔
1045

1046
        return 0;
4,560✔
1047
}
1048

1049
static bool tty_may_match_dev_console(const char *tty) {
674✔
1050
        _cleanup_free_ char *resolved = NULL;
674✔
1051

1052
        if (!tty)
674✔
1053
                return true;
1054

1055
        tty = skip_dev_prefix(tty);
674✔
1056

1057
        /* trivial identity? */
1058
        if (streq(tty, "console"))
674✔
1059
                return true;
1060

1061
        if (resolve_dev_console(&resolved) < 0)
48✔
1062
                return true; /* if we could not resolve, assume it may */
1063

1064
        /* "tty0" means the active VC, so it may be the same sometimes */
1065
        return path_equal(skip_dev_prefix(resolved), tty) || (streq(skip_dev_prefix(resolved), "tty0") && tty_is_vc(tty));
48✔
1066
}
1067

1068
static bool exec_context_may_touch_tty(const ExecContext *ec) {
49,241✔
1069
        assert(ec);
49,241✔
1070

1071
        return ec->tty_reset ||
97,893✔
1072
                ec->tty_vhangup ||
48,652✔
1073
                ec->tty_vt_disallocate ||
48,652✔
1074
                exec_input_is_terminal(ec->std_input) ||
48,652✔
1075
                ec->std_output == EXEC_OUTPUT_TTY ||
97,893✔
1076
                ec->std_error == EXEC_OUTPUT_TTY;
48,528✔
1077
}
1078

1079
bool exec_context_may_touch_console(const ExecContext *ec) {
46,292✔
1080

1081
        return exec_context_may_touch_tty(ec) &&
46,966✔
1082
               tty_may_match_dev_console(exec_context_tty_path(ec));
674✔
1083
}
1084

1085
bool exec_context_shall_ansi_seq_reset(const ExecContext *c) {
1,565✔
1086
        assert(c);
1,565✔
1087

1088
        /* Determines whether ANSI sequences shall be used during any terminal initialisation:
1089
         *
1090
         * 1. If the reset logic is enabled at all, this is an immediate no.
1091
         *
1092
         * 2. If $TERM is set to anything other than "dumb", it's a yes.
1093
         */
1094

1095
        if (!c->tty_reset)
1,565✔
1096
                return false;
1097

1098
        /* FIXME:
1099
         * On invocation, we generate $TERM based on settings for StandardOutput= and friends and the kernel
1100
         * command line options, or propagate $TERM from the service manager. See setup_term_environment(). */
1101
        return !streq_ptr(strv_env_get(c->environment, "TERM"), "dumb");
771✔
1102
}
1103

1104
static void strv_fprintf(FILE *f, char **l) {
2✔
1105
        assert(f);
2✔
1106

1107
        STRV_FOREACH(g, l)
6✔
1108
                fprintf(f, " %s", *g);
4✔
1109
}
2✔
1110

1111
static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv) {
3,480✔
1112
        assert(f);
3,480✔
1113
        assert(prefix);
3,480✔
1114
        assert(name);
3,480✔
1115

1116
        if (!strv_isempty(strv)) {
3,480✔
1117
                fprintf(f, "%s%s:", prefix, name);
2✔
1118
                strv_fprintf(f, strv);
2✔
1119
                fputs("\n", f);
2✔
1120
        }
1121
}
3,480✔
1122

1123
void exec_params_dump(const ExecParameters *p, FILE* f, const char *prefix) {
×
1124
        assert(p);
×
1125
        assert(f);
×
1126

1127
        prefix = strempty(prefix);
×
1128

1129
        fprintf(f,
×
1130
                "%sRuntimeScope: %s\n"
1131
                "%sExecFlags: %u\n"
1132
                "%sSELinuxContextNetwork: %s\n"
1133
                "%sCgroupPath: %s\n"
1134
                "%sCrededentialsDirectory: %s\n"
1135
                "%sEncryptedCredentialsDirectory: %s\n"
1136
                "%sConfirmSpawn: %s\n"
1137
                "%sShallConfirmSpawn: %s\n"
1138
                "%sWatchdogUSec: " USEC_FMT "\n"
1139
                "%sNotifySocket: %s\n"
1140
                "%sDebugInvocation: %s\n"
1141
                "%sFallbackSmackProcessLabel: %s\n",
1142
                prefix, runtime_scope_to_string(p->runtime_scope),
×
1143
                prefix, p->flags,
×
1144
                prefix, yes_no(p->selinux_context_net),
×
1145
                prefix, p->cgroup_path,
×
1146
                prefix, strempty(p->received_credentials_directory),
×
1147
                prefix, strempty(p->received_encrypted_credentials_directory),
×
1148
                prefix, strempty(p->confirm_spawn),
×
1149
                prefix, yes_no(p->shall_confirm_spawn),
×
1150
                prefix, p->watchdog_usec,
×
1151
                prefix, strempty(p->notify_socket),
×
1152
                prefix, yes_no(p->debug_invocation),
×
1153
                prefix, strempty(p->fallback_smack_process_label));
×
1154

1155
        strv_dump(f, prefix, "FdNames", p->fd_names);
×
1156
        strv_dump(f, prefix, "Environment", p->environment);
×
1157
        strv_dump(f, prefix, "Prefix", p->prefix);
×
1158

1159
        LIST_FOREACH(open_files, file, p->open_files)
×
1160
                fprintf(f, "%sOpenFile: %s %s", prefix, file->path, open_file_flags_to_string(file->flags));
×
1161

1162
        strv_dump(f, prefix, "FilesEnv", p->files_env);
×
1163
}
×
1164

1165
void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
435✔
1166
        int r;
435✔
1167

1168
        assert(c);
435✔
1169
        assert(f);
435✔
1170

1171
        prefix = strempty(prefix);
435✔
1172

1173
        fprintf(f,
1,303✔
1174
                "%sUMask: %04o\n"
1175
                "%sWorkingDirectory: %s\n"
1176
                "%sRootDirectory: %s\n"
1177
                "%sRootEphemeral: %s\n"
1178
                "%sNonBlocking: %s\n"
1179
                "%sPrivateTmp: %s\n"
1180
                "%sPrivateDevices: %s\n"
1181
                "%sProtectKernelTunables: %s\n"
1182
                "%sProtectKernelModules: %s\n"
1183
                "%sProtectKernelLogs: %s\n"
1184
                "%sProtectClock: %s\n"
1185
                "%sProtectControlGroups: %s\n"
1186
                "%sPrivateNetwork: %s\n"
1187
                "%sPrivateUsers: %s\n"
1188
                "%sPrivatePIDs: %s\n"
1189
                "%sProtectHome: %s\n"
1190
                "%sProtectSystem: %s\n"
1191
                "%sMountAPIVFS: %s\n"
1192
                "%sBindLogSockets: %s\n"
1193
                "%sIgnoreSIGPIPE: %s\n"
1194
                "%sMemoryDenyWriteExecute: %s\n"
1195
                "%sRestrictRealtime: %s\n"
1196
                "%sRestrictSUIDSGID: %s\n"
1197
                "%sKeyringMode: %s\n"
1198
                "%sProtectHostname: %s%s%s\n"
1199
                "%sProtectProc: %s\n"
1200
                "%sProcSubset: %s\n"
1201
                "%sMemoryTHP: %s\n"
1202
                "%sPrivateBPF: %s\n",
1203
                prefix, c->umask,
435✔
1204
                prefix, empty_to_root(c->working_directory),
435✔
1205
                prefix, empty_to_root(c->root_directory),
435✔
1206
                prefix, yes_no(c->root_ephemeral),
435✔
1207
                prefix, yes_no(c->non_blocking),
435✔
1208
                prefix, private_tmp_to_string(c->private_tmp),
435✔
1209
                prefix, yes_no(c->private_devices),
435✔
1210
                prefix, yes_no(c->protect_kernel_tunables),
435✔
1211
                prefix, yes_no(c->protect_kernel_modules),
435✔
1212
                prefix, yes_no(c->protect_kernel_logs),
435✔
1213
                prefix, yes_no(c->protect_clock),
435✔
1214
                prefix, protect_control_groups_to_string(c->protect_control_groups),
435✔
1215
                prefix, yes_no(c->private_network),
435✔
1216
                prefix, private_users_to_string(c->private_users),
435✔
1217
                prefix, private_pids_to_string(c->private_pids),
435✔
1218
                prefix, protect_home_to_string(c->protect_home),
435✔
1219
                prefix, protect_system_to_string(c->protect_system),
435✔
1220
                prefix, yes_no(exec_context_get_effective_mount_apivfs(c)),
1221
                prefix, yes_no(exec_context_get_effective_bind_log_sockets(c)),
1222
                prefix, yes_no(c->ignore_sigpipe),
435✔
1223
                prefix, yes_no(c->memory_deny_write_execute),
435✔
1224
                prefix, yes_no(c->restrict_realtime),
435✔
1225
                prefix, yes_no(c->restrict_suid_sgid),
435✔
1226
                prefix, exec_keyring_mode_to_string(c->keyring_mode),
435✔
1227
                prefix, protect_hostname_to_string(c->protect_hostname), c->private_hostname ? ":" : "", strempty(c->private_hostname),
870✔
1228
                prefix, protect_proc_to_string(c->protect_proc),
435✔
1229
                prefix, proc_subset_to_string(c->proc_subset),
435✔
1230
                prefix, exec_memory_thp_to_string(c->memory_thp),
435✔
1231
                prefix, private_bpf_to_string(c->private_bpf));
435✔
1232

1233
        if (c->private_bpf == PRIVATE_BPF_YES) {
435✔
1234
                _cleanup_free_ char
×
1235
                        *commands = bpf_delegate_commands_to_string(c->bpf_delegate_commands),
×
1236
                        *maps = bpf_delegate_maps_to_string(c->bpf_delegate_maps),
×
1237
                        *programs = bpf_delegate_programs_to_string(c->bpf_delegate_programs),
×
1238
                        *attachments = bpf_delegate_attachments_to_string(c->bpf_delegate_attachments);
×
1239

1240
                fprintf(f, "%sBPFDelegateCommands: %s\n", prefix, strna(commands));
×
1241
                fprintf(f, "%sBPFDelegateMaps: %s\n", prefix, strna(maps));
×
1242
                fprintf(f, "%sBPFDelegatePrograms: %s\n", prefix, strna(programs));
×
1243
                fprintf(f, "%sBPFDelegateAttachments: %s\n", prefix, strna(attachments));
×
1244
        }
1245

1246
        if (c->set_login_environment >= 0)
435✔
1247
                fprintf(f, "%sSetLoginEnvironment: %s\n", prefix, yes_no(c->set_login_environment > 0));
2✔
1248

1249
        if (c->root_image)
435✔
1250
                fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
×
1251

1252
        if (c->root_image_options) {
435✔
1253
                _cleanup_free_ char *opts_str = NULL;
×
1254

1255
                if (mount_options_to_string(c->root_image_options, &opts_str) >= 0 && !isempty(opts_str))
×
1256
                        fprintf(f, "%sRootImageOptions: %s\n", prefix, opts_str);
×
1257
        }
1258

1259
        if (iovec_is_set(&c->root_hash)) {
435✔
1260
                _cleanup_free_ char *encoded = NULL;
×
1261
                encoded = hexmem(c->root_hash.iov_base, c->root_hash.iov_len);
×
1262
                if (encoded)
×
1263
                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
×
1264
        }
1265

1266
        if (c->root_hash_path)
435✔
1267
                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
×
1268

1269
        if (iovec_is_set(&c->root_hash_sig)) {
1270
                _cleanup_free_ char *encoded = NULL;
×
1271
                ssize_t len;
×
1272
                len = base64mem(c->root_hash_sig.iov_base, c->root_hash_sig.iov_len, &encoded);
×
1273
                if (len)
×
1274
                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
×
1275
        }
1276

1277
        if (c->root_hash_sig_path)
435✔
1278
                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
×
1279

1280
        if (c->root_verity)
435✔
1281
                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
×
1282

1283
        if (c->root_mstack)
435✔
1284
                fprintf(f, "%sRootMStack: %s\n", prefix, c->root_mstack);
×
1285

1286
        STRV_FOREACH(e, c->environment)
442✔
1287
                fprintf(f, "%sEnvironment: %s\n", prefix, *e);
7✔
1288

1289
        STRV_FOREACH(e, c->environment_files)
435✔
1290
                fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
×
1291

1292
        STRV_FOREACH(e, c->pass_environment)
447✔
1293
                fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
12✔
1294

1295
        STRV_FOREACH(e, c->unset_environment)
450✔
1296
                fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
15✔
1297

1298
        fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
435✔
1299

1300
        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
2,610✔
1301
                fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode);
2,175✔
1302

1303
                for (size_t i = 0; i < c->directories[dt].n_items; i++) {
2,198✔
1304
                        fprintf(f,
23✔
1305
                                "%s%s: %s%s\n",
1306
                                prefix,
1307
                                exec_directory_type_to_string(dt),
1308
                                c->directories[dt].items[i].path,
1309
                                FLAGS_SET(c->directories[dt].items[i].flags, EXEC_DIRECTORY_READ_ONLY) ? " (ro)" : "");
23✔
1310

1311
                        STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
23✔
1312
                                fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
×
1313
                }
1314
        }
1315

1316
        fprintf(f, "%sTimeoutCleanSec: %s\n", prefix, FORMAT_TIMESPAN(c->timeout_clean_usec, USEC_PER_SEC));
435✔
1317

1318
        if (c->memory_ksm >= 0)
435✔
1319
                fprintf(f, "%sMemoryKSM: %s\n", prefix, yes_no(c->memory_ksm > 0));
2✔
1320

1321
        if (c->nice_set)
435✔
1322
                fprintf(f, "%sNice: %i\n", prefix, c->nice);
2✔
1323

1324
        if (c->oom_score_adjust_set)
435✔
1325
                fprintf(f, "%sOOMScoreAdjust: %i\n", prefix, c->oom_score_adjust);
16✔
1326

1327
        if (c->coredump_filter_set)
435✔
1328
                fprintf(f, "%sCoredumpFilter: 0x%"PRIx64"\n", prefix, c->coredump_filter);
×
1329

1330
        for (unsigned i = 0; i < RLIM_NLIMITS; i++)
7,395✔
1331
                if (c->rlimit[i]) {
6,960✔
1332
                        fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
386✔
1333
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
1334
                        fprintf(f, "%sLimit%sSoft: " RLIM_FMT "\n",
386✔
1335
                                prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
386✔
1336
                }
1337

1338
        if (c->ioprio_is_set) {
435✔
1339
                _cleanup_free_ char *class_str = NULL;
3✔
1340

1341
                r = ioprio_class_to_string_alloc(ioprio_prio_class(c->ioprio), &class_str);
3✔
1342
                if (r >= 0)
3✔
1343
                        fprintf(f, "%sIOSchedulingClass: %s\n", prefix, class_str);
3✔
1344

1345
                fprintf(f, "%sIOPriority: %d\n", prefix, ioprio_prio_data(c->ioprio));
3✔
1346
        }
1347

1348
        if (c->cpu_sched_set) {
435✔
1349
                _cleanup_free_ char *policy_str = NULL;
×
1350

1351
                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
×
1352
                if (r >= 0)
×
1353
                        fprintf(f, "%sCPUSchedulingPolicy: %s\n", prefix, policy_str);
×
1354

1355
                fprintf(f,
×
1356
                        "%sCPUSchedulingPriority: %i\n"
1357
                        "%sCPUSchedulingResetOnFork: %s\n",
1358
                        prefix, c->cpu_sched_priority,
×
1359
                        prefix, yes_no(c->cpu_sched_reset_on_fork));
×
1360
        }
1361

1362
        if (c->cpu_set.set) {
435✔
1363
                _cleanup_free_ char *affinity = NULL;
×
1364

1365
                affinity = cpu_set_to_range_string(&c->cpu_set);
×
1366
                fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
×
1367
        }
1368

1369
        if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
435✔
1370
                _cleanup_free_ char *nodes = NULL;
1✔
1371

1372
                nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
1✔
1373
                fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
1✔
1374
                fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
1✔
1375
        }
1376

1377
        if (c->timer_slack_nsec != NSEC_INFINITY)
435✔
1378
                fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
1✔
1379

1380
        fprintf(f,
435✔
1381
                "%sStandardInput: %s\n"
1382
                "%sStandardOutput: %s\n"
1383
                "%sStandardError: %s\n",
1384
                prefix, exec_input_to_string(c->std_input),
435✔
1385
                prefix, exec_output_to_string(c->std_output),
435✔
1386
                prefix, exec_output_to_string(c->std_error));
435✔
1387

1388
        if (c->std_input == EXEC_INPUT_NAMED_FD)
435✔
1389
                fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
×
1390
        if (c->std_output == EXEC_OUTPUT_NAMED_FD)
435✔
1391
                fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
×
1392
        if (c->std_error == EXEC_OUTPUT_NAMED_FD)
435✔
1393
                fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
×
1394

1395
        if (c->std_input == EXEC_INPUT_FILE)
435✔
1396
                fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
×
1397
        if (c->std_output == EXEC_OUTPUT_FILE)
435✔
1398
                fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1399
        if (c->std_output == EXEC_OUTPUT_FILE_APPEND)
435✔
1400
                fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1401
        if (c->std_output == EXEC_OUTPUT_FILE_TRUNCATE)
435✔
1402
                fprintf(f, "%sStandardOutputFileToTruncate: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
×
1403
        if (c->std_error == EXEC_OUTPUT_FILE)
435✔
1404
                fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1405
        if (c->std_error == EXEC_OUTPUT_FILE_APPEND)
435✔
1406
                fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1407
        if (c->std_error == EXEC_OUTPUT_FILE_TRUNCATE)
435✔
1408
                fprintf(f, "%sStandardErrorFileToTruncate: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
×
1409

1410
        if (c->tty_path)
435✔
1411
                fprintf(f,
2✔
1412
                        "%sTTYPath: %s\n"
1413
                        "%sTTYReset: %s\n"
1414
                        "%sTTYVHangup: %s\n"
1415
                        "%sTTYVTDisallocate: %s\n"
1416
                        "%sTTYRows: %u\n"
1417
                        "%sTTYColumns: %u\n",
1418
                        prefix, c->tty_path,
1419
                        prefix, yes_no(c->tty_reset),
2✔
1420
                        prefix, yes_no(c->tty_vhangup),
2✔
1421
                        prefix, yes_no(c->tty_vt_disallocate),
2✔
1422
                        prefix, c->tty_rows,
2✔
1423
                        prefix, c->tty_cols);
2✔
1424

1425
        if (IN_SET(c->std_output,
435✔
1426
                   EXEC_OUTPUT_KMSG,
1427
                   EXEC_OUTPUT_JOURNAL,
1428
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1429
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
1430
            IN_SET(c->std_error,
24✔
1431
                   EXEC_OUTPUT_KMSG,
1432
                   EXEC_OUTPUT_JOURNAL,
1433
                   EXEC_OUTPUT_KMSG_AND_CONSOLE,
1434
                   EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
1435

1436
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
411✔
1437

1438
                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
411✔
1439
                if (r >= 0)
411✔
1440
                        fprintf(f, "%sSyslogFacility: %s\n", prefix, fac_str);
411✔
1441

1442
                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
411✔
1443
                if (r >= 0)
411✔
1444
                        fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str);
411✔
1445
        }
1446

1447
        if (c->log_level_max >= 0) {
435✔
1448
                _cleanup_free_ char *t = NULL;
1✔
1449

1450
                (void) log_level_to_string_alloc(c->log_level_max, &t);
1✔
1451

1452
                fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
1✔
1453
        }
1454

1455
        if (c->log_ratelimit.interval > 0)
435✔
1456
                fprintf(f,
×
1457
                        "%sLogRateLimitIntervalSec: %s\n",
1458
                        prefix, FORMAT_TIMESPAN(c->log_ratelimit.interval, USEC_PER_SEC));
×
1459

1460
        if (c->log_ratelimit.burst > 0)
435✔
1461
                fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit.burst);
×
1462

1463
        if (!set_isempty(c->log_filter_allowed_patterns) || !set_isempty(c->log_filter_denied_patterns)) {
435✔
1464
                fprintf(f, "%sLogFilterPatterns:", prefix);
×
1465

1466
                char *pattern;
×
1467
                SET_FOREACH(pattern, c->log_filter_allowed_patterns)
×
1468
                        fprintf(f, " %s", pattern);
×
1469
                SET_FOREACH(pattern, c->log_filter_denied_patterns)
×
1470
                        fprintf(f, " ~%s", pattern);
×
1471
                fputc('\n', f);
×
1472
        }
1473

1474
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields) {
439✔
1475
                fprintf(f, "%sLogExtraFields: ", prefix);
4✔
1476
                fwrite(field->iov_base, 1, field->iov_len, f);
4✔
1477
                fputc('\n', f);
4✔
1478
        }
1479

1480
        if (c->log_namespace)
435✔
1481
                fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
×
1482

1483
        if (c->secure_bits) {
435✔
1484
                _cleanup_free_ char *str = NULL;
×
1485

1486
                r = secure_bits_to_string_alloc(c->secure_bits, &str);
×
1487
                if (r >= 0)
×
1488
                        fprintf(f, "%sSecure Bits: %s\n", prefix, str);
×
1489
        }
1490

1491
        if (c->capability_bounding_set != CAP_MASK_UNSET) {
435✔
1492
                _cleanup_free_ char *str = NULL;
435✔
1493

1494
                r = capability_set_to_string(c->capability_bounding_set, &str);
435✔
1495
                if (r >= 0)
435✔
1496
                        fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str);
435✔
1497
        }
1498

1499
        if (c->capability_ambient_set != 0) {
435✔
1500
                _cleanup_free_ char *str = NULL;
3✔
1501

1502
                r = capability_set_to_string(c->capability_ambient_set, &str);
3✔
1503
                if (r >= 0)
3✔
1504
                        fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str);
3✔
1505
        }
1506

1507
        if (c->user)
435✔
1508
                fprintf(f, "%sUser: %s\n", prefix, c->user);
7✔
1509
        if (c->group)
435✔
1510
                fprintf(f, "%sGroup: %s\n", prefix, c->group);
1✔
1511

1512
        fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
869✔
1513

1514
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
435✔
1515

1516
        if (c->pam_name)
435✔
1517
                fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
1✔
1518

1519
        strv_dump(f, prefix, "ReadWritePaths", c->read_write_paths);
435✔
1520
        strv_dump(f, prefix, "ReadOnlyPaths", c->read_only_paths);
435✔
1521
        strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
435✔
1522
        strv_dump(f, prefix, "ExecPaths", c->exec_paths);
435✔
1523
        strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
435✔
1524
        strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
435✔
1525

1526
        FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts)
439✔
1527
                fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,
4✔
1528
                        mount->read_only ? "BindReadOnlyPaths" : "BindPaths",
4✔
1529
                        mount->ignore_enoent ? "-": "",
4✔
1530
                        mount->source,
1531
                        mount->destination,
1532
                        mount->recursive ? "rbind" : "norbind");
4✔
1533

1534
        FOREACH_ARRAY(tmpfs, c->temporary_filesystems, c->n_temporary_filesystems)
435✔
1535
                fprintf(f, "%sTemporaryFileSystem: %s%s%s\n", prefix,
×
1536
                        tmpfs->path,
1537
                        isempty(tmpfs->options) ? "" : ":",
×
1538
                        strempty(tmpfs->options));
×
1539

1540
        if (c->utmp_id)
435✔
1541
                fprintf(f,
2✔
1542
                        "%sUtmpIdentifier: %s\n",
1543
                        prefix, c->utmp_id);
1544

1545
        if (c->selinux_context)
435✔
1546
                fprintf(f,
×
1547
                        "%sSELinuxContext: %s%s\n",
1548
                        prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
×
1549

1550
        if (c->apparmor_profile)
435✔
1551
                fprintf(f,
×
1552
                        "%sAppArmorProfile: %s%s\n",
1553
                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
×
1554

1555
        if (c->smack_process_label)
435✔
1556
                fprintf(f,
×
1557
                        "%sSmackProcessLabel: %s%s\n",
1558
                        prefix, c->smack_process_label_ignore ? "-" : "", c->smack_process_label);
×
1559

1560
        if (c->personality != PERSONALITY_INVALID)
435✔
1561
                fprintf(f,
1✔
1562
                        "%sPersonality: %s\n",
1563
                        prefix, strna(personality_to_string(c->personality)));
1564

1565
        fprintf(f,
435✔
1566
                "%sLockPersonality: %s\n",
1567
                prefix, yes_no(c->lock_personality));
435✔
1568

1569
        if (c->syscall_filter) {
435✔
1570
                fprintf(f,
24✔
1571
                        "%sSystemCallFilter: ",
1572
                        prefix);
1573

1574
                if (!c->syscall_allow_list)
24✔
1575
                        fputc('~', f);
×
1576

1577
#if HAVE_SECCOMP
1578
                if (DLOPEN_LIBSECCOMP(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) >= 0) {
24✔
1579
                        void *id, *val;
24✔
1580
                        bool first = true;
24✔
1581
                        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
9,404✔
1582
                                _cleanup_free_ char *name = NULL;
9,380✔
1583
                                const char *errno_name = NULL;
9,380✔
1584
                                int num = PTR_TO_INT(val);
9,380✔
1585

1586
                                if (first)
9,380✔
1587
                                        first = false;
1588
                                else
1589
                                        fputc(' ', f);
9,356✔
1590

1591
                                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
9,380✔
1592
                                fputs(strna(name), f);
9,380✔
1593

1594
                                if (num >= 0) {
9,380✔
1595
                                        errno_name = seccomp_errno_or_action_to_string(num);
×
1596
                                        if (errno_name)
×
1597
                                                fprintf(f, ":%s", errno_name);
×
1598
                                        else
1599
                                                fprintf(f, ":%d", num);
×
1600
                                }
1601
                        }
1602
                }
1603
#endif
1604

1605
                fputc('\n', f);
24✔
1606
        }
1607

1608
        if (c->syscall_archs) {
435✔
1609
                fprintf(f,
26✔
1610
                        "%sSystemCallArchitectures:",
1611
                        prefix);
1612

1613
#if HAVE_SECCOMP
1614
                void *id;
26✔
1615
                SET_FOREACH(id, c->syscall_archs)
52✔
1616
                        fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
26✔
1617
#endif
1618
                fputc('\n', f);
26✔
1619
        }
1620

1621
        if (exec_context_restrict_namespaces_set(c)) {
435✔
1622
                _cleanup_free_ char *s = NULL;
22✔
1623

1624
                r = namespace_flags_to_string(c->restrict_namespaces, &s);
22✔
1625
                if (r >= 0)
22✔
1626
                        fprintf(f, "%sRestrictNamespaces: %s\n",
22✔
1627
                                prefix, strna(s));
1628
        }
1629

1630
#if HAVE_LIBBPF
1631
        if (exec_context_restrict_filesystems_set(c)) {
435✔
1632
                char *fs;
×
1633
                SET_FOREACH(fs, c->restrict_filesystems)
×
1634
                        fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs);
×
1635
        }
1636
#endif
1637

1638
        if (c->user_namespace_path)
435✔
1639
                fprintf(f,
×
1640
                        "%sUserNamespacePath: %s\n",
1641
                        prefix, c->user_namespace_path);
1642

1643
        if (c->network_namespace_path)
435✔
1644
                fprintf(f,
×
1645
                        "%sNetworkNamespacePath: %s\n",
1646
                        prefix, c->network_namespace_path);
1647

1648
        if (c->syscall_errno > 0) {
435✔
1649
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
434✔
1650

1651
#if HAVE_SECCOMP
1652
                const char *errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
434✔
1653
                if (errno_name)
434✔
1654
                        fputs(errno_name, f);
434✔
1655
                else
1656
                        fprintf(f, "%d", c->syscall_errno);
×
1657
#endif
1658
                fputc('\n', f);
434✔
1659
        }
1660

1661
        FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
435✔
1662
                fprintf(f, "%sMountImages: %s%s:%s", prefix,
×
1663
                        mount->ignore_enoent ? "-": "",
×
1664
                        mount->source,
1665
                        mount->destination);
1666
                if (mount->mount_options) {
×
1667
                        _cleanup_free_ char *opts = NULL;
×
1668

1669
                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
×
1670
                                fprintf(f, " %s", opts);
×
1671
                }
1672
                fprintf(f, "\n");
×
1673
        }
1674

1675
        FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
435✔
1676
                fprintf(f, "%sExtensionImages: %s%s", prefix,
×
1677
                        mount->ignore_enoent ? "-": "",
×
1678
                        mount->source);
1679
                if (mount->mount_options) {
×
1680
                        _cleanup_free_ char *opts = NULL;
×
1681

1682
                        if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
×
1683
                                fprintf(f, " %s", opts);
×
1684
                }
1685
                fprintf(f, "\n");
×
1686
        }
1687

1688
        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
435✔
1689
}
435✔
1690

1691
bool exec_context_maintains_privileges(const ExecContext *c) {
×
1692
        assert(c);
×
1693

1694
        /* Returns true if the process forked off would run under
1695
         * an unchanged UID or as root. */
1696

1697
        if (!c->user)
×
1698
                return true;
1699

1700
        if (STR_IN_SET(c->user, "root", "0"))
×
1701
                return true;
×
1702

1703
        return false;
×
1704
}
1705

1706
int exec_context_get_effective_ioprio(const ExecContext *c) {
15,886✔
1707
        int p;
15,886✔
1708

1709
        assert(c);
15,886✔
1710

1711
        if (c->ioprio_is_set)
15,886✔
1712
                return c->ioprio;
68✔
1713

1714
        p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
15,818✔
1715
        if (p < 0)
15,818✔
1716
                return IOPRIO_DEFAULT_CLASS_AND_PRIO;
1717

1718
        return ioprio_normalize(p);
15,818✔
1719
}
1720

1721
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
58,897✔
1722
        assert(c);
58,897✔
1723

1724
        /* Explicit setting wins */
1725
        if (c->mount_apivfs >= 0)
58,897✔
1726
                return c->mount_apivfs > 0;
107✔
1727

1728
        /* Default to "yes" if root directory or image are specified */
1729
        if (exec_context_with_rootfs(c))
58,790✔
1730
                return true;
341✔
1731

1732
        return false;
1733
}
1734

1735
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
46,179✔
1736
        assert(c);
46,179✔
1737

1738
        /* If log namespace is specified, "/run/systemd/journal.namespace/" would be bind mounted to
1739
         * "/run/systemd/journal/", which effectively means BindLogSockets=yes */
1740
        if (c->log_namespace)
46,179✔
1741
                return true;
1742

1743
        if (c->bind_log_sockets >= 0)
46,171✔
1744
                return c->bind_log_sockets > 0;
2✔
1745

1746
        if (exec_context_get_effective_mount_apivfs(c))
46,169✔
1747
                return true;
1748

1749
        /* When PrivateDevices=yes, /dev/log gets symlinked to /run/systemd/journal/dev-log */
1750
        if (exec_context_with_rootfs(c) && c->private_devices)
45,978✔
1751
                return true;
×
1752

1753
        return false;
1754
}
1755

1756
void exec_context_free_log_extra_fields(ExecContext *c) {
106,419✔
1757
        assert(c);
106,419✔
1758

1759
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
112,003✔
1760
                free(field->iov_base);
5,584✔
1761

1762
        c->log_extra_fields = mfree(c->log_extra_fields);
106,419✔
1763
        c->n_log_extra_fields = 0;
106,419✔
1764
}
106,419✔
1765

1766
void exec_context_revert_tty(ExecContext *c, sd_id128_t invocation_id) {
2,949✔
1767
        _cleanup_close_ int fd = -EBADF;
2,949✔
1768
        const char *path;
2,949✔
1769
        struct stat st;
2,949✔
1770
        int r;
2,949✔
1771

1772
        assert(c);
2,949✔
1773

1774
        /* First, reset the TTY (possibly kicking everybody else from the TTY) */
1775
        exec_context_tty_reset(c, /* parameters= */ NULL, invocation_id);
2,949✔
1776

1777
        /* And then undo what chown_terminal() did earlier. Note that we only do this if we have a path
1778
         * configured. If the TTY was passed to us as file descriptor we assume the TTY is opened and managed
1779
         * by whoever passed it to us and thus knows better when and how to chmod()/chown() it back. */
1780
        if (!exec_context_may_touch_tty(c))
2,949✔
1781
                return;
1782

1783
        path = exec_context_tty_path(c);
39✔
1784
        if (!path)
39✔
1785
                return;
1786

1787
        fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
39✔
1788
        if (fd < 0)
39✔
1789
                return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
×
1790
                                             "Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
1791
                                             path);
1792

1793
        if (fstat(fd, &st) < 0)
39✔
1794
                return (void) log_warning_errno(errno, "Failed to stat TTY '%s', ignoring: %m", path);
2,910✔
1795

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

1804
        r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
39✔
1805
        if (r < 0)
39✔
1806
                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);
39✔
1807
}
1808

1809
int exec_context_get_clean_directories(
1✔
1810
                ExecContext *c,
1811
                char **prefix,
1812
                ExecCleanMask mask,
1813
                char ***ret) {
1814

1815
        _cleanup_strv_free_ char **l = NULL;
1✔
1816
        int r;
1✔
1817

1818
        assert(c);
1✔
1819
        assert(prefix);
1✔
1820
        assert(ret);
1✔
1821

1822
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
6✔
1823
                if (!BIT_SET(mask, t))
5✔
1824
                        continue;
×
1825

1826
                if (!prefix[t])
5✔
1827
                        continue;
×
1828

1829
                FOREACH_ARRAY(i, c->directories[t].items, c->directories[t].n_items) {
7✔
1830
                        char *j;
2✔
1831

1832
                        j = path_join(prefix[t], i->path);
2✔
1833
                        if (!j)
2✔
1834
                                return -ENOMEM;
1835

1836
                        r = strv_consume(&l, j);
2✔
1837
                        if (r < 0)
2✔
1838
                                return r;
1839

1840
                        /* Also remove private directories unconditionally. */
1841
                        if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
2✔
1842
                                j = path_join(prefix[t], "private", i->path);
2✔
1843
                                if (!j)
2✔
1844
                                        return -ENOMEM;
1845

1846
                                r = strv_consume(&l, j);
2✔
1847
                                if (r < 0)
2✔
1848
                                        return r;
1849
                        }
1850

1851
                        STRV_FOREACH(symlink, i->symlinks) {
2✔
1852
                                j = path_join(prefix[t], *symlink);
×
1853
                                if (!j)
×
1854
                                        return -ENOMEM;
1855

1856
                                r = strv_consume(&l, j);
×
1857
                                if (r < 0)
×
1858
                                        return r;
1859
                        }
1860
                }
1861
        }
1862

1863
        *ret = TAKE_PTR(l);
1✔
1864
        return 0;
1✔
1865
}
1866

1867
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
7,157✔
1868
        ExecCleanMask mask = 0;
7,157✔
1869

1870
        assert(c);
7,157✔
1871
        assert(ret);
7,157✔
1872

1873
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
42,942✔
1874
                if (c->directories[t].n_items > 0)
35,785✔
1875
                        mask |= 1U << t;
423✔
1876

1877
        *ret = mask;
7,157✔
1878
        return 0;
7,157✔
1879
}
1880

1881
int exec_context_get_oom_score_adjust(const ExecContext *c) {
7,943✔
1882
        int n = 0, r;
7,943✔
1883

1884
        assert(c);
7,943✔
1885

1886
        if (c->oom_score_adjust_set)
7,943✔
1887
                return c->oom_score_adjust;
818✔
1888

1889
        r = get_oom_score_adjust(&n);
7,125✔
1890
        if (r < 0)
7,125✔
1891
                log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
×
1892

1893
        return n;
7,125✔
1894
}
1895

1896
uint64_t exec_context_get_coredump_filter(const ExecContext *c) {
7,943✔
1897
        _cleanup_free_ char *t = NULL;
7,943✔
1898
        uint64_t n = COREDUMP_FILTER_MASK_DEFAULT;
7,943✔
1899
        int r;
7,943✔
1900

1901
        assert(c);
7,943✔
1902

1903
        if (c->coredump_filter_set)
7,943✔
1904
                return c->coredump_filter;
×
1905

1906
        r = read_one_line_file("/proc/self/coredump_filter", &t);
7,943✔
1907
        if (r < 0)
7,943✔
1908
                log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
×
1909
        else {
1910
                r = safe_atoux64(t, &n);
7,943✔
1911
                if (r < 0)
7,943✔
1912
                        log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
×
1913
        }
1914

1915
        return n;
7,943✔
1916
}
1917

1918
int exec_context_get_nice(const ExecContext *c) {
7,943✔
1919
        int n;
7,943✔
1920

1921
        assert(c);
7,943✔
1922

1923
        if (c->nice_set)
7,943✔
1924
                return c->nice;
31✔
1925

1926
        errno = 0;
7,912✔
1927
        n = getpriority(PRIO_PROCESS, 0);
7,912✔
1928
        if (errno > 0) {
7,912✔
1929
                log_debug_errno(errno, "Failed to get process nice value, ignoring: %m");
×
1930
                n = 0;
1931
        }
1932

1933
        return n;
1934
}
1935

1936
int exec_context_get_cpu_sched_policy(const ExecContext *c) {
7,943✔
1937
        int n;
7,943✔
1938

1939
        assert(c);
7,943✔
1940

1941
        if (c->cpu_sched_set)
7,943✔
1942
                return c->cpu_sched_policy;
×
1943

1944
        n = sched_getscheduler(0);
7,943✔
1945
        if (n < 0)
7,943✔
1946
                log_debug_errno(errno, "Failed to get scheduler policy, ignoring: %m");
×
1947

1948
        return n < 0 ? SCHED_OTHER : n;
7,943✔
1949
}
1950

1951
int exec_context_get_cpu_sched_priority(const ExecContext *c) {
7,943✔
1952
        struct sched_param p = {};
7,943✔
1953
        int r;
7,943✔
1954

1955
        assert(c);
7,943✔
1956

1957
        if (c->cpu_sched_set)
7,943✔
1958
                return c->cpu_sched_priority;
×
1959

1960
        r = sched_getparam(0, &p);
7,943✔
1961
        if (r < 0)
7,943✔
1962
                log_debug_errno(errno, "Failed to get scheduler priority, ignoring: %m");
×
1963

1964
        return r >= 0 ? p.sched_priority : 0;
7,943✔
1965
}
1966

1967
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) {
7,943✔
1968
        int r;
7,943✔
1969

1970
        assert(c);
7,943✔
1971

1972
        if (c->timer_slack_nsec != NSEC_INFINITY)
7,943✔
1973
                return c->timer_slack_nsec;
1974

1975
        r = prctl(PR_GET_TIMERSLACK);
7,943✔
1976
        if (r < 0)
7,943✔
1977
                log_debug_errno(r, "Failed to get timer slack, ignoring: %m");
×
1978

1979
        return (uint64_t) MAX(r, 0);
7,943✔
1980
}
1981

1982
bool exec_context_get_set_login_environment(const ExecContext *c) {
18,374✔
1983
        assert(c);
18,374✔
1984

1985
        if (c->set_login_environment >= 0)
18,374✔
1986
                return c->set_login_environment;
×
1987

1988
        return c->user || c->dynamic_user || c->pam_name;
32,993✔
1989
}
1990

1991
char** exec_context_get_syscall_filter(const ExecContext *c) {
7,943✔
1992
        _cleanup_strv_free_ char **l = NULL;
7,943✔
1993

1994
        assert(c);
7,943✔
1995

1996
#if HAVE_SECCOMP
1997
        if (DLOPEN_LIBSECCOMP(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0)
7,943✔
1998
                return strv_new(NULL);
×
1999

2000
        void *id, *val;
7,943✔
2001
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
66,781✔
2002
                _cleanup_free_ char *name = NULL;
58,838✔
2003
                const char *e = NULL;
58,838✔
2004
                char *s;
58,838✔
2005
                int num = PTR_TO_INT(val);
58,838✔
2006

2007
                if (c->syscall_allow_list && num >= 0)
58,838✔
2008
                        /* syscall with num >= 0 in allow-list is denied. */
2009
                        continue;
×
2010

2011
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
58,838✔
2012
                if (!name)
58,838✔
2013
                        continue;
×
2014

2015
                if (num >= 0) {
58,838✔
2016
                        e = seccomp_errno_or_action_to_string(num);
×
2017
                        if (e)
×
2018
                                s = strjoin(name, ":", e);
×
2019
                        else
2020
                                s = asprintf_safe("%s:%d", name, num);
×
2021
                        if (!s)
×
2022
                                return NULL;
2023
                } else
2024
                        s = TAKE_PTR(name);
2025

2026
                if (strv_consume(&l, s) < 0)
58,838✔
2027
                        return NULL;
2028
        }
2029

2030
        strv_sort(l);
7,943✔
2031
#endif
2032

2033
        return l ? TAKE_PTR(l) : strv_new(NULL);
7,943✔
2034
}
2035

2036
char** exec_context_get_syscall_archs(const ExecContext *c) {
7,943✔
2037
        _cleanup_strv_free_ char **l = NULL;
7,943✔
2038

2039
        assert(c);
7,943✔
2040

2041
#if HAVE_SECCOMP
2042
        void *id;
7,943✔
2043
        SET_FOREACH(id, c->syscall_archs) {
8,115✔
2044
                const char *name;
172✔
2045

2046
                name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
172✔
2047
                if (!name)
172✔
2048
                        continue;
×
2049

2050
                if (strv_extend(&l, name) < 0)
172✔
2051
                        return NULL;
×
2052
        }
2053

2054
        strv_sort(l);
7,943✔
2055
#endif
2056

2057
        return l ? TAKE_PTR(l) : strv_new(NULL);
7,943✔
2058
}
2059

2060
char** exec_context_get_syscall_log(const ExecContext *c) {
7,943✔
2061
        _cleanup_strv_free_ char **l = NULL;
7,943✔
2062

2063
        assert(c);
7,943✔
2064

2065
#if HAVE_SECCOMP
2066
        if (DLOPEN_LIBSECCOMP(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0)
7,943✔
2067
                return strv_new(NULL);
×
2068

2069
        void *id, *val;
7,943✔
2070
        HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
7,943✔
2071
                char *name = NULL;
×
2072

2073
                name = sym_seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
×
2074
                if (!name)
×
2075
                        continue;
×
2076

2077
                if (strv_consume(&l, name) < 0)
×
2078
                        return NULL;
×
2079
        }
2080

2081
        strv_sort(l);
7,943✔
2082
#endif
2083

2084
        return l ? TAKE_PTR(l) : strv_new(NULL);
7,943✔
2085
}
2086

2087
char** exec_context_get_address_families(const ExecContext *c) {
7,943✔
2088
        _cleanup_strv_free_ char **l = NULL;
7,943✔
2089
        void *af;
7,943✔
2090

2091
        assert(c);
7,943✔
2092

2093
        SET_FOREACH(af, c->address_families) {
8,484✔
2094
                const char *name;
541✔
2095

2096
                name = af_to_name(PTR_TO_INT(af));
541✔
2097
                if (!name)
541✔
2098
                        continue;
×
2099

2100
                if (strv_extend(&l, name) < 0)
541✔
2101
                        return NULL;
×
2102
        }
2103

2104
        strv_sort(l);
7,943✔
2105

2106
        return l ? TAKE_PTR(l) : strv_new(NULL);
7,943✔
2107
}
2108

2109
char** exec_context_get_restrict_filesystems(const ExecContext *c) {
7,943✔
2110
        assert(c);
7,943✔
2111

2112
#if HAVE_LIBBPF
2113
        char **l = set_get_strv(c->restrict_filesystems);
7,943✔
2114
        if (!l)
7,943✔
2115
                return NULL;
2116

2117
        return strv_sort(l);
7,943✔
2118
#else
2119
        return strv_new(NULL);
2120
#endif
2121
}
2122

2123
bool exec_context_restrict_namespaces_set(const ExecContext *c) {
16,139✔
2124
        assert(c);
16,139✔
2125

2126
        return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL;
16,139✔
2127
}
2128

2129
bool exec_context_restrict_filesystems_set(const ExecContext *c) {
21,828✔
2130
        assert(c);
21,828✔
2131

2132
        return c->restrict_filesystems_allow_list ||
21,828✔
2133
          !set_isempty(c->restrict_filesystems);
21,828✔
2134
}
2135

2136
bool exec_context_with_rootfs(const ExecContext *c) {
109,566✔
2137
        assert(c);
109,566✔
2138

2139
        /* Checks if RootDirectory=, RootImage=, RootMStack= or RootDirectoryFileDescriptor= are used */
2140

2141
        return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd || c->root_mstack;
109,566✔
2142
}
2143

2144
bool exec_context_with_rootfs_strict(const ExecContext *c) {
1,536✔
2145
        assert(c);
1,536✔
2146

2147
        /* just like exec_context_with_rootfs(), but doesn't suppress a root directory of "/", i.e. returns
2148
         * true in more cases: when a root directory is explicitly configured, even if it's our usual
2149
         * root. */
2150

2151
        return c->root_directory || c->root_image || c->root_directory_as_fd || c->root_mstack;
1,536✔
2152
}
2153

2154
int exec_context_has_vpicked_extensions(const ExecContext *context) {
31,630✔
2155
        int r;
31,630✔
2156

2157
        assert(context);
31,630✔
2158

2159
        FOREACH_ARRAY(mi, context->extension_images, context->n_extension_images) {
31,686✔
2160
                r = path_uses_vpick(mi->source);
56✔
2161
                if (r != 0)
56✔
2162
                        return r;
2163
        }
2164
        STRV_FOREACH(ed, context->extension_directories) {
31,631✔
2165
                r = path_uses_vpick(*ed);
1✔
2166
                if (r != 0)
1✔
2167
                        return r;
2168
        }
2169

2170
        return 0;
2171
}
2172

2173
void exec_status_start(ExecStatus *s, pid_t pid, const dual_timestamp *ts) {
10,759✔
2174
        assert(s);
10,759✔
2175

2176
        *s = (ExecStatus) {
10,759✔
2177
                .pid = pid,
2178
        };
2179

2180
        if (ts)
10,759✔
2181
                s->start_timestamp = *ts;
10,759✔
2182
        else
2183
                dual_timestamp_now(&s->start_timestamp);
×
2184
}
10,759✔
2185

2186
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status) {
3,738✔
2187
        assert(s);
3,738✔
2188

2189
        if (s->pid != pid)
3,738✔
2190
                *s = (ExecStatus) {
7✔
2191
                        .pid = pid,
2192
                };
2193

2194
        dual_timestamp_now(&s->exit_timestamp);
3,738✔
2195

2196
        s->code = code;
3,738✔
2197
        s->status = status;
3,738✔
2198

2199
        if (context && context->utmp_id)
3,738✔
2200
                (void) utmp_put_dead_process(context->utmp_id, pid, code, status);
16✔
2201
}
3,738✔
2202

2203
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
15,288✔
2204
        assert(s);
15,288✔
2205
        assert(ucred);
15,288✔
2206
        assert(ts);
15,288✔
2207

2208
        if (ucred->pid != s->pid)
15,288✔
2209
                *s = (ExecStatus) {
15✔
2210
                        .pid = ucred->pid,
2211
                };
2212

2213
        s->handoff_timestamp = *ts;
15,288✔
2214
}
15,288✔
2215

2216
void exec_status_reset(ExecStatus *s) {
35,029✔
2217
        assert(s);
35,029✔
2218

2219
        *s = (ExecStatus) {};
35,029✔
2220
}
35,029✔
2221

2222
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix) {
227✔
2223
        assert(s);
227✔
2224
        assert(f);
227✔
2225

2226
        if (s->pid <= 0)
227✔
2227
                return;
2228

2229
        prefix = strempty(prefix);
43✔
2230

2231
        fprintf(f,
43✔
2232
                "%sPID: "PID_FMT"\n",
2233
                prefix, s->pid);
2234

2235
        if (dual_timestamp_is_set(&s->start_timestamp))
43✔
2236
                fprintf(f,
43✔
2237
                        "%sStart Timestamp: %s\n",
2238
                        prefix, FORMAT_TIMESTAMP_STYLE(s->start_timestamp.realtime, TIMESTAMP_US));
43✔
2239

2240
        if (dual_timestamp_is_set(&s->handoff_timestamp) && dual_timestamp_is_set(&s->start_timestamp) &&
43✔
2241
            s->handoff_timestamp.monotonic > s->start_timestamp.monotonic)
42✔
2242
                fprintf(f,
42✔
2243
                        "%sHandoff Timestamp: %s since start\n",
2244
                        prefix,
2245
                        FORMAT_TIMESPAN(usec_sub_unsigned(s->handoff_timestamp.monotonic, s->start_timestamp.monotonic), 1));
84✔
2246
        else
2247
                fprintf(f,
1✔
2248
                        "%sHandoff Timestamp: %s\n",
2249
                        prefix, FORMAT_TIMESTAMP_STYLE(s->handoff_timestamp.realtime, TIMESTAMP_US));
1✔
2250

2251
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
43✔
2252

2253
                if (dual_timestamp_is_set(&s->handoff_timestamp) && s->exit_timestamp.monotonic > s->handoff_timestamp.monotonic)
20✔
2254
                        fprintf(f,
20✔
2255
                                "%sExit Timestamp: %s since handoff\n",
2256
                                prefix,
2257
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->handoff_timestamp.monotonic), 1));
40✔
2258
                else if (dual_timestamp_is_set(&s->start_timestamp) && s->exit_timestamp.monotonic > s->start_timestamp.monotonic)
×
2259
                        fprintf(f,
×
2260
                                "%sExit Timestamp: %s since start\n",
2261
                                prefix,
2262
                                FORMAT_TIMESPAN(usec_sub_unsigned(s->exit_timestamp.monotonic, s->start_timestamp.monotonic), 1));
×
2263
                else
2264
                        fprintf(f,
×
2265
                                "%sExit Timestamp: %s\n",
2266
                                prefix, FORMAT_TIMESTAMP_STYLE(s->exit_timestamp.realtime, TIMESTAMP_US));
×
2267

2268
                fprintf(f,
20✔
2269
                        "%sExit Code: %s\n"
2270
                        "%sExit Status: %i\n",
2271
                        prefix, sigchld_code_to_string(s->code),
20✔
2272
                        prefix, s->status);
20✔
2273
        }
2274
}
2275

2276
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
227✔
2277
        _cleanup_free_ char *cmd = NULL;
454✔
2278
        const char *prefix2;
227✔
2279

2280
        assert(c);
227✔
2281
        assert(f);
227✔
2282

2283
        prefix = strempty(prefix);
227✔
2284
        prefix2 = strjoina(prefix, "\t");
1,135✔
2285

2286
        cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
227✔
2287

2288
        fprintf(f,
227✔
2289
                "%sCommand Line: %s\n",
2290
                prefix, strnull(cmd));
2291

2292
        exec_status_dump(&c->exec_status, f, prefix2);
227✔
2293
}
227✔
2294

2295
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
222✔
2296
        assert(f);
222✔
2297

2298
        prefix = strempty(prefix);
222✔
2299

2300
        LIST_FOREACH(command, i, c)
448✔
2301
                exec_command_dump(i, f, prefix);
226✔
2302
}
222✔
2303

2304
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
39,724✔
2305
        ExecCommand *end;
39,724✔
2306

2307
        assert(l);
39,724✔
2308
        assert(e);
39,724✔
2309

2310
        if (*l) {
39,724✔
2311
                /* It's kind of important, that we keep the order here */
2312
                end = LIST_FIND_TAIL(command, *l);
620✔
2313
                LIST_INSERT_AFTER(command, *l, end, e);
365✔
2314
        } else
2315
                *l = e;
39,359✔
2316
}
39,724✔
2317

2318
int exec_command_set(ExecCommand *c, const char *path, ...) {
276✔
2319
        va_list ap;
276✔
2320
        char **l, *p;
276✔
2321

2322
        assert(c);
276✔
2323
        assert(path);
276✔
2324

2325
        va_start(ap, path);
276✔
2326
        l = strv_new_ap(path, ap);
276✔
2327
        va_end(ap);
276✔
2328

2329
        if (!l)
276✔
2330
                return -ENOMEM;
276✔
2331

2332
        p = strdup(path);
276✔
2333
        if (!p) {
276✔
2334
                strv_free(l);
×
2335
                return -ENOMEM;
×
2336
        }
2337

2338
        free_and_replace(c->path, p);
276✔
2339

2340
        return strv_free_and_replace(c->argv, l);
276✔
2341
}
2342

2343
int exec_command_append(ExecCommand *c, const char *path, ...) {
429✔
2344
        char **l;
429✔
2345
        va_list ap;
429✔
2346
        int r;
429✔
2347

2348
        assert(c);
429✔
2349
        assert(path);
429✔
2350

2351
        va_start(ap, path);
429✔
2352
        l = strv_new_ap(path, ap);
429✔
2353
        va_end(ap);
429✔
2354

2355
        if (!l)
429✔
2356
                return -ENOMEM;
429✔
2357

2358
        r = strv_extend_strv_consume(&c->argv, l, /* filter_duplicates= */ false);
429✔
2359
        if (r < 0)
429✔
2360
                return r;
×
2361

2362
        return 0;
2363
}
2364

2365
static char *destroy_tree(char *path) {
848✔
2366
        if (!path)
848✔
2367
                return NULL;
2368

2369
        if (!path_equal(path, RUN_SYSTEMD_EMPTY)) {
76✔
2370
                log_debug("Spawning process to nuke '%s'", path);
76✔
2371

2372
                (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
76✔
2373
        }
2374

2375
        return mfree(path);
76✔
2376
}
2377

2378
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
472,614✔
2379
        assert(rt);
472,614✔
2380

2381
        if (rt->manager)
472,614✔
2382
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
502✔
2383

2384
        rt->id = mfree(rt->id);
472,614✔
2385
        rt->tmp_dir = mfree(rt->tmp_dir);
472,614✔
2386
        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
472,614✔
2387
        safe_close_pair(rt->userns_storage_socket);
472,614✔
2388
        safe_close_pair(rt->netns_storage_socket);
472,614✔
2389
        safe_close_pair(rt->ipcns_storage_socket);
472,614✔
2390
}
472,614✔
2391

2392
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
472,580✔
2393
        if (!rt)
472,580✔
2394
                return NULL;
2395

2396
        exec_shared_runtime_done(rt);
472,580✔
2397
        return mfree(rt);
472,580✔
2398
}
2399

2400
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
556✔
2401
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
483,164✔
2402

2403
ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
165✔
2404
        if (!rt)
165✔
2405
                return NULL;
2406

2407
        assert(rt->n_ref > 0);
146✔
2408
        rt->n_ref--;
146✔
2409

2410
        if (rt->n_ref > 0)
146✔
2411
                return NULL;
2412

2413
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
146✔
2414
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
146✔
2415

2416
        return exec_shared_runtime_free(rt);
146✔
2417
}
2418

2419
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
472,580✔
2420
        _cleanup_free_ char *id_copy = NULL;
945,160✔
2421
        ExecSharedRuntime *n;
472,580✔
2422

2423
        assert(ret);
472,580✔
2424

2425
        id_copy = strdup(id);
472,580✔
2426
        if (!id_copy)
472,580✔
2427
                return -ENOMEM;
2428

2429
        n = new(ExecSharedRuntime, 1);
472,580✔
2430
        if (!n)
472,580✔
2431
                return -ENOMEM;
2432

2433
        *n = (ExecSharedRuntime) {
472,580✔
2434
                .id = TAKE_PTR(id_copy),
472,580✔
2435
                .userns_storage_socket = EBADF_PAIR,
2436
                .netns_storage_socket = EBADF_PAIR,
2437
                .ipcns_storage_socket = EBADF_PAIR,
2438
        };
2439

2440
        *ret = n;
472,580✔
2441
        return 0;
472,580✔
2442
}
2443

2444
static int exec_shared_runtime_add(
502✔
2445
                Manager *m,
2446
                const char *id,
2447
                char **tmp_dir,
2448
                char **var_tmp_dir,
2449
                int userns_storage_socket[2],
2450
                int netns_storage_socket[2],
2451
                int ipcns_storage_socket[2],
2452
                ExecSharedRuntime **ret) {
2453

2454
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
502✔
2455
        int r;
502✔
2456

2457
        assert(m);
502✔
2458
        assert(id);
502✔
2459
        assert(tmp_dir);
502✔
2460
        assert(var_tmp_dir);
502✔
2461

2462
        /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
2463

2464
        r = exec_shared_runtime_allocate(&rt, id);
502✔
2465
        if (r < 0)
502✔
2466
                return r;
2467

2468
        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
502✔
2469
        if (r < 0)
502✔
2470
                return r;
2471

2472
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
502✔
2473
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
502✔
2474

2475
        if (userns_storage_socket) {
502✔
2476
                rt->userns_storage_socket[0] = TAKE_FD(userns_storage_socket[0]);
502✔
2477
                rt->userns_storage_socket[1] = TAKE_FD(userns_storage_socket[1]);
502✔
2478
        }
2479

2480
        if (netns_storage_socket) {
502✔
2481
                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
502✔
2482
                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
502✔
2483
        }
2484

2485
        if (ipcns_storage_socket) {
502✔
2486
                rt->ipcns_storage_socket[0] = TAKE_FD(ipcns_storage_socket[0]);
502✔
2487
                rt->ipcns_storage_socket[1] = TAKE_FD(ipcns_storage_socket[1]);
502✔
2488
        }
2489

2490
        rt->manager = m;
502✔
2491

2492
        if (ret)
502✔
2493
                *ret = rt;
175✔
2494
        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
2495
        TAKE_PTR(rt);
502✔
2496
        return 0;
502✔
2497
}
2498

2499
static int exec_shared_runtime_make(
21,703✔
2500
                Manager *m,
2501
                const ExecContext *c,
2502
                const char *id,
2503
                ExecSharedRuntime **ret) {
2504

2505
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
21,703✔
2506
        _cleanup_close_pair_ int userns_storage_socket[2] = EBADF_PAIR, netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
65,109✔
2507
        int r;
21,703✔
2508

2509
        assert(m);
21,703✔
2510
        assert(c);
21,703✔
2511
        assert(id);
21,703✔
2512

2513
        /* It is not necessary to create ExecSharedRuntime object. */
2514
        if (!c->user_namespace_path && !exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) &&
21,703✔
2515
            c->private_tmp != PRIVATE_TMP_CONNECTED && c->private_var_tmp != PRIVATE_TMP_CONNECTED) {
21,605✔
2516
                *ret = NULL;
21,528✔
2517
                return 0;
21,528✔
2518
        }
2519

2520
        if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
211✔
2521
            !prefixed_path_strv_contains(c->inaccessible_paths, "/tmp")) {
36✔
2522

2523
                r = setup_tmp_dir_one(id, "/tmp", &tmp_dir);
36✔
2524
                if (r < 0)
36✔
2525
                        return r;
2526
        }
2527

2528
        if (c->private_var_tmp == PRIVATE_TMP_CONNECTED &&
258✔
2529
            !prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") &&
166✔
2530
            !prefixed_path_strv_contains(c->inaccessible_paths, "/var")) {
83✔
2531

2532
                r = setup_tmp_dir_one(id, "/var/tmp", &var_tmp_dir);
83✔
2533
                if (r < 0)
83✔
2534
                        return r;
2535
        }
2536

2537
        if (c->user_namespace_path)
175✔
2538
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, userns_storage_socket) < 0)
×
2539
                        return -errno;
×
2540

2541
        if (exec_needs_network_namespace(c))
175✔
2542
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
97✔
2543
                        return -errno;
×
2544

2545
        if (exec_needs_ipc_namespace(c))
175✔
2546
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
88✔
2547
                        return -errno;
×
2548

2549
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_storage_socket, netns_storage_socket, ipcns_storage_socket, ret);
175✔
2550
        if (r < 0)
175✔
2551
                return r;
×
2552

2553
        return 1;
2554
}
2555

2556
int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
22,030✔
2557
        ExecSharedRuntime *rt;
22,030✔
2558
        int r;
22,030✔
2559

2560
        assert(m);
22,030✔
2561
        assert(id);
22,030✔
2562
        assert(ret);
22,030✔
2563

2564
        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
22,030✔
2565
        if (rt)
22,030✔
2566
                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
2567
                goto ref;
327✔
2568

2569
        if (!create) {
21,703✔
2570
                *ret = NULL;
×
2571
                return 0;
×
2572
        }
2573

2574
        /* If not found, then create a new object. */
2575
        r = exec_shared_runtime_make(m, c, id, &rt);
21,703✔
2576
        if (r < 0)
21,703✔
2577
                return r;
2578
        if (r == 0) {
21,703✔
2579
                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
2580
                *ret = NULL;
21,528✔
2581
                return 0;
21,528✔
2582
        }
2583

2584
ref:
175✔
2585
        /* increment reference counter. */
2586
        rt->n_ref++;
502✔
2587
        *ret = rt;
502✔
2588
        return 1;
502✔
2589
}
2590

2591
int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
223✔
2592
        ExecSharedRuntime *rt;
223✔
2593

2594
        assert(m);
223✔
2595
        assert(f);
223✔
2596
        assert(fds);
223✔
2597

2598
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
579✔
2599
                fprintf(f, "exec-runtime=%s", rt->id);
356✔
2600

2601
                if (rt->tmp_dir)
356✔
2602
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
175✔
2603

2604
                if (rt->var_tmp_dir)
356✔
2605
                        fprintf(f, " var-tmp-dir=%s", rt->var_tmp_dir);
356✔
2606

2607
                if (rt->userns_storage_socket[0] >= 0) {
356✔
2608
                        int copy;
×
2609

2610
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[0]);
×
2611
                        if (copy < 0)
×
2612
                                return copy;
×
2613

2614
                        fprintf(f, " userns-socket-0=%i", copy);
×
2615
                }
2616

2617
                if (rt->userns_storage_socket[1] >= 0) {
356✔
2618
                        int copy;
×
2619

2620
                        copy = fdset_put_dup(fds, rt->userns_storage_socket[1]);
×
2621
                        if (copy < 0)
×
2622
                                return copy;
2623

2624
                        fprintf(f, " userns-socket-1=%i", copy);
×
2625
                }
2626

2627
                if (rt->netns_storage_socket[0] >= 0) {
356✔
2628
                        int copy;
10✔
2629

2630
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
10✔
2631
                        if (copy < 0)
10✔
2632
                                return copy;
2633

2634
                        fprintf(f, " netns-socket-0=%i", copy);
10✔
2635
                }
2636

2637
                if (rt->netns_storage_socket[1] >= 0) {
356✔
2638
                        int copy;
10✔
2639

2640
                        copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
10✔
2641
                        if (copy < 0)
10✔
2642
                                return copy;
2643

2644
                        fprintf(f, " netns-socket-1=%i", copy);
10✔
2645
                }
2646

2647
                if (rt->ipcns_storage_socket[0] >= 0) {
356✔
2648
                        int copy;
×
2649

2650
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[0]);
×
2651
                        if (copy < 0)
×
2652
                                return copy;
2653

2654
                        fprintf(f, " ipcns-socket-0=%i", copy);
×
2655
                }
2656

2657
                if (rt->ipcns_storage_socket[1] >= 0) {
356✔
2658
                        int copy;
×
2659

2660
                        copy = fdset_put_dup(fds, rt->ipcns_storage_socket[1]);
×
2661
                        if (copy < 0)
×
2662
                                return copy;
2663

2664
                        fprintf(f, " ipcns-socket-1=%i", copy);
×
2665
                }
2666

2667
                fputc('\n', f);
356✔
2668
        }
2669

2670
        return 0;
223✔
2671
}
2672

2673
int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
482,662✔
2674
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
482,662✔
2675
        ExecSharedRuntime *rt = NULL;
482,662✔
2676
        int r;
482,662✔
2677

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

2683
        assert(u);
482,662✔
2684
        assert(key);
482,662✔
2685
        assert(value);
482,662✔
2686

2687
        /* Manager manages ExecSharedRuntime objects by the unit id.
2688
         * So, we omit the serialized text when the unit does not have id (yet?)... */
2689
        if (isempty(u->id)) {
482,662✔
2690
                log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
×
2691
                return 0;
×
2692
        }
2693

2694
        if (u->manager) {
482,662✔
2695
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
482,662✔
2696
                        return log_oom();
×
2697

2698
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
482,662✔
2699
        }
2700
        if (!rt) {
482,662✔
2701
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
472,078✔
2702
                        return log_oom();
×
2703

2704
                rt = rt_create;
472,078✔
2705
        }
2706

2707
        if (streq(key, "tmp-dir")) {
482,662✔
2708
                if (free_and_strdup_warn(&rt->tmp_dir, value) < 0)
×
2709
                        return -ENOMEM;
2710

2711
        } else if (streq(key, "var-tmp-dir")) {
482,662✔
2712
                if (free_and_strdup_warn(&rt->var_tmp_dir, value) < 0)
×
2713
                        return -ENOMEM;
2714

2715
        } else if (streq(key, "netns-socket-0")) {
482,662✔
2716

2717
                safe_close(rt->netns_storage_socket[0]);
×
2718
                rt->netns_storage_socket[0] = deserialize_fd(fds, value);
×
2719
                if (rt->netns_storage_socket[0] < 0)
×
2720
                        return 0;
2721

2722
        } else if (streq(key, "netns-socket-1")) {
482,662✔
2723

2724
                safe_close(rt->netns_storage_socket[1]);
×
2725
                rt->netns_storage_socket[1] = deserialize_fd(fds, value);
×
2726
                if (rt->netns_storage_socket[1] < 0)
×
2727
                        return 0;
2728
        } else
2729
                return 0;
2730

2731
        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
2732
        if (rt_create && u->manager) {
×
2733
                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
×
2734
                if (r < 0) {
×
2735
                        log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
×
2736
                        return 0;
×
2737
                }
2738

2739
                rt_create->manager = u->manager;
×
2740

2741
                /* Avoid cleanup */
2742
                TAKE_PTR(rt_create);
×
2743
        }
2744

2745
        return 1;
2746
}
2747

2748
int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
327✔
2749
        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
327✔
2750
        char *id = NULL;
327✔
2751
        int r, userns_fdpair[] = {-1, -1}, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
327✔
2752
        const char *p, *v = ASSERT_PTR(value);
327✔
2753
        size_t n;
327✔
2754

2755
        assert(m);
327✔
2756
        assert(fds);
327✔
2757

2758
        n = strcspn(v, " ");
327✔
2759
        id = strndupa_safe(v, n);
327✔
2760
        if (v[n] != ' ')
327✔
2761
                goto finalize;
×
2762
        p = v + n + 1;
327✔
2763

2764
        v = startswith(p, "tmp-dir=");
327✔
2765
        if (v) {
327✔
2766
                n = strcspn(v, " ");
161✔
2767
                tmp_dir = strndup(v, n);
161✔
2768
                if (!tmp_dir)
161✔
2769
                        return log_oom();
×
2770
                if (v[n] != ' ')
161✔
2771
                        goto finalize;
×
2772
                p = v + n + 1;
161✔
2773
        }
2774

2775
        v = startswith(p, "var-tmp-dir=");
327✔
2776
        if (v) {
327✔
2777
                n = strcspn(v, " ");
327✔
2778
                var_tmp_dir = strndup(v, n);
327✔
2779
                if (!var_tmp_dir)
327✔
2780
                        return log_oom();
×
2781
                if (v[n] != ' ')
327✔
2782
                        goto finalize;
320✔
2783
                p = v + n + 1;
7✔
2784
        }
2785

2786
        v = startswith(p, "userns-socket-0=");
7✔
2787
        if (v) {
7✔
2788
                char *buf;
×
2789

2790
                n = strcspn(v, " ");
×
2791
                buf = strndupa_safe(v, n);
×
2792

2793
                userns_fdpair[0] = deserialize_fd(fds, buf);
×
2794
                if (userns_fdpair[0] < 0)
×
2795
                        return userns_fdpair[0];
2796
                if (v[n] != ' ')
×
2797
                        goto finalize;
×
2798
                p = v + n + 1;
×
2799
        }
2800

2801
        v = startswith(p, "userns-socket-1=");
7✔
2802
        if (v) {
7✔
2803
                char *buf;
×
2804

2805
                n = strcspn(v, " ");
×
2806
                buf = strndupa_safe(v, n);
×
2807

2808
                userns_fdpair[1] = deserialize_fd(fds, buf);
×
2809
                if (userns_fdpair[1] < 0)
×
2810
                        return userns_fdpair[1];
2811
                if (v[n] != ' ')
×
2812
                        goto finalize;
×
2813
                p = v + n + 1;
×
2814
        }
2815

2816
        v = startswith(p, "netns-socket-0=");
7✔
2817
        if (v) {
7✔
2818
                char *buf;
7✔
2819

2820
                n = strcspn(v, " ");
7✔
2821
                buf = strndupa_safe(v, n);
7✔
2822

2823
                netns_fdpair[0] = deserialize_fd(fds, buf);
7✔
2824
                if (netns_fdpair[0] < 0)
7✔
2825
                        return netns_fdpair[0];
2826
                if (v[n] != ' ')
7✔
2827
                        goto finalize;
×
2828
                p = v + n + 1;
7✔
2829
        }
2830

2831
        v = startswith(p, "netns-socket-1=");
7✔
2832
        if (v) {
7✔
2833
                char *buf;
7✔
2834

2835
                n = strcspn(v, " ");
7✔
2836
                buf = strndupa_safe(v, n);
7✔
2837

2838
                netns_fdpair[1] = deserialize_fd(fds, buf);
7✔
2839
                if (netns_fdpair[1] < 0)
7✔
2840
                        return netns_fdpair[1];
2841
                if (v[n] != ' ')
7✔
2842
                        goto finalize;
7✔
2843
                p = v + n + 1;
×
2844
        }
2845

2846
        v = startswith(p, "ipcns-socket-0=");
×
2847
        if (v) {
×
2848
                char *buf;
×
2849

2850
                n = strcspn(v, " ");
×
2851
                buf = strndupa_safe(v, n);
×
2852

2853
                ipcns_fdpair[0] = deserialize_fd(fds, buf);
×
2854
                if (ipcns_fdpair[0] < 0)
×
2855
                        return ipcns_fdpair[0];
2856
                if (v[n] != ' ')
×
2857
                        goto finalize;
×
2858
                p = v + n + 1;
×
2859
        }
2860

2861
        v = startswith(p, "ipcns-socket-1=");
×
2862
        if (v) {
×
2863
                char *buf;
×
2864

2865
                n = strcspn(v, " ");
×
2866
                buf = strndupa_safe(v, n);
×
2867

2868
                ipcns_fdpair[1] = deserialize_fd(fds, buf);
×
2869
                if (ipcns_fdpair[1] < 0)
×
2870
                        return ipcns_fdpair[1];
2871
        }
2872

2873
finalize:
×
2874
        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, userns_fdpair, netns_fdpair, ipcns_fdpair, NULL);
327✔
2875
        if (r < 0)
327✔
2876
                return log_debug_errno(r, "Failed to add exec-runtime: %m");
×
2877
        return 0;
2878
}
2879

2880
void exec_shared_runtime_vacuum(Manager *m) {
1,978✔
2881
        ExecSharedRuntime *rt;
1,978✔
2882

2883
        assert(m);
1,978✔
2884

2885
        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
2886

2887
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
2,305✔
2888
                if (rt->n_ref > 0)
327✔
2889
                        continue;
327✔
2890

2891
                (void) exec_shared_runtime_free(rt);
×
2892
        }
2893
}
1,978✔
2894

2895
int exec_runtime_make(
22,030✔
2896
                const Unit *unit,
2897
                const ExecContext *context,
2898
                ExecSharedRuntime *shared,
2899
                DynamicCreds *creds,
2900
                ExecRuntime **ret) {
2901
        _cleanup_close_pair_ int ephemeral_storage_socket[2] = EBADF_PAIR;
22,030✔
2902
        _cleanup_free_ char *ephemeral = NULL;
22,030✔
2903
        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
22,030✔
2904
        int r;
22,030✔
2905

2906
        assert(unit);
22,030✔
2907
        assert(context);
22,030✔
2908
        assert(ret);
22,030✔
2909

2910
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
22,030✔
2911
                *ret = NULL;
21,474✔
2912
                return 0;
21,474✔
2913
        }
2914

2915
        if (exec_needs_ephemeral(context)) {
556✔
2916
                r = mkdir_p("/var/lib/systemd/ephemeral-trees", 0755);
×
2917
                if (r < 0)
×
2918
                        return r;
2919

2920
                r = tempfn_random_child("/var/lib/systemd/ephemeral-trees", unit->id, &ephemeral);
×
2921
                if (r < 0)
×
2922
                        return r;
2923

2924
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ephemeral_storage_socket) < 0)
×
2925
                        return -errno;
×
2926
        }
2927

2928
        rt = new(ExecRuntime, 1);
556✔
2929
        if (!rt)
556✔
2930
                return -ENOMEM;
2931

2932
        *rt = (ExecRuntime) {
556✔
2933
                .shared = shared,
2934
                .dynamic_creds = creds,
2935
                .ephemeral_copy = TAKE_PTR(ephemeral),
556✔
2936
                .ephemeral_storage_socket[0] = TAKE_FD(ephemeral_storage_socket[0]),
556✔
2937
                .ephemeral_storage_socket[1] = TAKE_FD(ephemeral_storage_socket[1]),
556✔
2938
        };
2939

2940
        *ret = TAKE_PTR(rt);
556✔
2941
        return 1;
556✔
2942
}
2943

2944
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
106,547✔
2945
        if (!rt)
106,547✔
2946
                return NULL;
2947

2948
        exec_shared_runtime_unref(rt->shared);
556✔
2949
        dynamic_creds_unref(rt->dynamic_creds);
556✔
2950

2951
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
556✔
2952

2953
        safe_close_pair(rt->ephemeral_storage_socket);
556✔
2954
        return mfree(rt);
556✔
2955
}
2956

2957
ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
8,537✔
2958
        if (!rt)
8,537✔
2959
                return NULL;
2960

2961
        rt->shared = exec_shared_runtime_destroy(rt->shared);
165✔
2962
        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
165✔
2963
        return exec_runtime_free(rt);
165✔
2964
}
2965

2966
void exec_runtime_clear(ExecRuntime *rt) {
34✔
2967
        if (!rt)
34✔
2968
                return;
2969

2970
        safe_close_pair(rt->ephemeral_storage_socket);
34✔
2971
        rt->ephemeral_copy = mfree(rt->ephemeral_copy);
34✔
2972
}
2973

2974
void exec_params_shallow_clear(ExecParameters *p) {
4,594✔
2975
        if (!p)
4,594✔
2976
                return;
2977

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

2981
        p->environment = strv_free(p->environment);
4,594✔
2982
        p->fd_names = strv_free(p->fd_names);
4,594✔
2983
        p->files_env = strv_free(p->files_env);
4,594✔
2984
        p->fds = mfree(p->fds);
4,594✔
2985
        p->root_directory_fd = safe_close(p->root_directory_fd);
4,594✔
2986
        p->exec_fd = safe_close(p->exec_fd);
4,594✔
2987
        p->user_lookup_fd = -EBADF;
4,594✔
2988
        p->bpf_restrict_fs_map_fd = -EBADF;
4,594✔
2989
        p->unit_id = mfree(p->unit_id);
4,594✔
2990
        p->invocation_id = SD_ID128_NULL;
4,594✔
2991
        p->invocation_id_string[0] = '\0';
4,594✔
2992
        p->confirm_spawn = mfree(p->confirm_spawn);
4,594✔
2993
}
2994

2995
void exec_params_deep_clear(ExecParameters *p) {
34✔
2996
        if (!p)
34✔
2997
                return;
2998

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

3003
        close_many_unset(p->fds, p->n_socket_fds + p->n_stashed_fds);
34✔
3004

3005
        p->cgroup_path = mfree(p->cgroup_path);
34✔
3006

3007
        if (p->prefix) {
34✔
3008
                free_many_charp(p->prefix, _EXEC_DIRECTORY_TYPE_MAX);
34✔
3009
                p->prefix = mfree(p->prefix);
34✔
3010
        }
3011

3012
        p->received_credentials_directory = mfree(p->received_credentials_directory);
34✔
3013
        p->received_encrypted_credentials_directory = mfree(p->received_encrypted_credentials_directory);
34✔
3014

3015
        if (p->idle_pipe) {
34✔
3016
                close_many_and_free(p->idle_pipe, 4);
×
3017
                p->idle_pipe = NULL;
×
3018
        }
3019

3020
        p->stdin_fd = safe_close(p->stdin_fd);
34✔
3021
        p->stdout_fd = safe_close(p->stdout_fd);
34✔
3022
        p->stderr_fd = safe_close(p->stderr_fd);
34✔
3023

3024
        p->notify_socket = mfree(p->notify_socket);
34✔
3025

3026
        open_file_free_many(&p->open_files);
34✔
3027

3028
        p->fallback_smack_process_label = mfree(p->fallback_smack_process_label);
34✔
3029

3030
        exec_params_shallow_clear(p);
34✔
3031
}
3032

3033
void exec_directory_done(ExecDirectory *d) {
532,085✔
3034
        if (!d)
532,085✔
3035
                return;
3036

3037
        FOREACH_ARRAY(i, d->items, d->n_items) {
535,613✔
3038
                free(i->path);
3,528✔
3039
                strv_free(i->symlinks);
3,528✔
3040
        }
3041

3042
        d->items = mfree(d->items);
532,085✔
3043
        d->n_items = 0;
532,085✔
3044
        d->mode = 0755;
532,085✔
3045
}
3046

3047
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
6,628✔
3048
        assert(d);
6,628✔
3049
        assert(path);
6,628✔
3050

3051
        FOREACH_ARRAY(i, d->items, d->n_items)
10,332✔
3052
                if (path_equal(i->path, path))
3,719✔
3053
                        return i;
3054

3055
        return NULL;
3056
}
3057

3058
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
6,628✔
3059
        _cleanup_strv_free_ char **s = NULL;
×
3060
        _cleanup_free_ char *p = NULL;
6,628✔
3061
        ExecDirectoryItem *existing;
6,628✔
3062
        int r;
6,628✔
3063

3064
        assert(d);
6,628✔
3065
        assert(path);
6,628✔
3066

3067
        existing = exec_directory_find(d, path);
6,628✔
3068
        if (existing) {
6,628✔
3069
                r = strv_extend(&existing->symlinks, symlink);
15✔
3070
                if (r < 0)
15✔
3071
                        return r;
3072

3073
                existing->flags |= flags;
15✔
3074

3075
                return 0; /* existing item is updated */
15✔
3076
        }
3077

3078
        p = strdup(path);
6,613✔
3079
        if (!p)
6,613✔
3080
                return -ENOMEM;
3081

3082
        if (symlink) {
6,613✔
3083
                s = strv_new(symlink);
6✔
3084
                if (!s)
6✔
3085
                        return -ENOMEM;
3086
        }
3087

3088
        if (!GREEDY_REALLOC(d->items, d->n_items + 1))
6,613✔
3089
                return -ENOMEM;
3090

3091
        d->items[d->n_items++] = (ExecDirectoryItem) {
6,613✔
3092
                .path = TAKE_PTR(p),
6,613✔
3093
                .symlinks = TAKE_PTR(s),
6,613✔
3094
                .flags = flags,
3095
        };
3096

3097
        return 1; /* new item is added */
6,613✔
3098
}
3099

3100
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
1,772✔
3101
        assert(a);
1,772✔
3102
        assert(b);
1,772✔
3103

3104
        return path_compare(a->path, b->path);
1,772✔
3105
}
3106

3107
void exec_directory_sort(ExecDirectory *d) {
301,057✔
3108
        assert(d);
301,057✔
3109

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

3115
        if (d->n_items <= 1)
301,057✔
3116
                return;
3117

3118
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
286✔
3119

3120
        for (size_t i = 1; i < d->n_items; i++)
1,611✔
3121
                for (size_t j = 0; j < i; j++)
3,544✔
3122
                        if (path_startswith(d->items[i].path, d->items[j].path)) {
2,520✔
3123
                                d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
15✔
3124
                                break;
15✔
3125
                        }
3126
}
3127

3128
ExecCleanMask exec_clean_mask_from_string(const char *s) {
4✔
3129
        ExecDirectoryType t;
4✔
3130

3131
        assert(s);
4✔
3132

3133
        if (streq(s, "all"))
4✔
3134
                return EXEC_CLEAN_ALL;
3135
        if (streq(s, "fdstore"))
3✔
3136
                return EXEC_CLEAN_FDSTORE;
3137

3138
        t = exec_resource_type_from_string(s);
2✔
3139
        if (t < 0)
2✔
3140
                return (ExecCleanMask) t;
3141

3142
        return 1U << t;
2✔
3143
}
3144

3145
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
3146
        [EXEC_INPUT_NULL]      = "null",
3147
        [EXEC_INPUT_TTY]       = "tty",
3148
        [EXEC_INPUT_TTY_FORCE] = "tty-force",
3149
        [EXEC_INPUT_TTY_FAIL]  = "tty-fail",
3150
        [EXEC_INPUT_SOCKET]    = "socket",
3151
        [EXEC_INPUT_NAMED_FD]  = "fd",
3152
        [EXEC_INPUT_DATA]      = "data",
3153
        [EXEC_INPUT_FILE]      = "file",
3154
};
3155

3156
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
26,483✔
3157

3158
static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
3159
        [EXEC_OUTPUT_INHERIT]             = "inherit",
3160
        [EXEC_OUTPUT_NULL]                = "null",
3161
        [EXEC_OUTPUT_TTY]                 = "tty",
3162
        [EXEC_OUTPUT_KMSG]                = "kmsg",
3163
        [EXEC_OUTPUT_KMSG_AND_CONSOLE]    = "kmsg+console",
3164
        [EXEC_OUTPUT_JOURNAL]             = "journal",
3165
        [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
3166
        [EXEC_OUTPUT_SOCKET]              = "socket",
3167
        [EXEC_OUTPUT_NAMED_FD]            = "fd",
3168
        [EXEC_OUTPUT_FILE]                = "file",
3169
        [EXEC_OUTPUT_FILE_APPEND]         = "append",
3170
        [EXEC_OUTPUT_FILE_TRUNCATE]       = "truncate",
3171
};
3172

3173
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
53,428✔
3174

3175
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
3176
        [EXEC_UTMP_INIT]  = "init",
3177
        [EXEC_UTMP_LOGIN] = "login",
3178
        [EXEC_UTMP_USER]  = "user",
3179
};
3180

3181
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
24,878✔
3182

3183
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
3184
        [EXEC_PRESERVE_NO]         = "no",
3185
        [EXEC_PRESERVE_YES]        = "yes",
3186
        [EXEC_PRESERVE_RESTART]    = "restart",
3187
        [EXEC_PRESERVE_ON_SUCCESS] = "on-success",
3188
};
3189

3190
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
34,119✔
3191

3192
/* This table maps ExecDirectoryType to the symlink setting it is configured with in the unit */
3193
static const char* const exec_directory_type_symlink_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3194
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectorySymlink",
3195
        [EXEC_DIRECTORY_STATE]         = "StateDirectorySymlink",
3196
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectorySymlink",
3197
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectorySymlink",
3198
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectorySymlink",
3199
};
3200

3201
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3202

3203
static const char* const exec_directory_type_mode_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3204
        [EXEC_DIRECTORY_RUNTIME]       = "RuntimeDirectoryMode",
3205
        [EXEC_DIRECTORY_STATE]         = "StateDirectoryMode",
3206
        [EXEC_DIRECTORY_CACHE]         = "CacheDirectoryMode",
3207
        [EXEC_DIRECTORY_LOGS]          = "LogsDirectoryMode",
3208
        [EXEC_DIRECTORY_CONFIGURATION] = "ConfigurationDirectoryMode",
3209
};
3210

3211
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3212

3213
/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
3214
 * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
3215
 * directories, specifically .timer units with their timestamp touch file. */
3216
static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
3217
        [EXEC_DIRECTORY_RUNTIME]       = "runtime",
3218
        [EXEC_DIRECTORY_STATE]         = "state",
3219
        [EXEC_DIRECTORY_CACHE]         = "cache",
3220
        [EXEC_DIRECTORY_LOGS]          = "logs",
3221
        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
3222
};
3223

3224
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
447✔
3225

3226
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
3227
        [EXEC_KEYRING_INHERIT] = "inherit",
3228
        [EXEC_KEYRING_PRIVATE] = "private",
3229
        [EXEC_KEYRING_SHARED]  = "shared",
3230
};
3231

3232
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
25,689✔
3233

3234
static const char* const exec_memory_thp_table[_EXEC_MEMORY_THP_MAX] = {
3235
        [EXEC_MEMORY_THP_INHERIT] = "inherit",
3236
        [EXEC_MEMORY_THP_DISABLE] = "disable",
3237
        [EXEC_MEMORY_THP_MADVISE] = "madvise",
3238
        [EXEC_MEMORY_THP_SYSTEM]  = "system",
3239
};
3240

3241
DEFINE_STRING_TABLE_LOOKUP(exec_memory_thp, ExecMemoryTHP);
25,264✔
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