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

systemd / systemd / 26546993077

27 May 2026 08:34PM UTC coverage: 72.995% (+0.3%) from 72.667%
26546993077

push

github

bluca
test-pressure: set timeout to make not wait forever

If this runs on a slow or busy machine, then we may not get enough
pressure to trigger the event sources. In such case, the test does not
finish. It is problematic when the test is _not_ run with 'meson test',
e.g. debian/ubuntu CIs.

Let's introduce a timeout for each event loop, and skip test cases
gracefully.

8 of 12 new or added lines in 1 file covered. (66.67%)

19671 existing lines in 226 files now uncovered.

337119 of 461841 relevant lines covered (72.99%)

1326365.62 hits per line

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

80.28
/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,451✔
71
        assert(context);
16,451✔
72

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

76
        if (context->tty_path)
15,506✔
77
                return context->tty_path;
743✔
78

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

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

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

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

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

98
        if (!tty_path)
552✔
99
                tty_path = exec_context_tty_path(context);
282✔
100

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

105
        /* Fill in data from kernel command line if anything is unspecified */
106
        if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
552✔
107
                (void) proc_cmdline_tty_size(
500✔
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 &&
971✔
117
            exec_context_shall_ansi_seq_reset(context) &&
712✔
118
            isatty_safe(input_fd)) {
293✔
119
                r = terminal_get_size(input_fd, output_fd, &rows, &cols, /* try_dsr= */ true, /* try_csi18= */ false);
293✔
120
                if (r < 0)
293✔
121
                        log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
293✔
122
        }
123

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

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

131
        assert(context);
15,028✔
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,028✔
139

140
        if (parameters && parameters->stdout_fd >= 0 && isatty_safe(parameters->stdout_fd))
15,028✔
141
                fd = parameters->stdout_fd;
26✔
142
        else if (path && exec_context_has_tty(context)) {
15,002✔
143
                fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
270✔
144
                if (fd < 0)
270✔
145
                        return (void) log_debug_errno(fd, "Failed to open terminal '%s', ignoring: %m", path);
14,732✔
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();
296✔
153
        if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
296✔
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))
296✔
156
                log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m");
×
157
        else if (lock_fd < 0)
296✔
158
                log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
×
159

160
        if (context->tty_reset)
296✔
161
                (void) terminal_reset_defensive(
233✔
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));
229✔
165

166
        r = exec_context_apply_tty_size(context, fd, fd, path);
296✔
167
        if (r < 0)
296✔
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)) {
553✔
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)
296✔
188
                (void) terminal_vhangup_fd(fd);
106✔
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);
296✔
193

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

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

201
        return context->private_network || context->network_namespace_path;
88,542✔
202
}
203

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

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

211
        return context->private_ipc || context->ipc_namespace_path;
82,220✔
212
}
213

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

218
ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context) {
102,038✔
219
        assert(context);
102,038✔
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)) {
102,038✔
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;
102,038✔
232
}
233

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

237
        return needs_cgroup_namespace(exec_get_protect_control_groups(context));
60,704✔
238
}
239

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

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

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

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

252
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params) {
133,772✔
253
        assert(context);
133,772✔
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))
133,772✔
257
                return false;
258

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

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

265
        if (context->root_image ||
42,660✔
266
            context->root_mstack)
42,577✔
267
                return true;
268

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

272
        if (!strv_isempty(context->read_write_paths) ||
42,551✔
273
            !strv_isempty(context->read_only_paths) ||
39,969✔
274
            !strv_isempty(context->inaccessible_paths) ||
39,953✔
275
            !strv_isempty(context->exec_paths) ||
39,915✔
276
            !strv_isempty(context->no_exec_paths))
39,915✔
277
                return true;
278

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

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

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

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

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

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

297
        if (context->private_devices ||
39,629✔
298
            context->private_tmp != PRIVATE_TMP_NO || /* no need to check for private_var_tmp here, private_tmp is never demoted to "no" */
38,072✔
299
            context->private_mounts > 0 ||
37,980✔
300
            (context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
37,384✔
301
            context->protect_system != PROTECT_SYSTEM_NO ||
37,351✔
302
            context->protect_home != PROTECT_HOME_NO ||
37,351✔
303
            context->protect_kernel_tunables ||
37,351✔
304
            context->protect_kernel_modules ||
37,351✔
305
            context->protect_kernel_logs ||
71,167✔
306
            exec_needs_cgroup_mount(context) ||
35,582✔
307
            context->protect_proc != PROTECT_PROC_DEFAULT ||
35,522✔
308
            context->proc_subset != PROC_SUBSET_ALL ||
35,436✔
309
            context->private_bpf != PRIVATE_BPF_NO ||
70,854✔
310
            exec_needs_ipc_namespace(context) ||
70,854✔
311
            exec_needs_pid_namespace(context, params))
35,427✔
312
                return true;
313

314
        if (context->root_directory) {
35,344✔
315
                if (exec_context_get_effective_mount_apivfs(context))
26✔
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,318✔
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,318✔
334
                return true;
335

336
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
211,516✔
337
                FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
181,501✔
338
                        if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
5,297✔
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,163✔
345
        assert(context);
4,163✔
346
        assert(params);
4,163✔
347

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

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

354
        if (!exec_context_with_rootfs(context))
3,414✔
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,380✔
367
        assert(params);
24,380✔
368

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

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

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

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

383
        if (!context->dynamic_user)
10,411✔
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,473✔
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,473✔
398
}
399

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

406
        const char *subgroup = NULL;
12,966✔
407
        char *p;
12,966✔
408

409
        assert(params);
12,966✔
410
        assert(c);
12,966✔
411
        assert(ret);
12,966✔
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)) {
12,966✔
424
                if (FLAGS_SET(params->flags, EXEC_IS_CONTROL))
785✔
425
                        subgroup = ".control";
426
                else
427
                        subgroup = c->delegate_subgroup;
722✔
428
        }
429

430
        if (subgroup)
722✔
431
                p = path_join(prefix, subgroup);
785✔
432
        else
433
                p = strdup(strempty(prefix));
12,189✔
434
        if (!p)
12,966✔
435
                return -ENOMEM;
436

437
        *ret = p;
12,966✔
438
        return !!subgroup;
12,966✔
439
}
440

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

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

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

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

455
        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
8,886✔
456

457
        log_unit_struct(unit, LOG_DEBUG,
4,443✔
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,443✔
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,443✔
475
        _cleanup_fdset_free_ FDSet *fdset = NULL;
×
476
        _cleanup_fclose_ FILE *f = NULL;
4,443✔
477
        int r;
4,443✔
478

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

491
        LOG_CONTEXT_PUSH_UNIT(unit);
8,886✔
492

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

617
void exec_context_init(ExecContext *c) {
113,067✔
618
        assert(c);
113,067✔
619

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

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

650
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
678,402✔
651
                d->mode = 0755;
565,335✔
652

653
        numa_policy_reset(&c->numa_policy);
113,067✔
654

655
        assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
113,067✔
656
}
113,067✔
657

658
void exec_context_done(ExecContext *c) {
100,912✔
659
        assert(c);
100,912✔
660

661
        c->environment = strv_free(c->environment);
100,912✔
662
        c->environment_files = strv_free(c->environment_files);
100,912✔
663
        c->pass_environment = strv_free(c->pass_environment);
100,912✔
664
        c->unset_environment = strv_free(c->unset_environment);
100,912✔
665

666
        rlimit_free_all(c->rlimit);
100,912✔
667

668
        for (size_t l = 0; l < 3; l++) {
403,648✔
669
                c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
302,736✔
670
                c->stdio_file[l] = mfree(c->stdio_file[l]);
302,736✔
671
        }
672

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

688
        c->supplementary_groups = strv_free(c->supplementary_groups);
100,912✔
689

690
        c->pam_name = mfree(c->pam_name);
100,912✔
691

692
        c->read_only_paths = strv_free(c->read_only_paths);
100,912✔
693
        c->read_write_paths = strv_free(c->read_write_paths);
100,912✔
694
        c->inaccessible_paths = strv_free(c->inaccessible_paths);
100,912✔
695
        c->exec_paths = strv_free(c->exec_paths);
100,912✔
696
        c->no_exec_paths = strv_free(c->no_exec_paths);
100,912✔
697
        c->exec_search_path = strv_free(c->exec_search_path);
100,912✔
698

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

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

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

721
        c->restrict_filesystems = set_free(c->restrict_filesystems);
100,912✔
722

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

728
        FOREACH_ARRAY(d, c->directories, _EXEC_DIRECTORY_TYPE_MAX)
605,472✔
729
                exec_directory_done(d);
504,560✔
730

731
        c->log_level_max = -1;
100,912✔
732

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

737
        c->log_ratelimit = (RateLimit) {};
100,912✔
738

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

742
        c->user_namespace_path = mfree(c->user_namespace_path);
100,912✔
743
        c->network_namespace_path = mfree(c->network_namespace_path);
100,912✔
744
        c->ipc_namespace_path = mfree(c->ipc_namespace_path);
100,912✔
745

746
        c->log_namespace = mfree(c->log_namespace);
100,912✔
747

748
        c->load_credentials = hashmap_free(c->load_credentials);
100,912✔
749
        c->set_credentials = hashmap_free(c->set_credentials);
100,912✔
750
        c->import_credentials = ordered_set_free(c->import_credentials);
100,912✔
751

752
        c->root_image_policy = image_policy_free(c->root_image_policy);
100,912✔
753
        c->mount_image_policy = image_policy_free(c->mount_image_policy);
100,912✔
754
        c->extension_image_policy = image_policy_free(c->extension_image_policy);
100,912✔
755

756
        c->private_hostname = mfree(c->private_hostname);
100,912✔
757
}
100,912✔
758

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

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

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

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

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

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

791
        return 0;
792
}
793

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

804
        int r;
20✔
805

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

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

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

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

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

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

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

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

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

844
        if (!runtime_prefix)
8,266✔
845
                return 0;
846

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

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

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

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

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

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

875
        return 0;
876
}
877

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

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

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

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

892
        return 0;
893
}
894

895
void exec_command_done(ExecCommand *c) {
196,608✔
896
        assert(c);
196,608✔
897

898
        c->path = mfree(c->path);
196,608✔
899
        c->argv = strv_free(c->argv);
196,608✔
900
}
196,608✔
901

902
void exec_command_done_array(ExecCommand *c, size_t n) {
52,755✔
903
        FOREACH_ARRAY(i, c, n)
211,018✔
904
                exec_command_done(i);
158,263✔
905
}
52,755✔
906

907
ExecCommand* exec_command_free(ExecCommand *c) {
38,311✔
908
        if (!c)
38,311✔
909
                return NULL;
910

911
        exec_command_done(c);
38,311✔
912
        return mfree(c);
38,311✔
913
}
914

915
ExecCommand* exec_command_free_list(ExecCommand *c) {
343,905✔
916
        ExecCommand *i;
343,905✔
917

918
        while ((i = LIST_POP(command, c)))
382,216✔
919
                exec_command_free(i);
38,311✔
920

921
        return NULL;
343,905✔
922
}
923

924
void exec_command_free_array(ExecCommand **c, size_t n) {
48,123✔
925
        FOREACH_ARRAY(i, c, n)
391,206✔
926
                *i = exec_command_free_list(*i);
343,083✔
927
}
48,123✔
928

929
void exec_command_reset_status_array(ExecCommand *c, size_t n) {
8,903✔
930
        FOREACH_ARRAY(i, c, n)
35,611✔
931
                exec_status_reset(&i->exec_status);
26,708✔
932
}
8,903✔
933

934
void exec_command_reset_status_list_array(ExecCommand **c, size_t n) {
7,689✔
935
        FOREACH_ARRAY(i, c, n)
56,310✔
936
                LIST_FOREACH(command, z, *i)
53,074✔
937
                        exec_status_reset(&z->exec_status);
4,453✔
938
}
7,689✔
939

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

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

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

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

954
        switch (fd_index) {
60,096✔
955

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

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

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

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

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

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

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

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

983
        assert(c);
4,443✔
984
        assert(ret);
4,443✔
985

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

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

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

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

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

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

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

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

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

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

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

1045
        *ret = TAKE_PTR(v);
4,443✔
1046

1047
        return 0;
4,443✔
1048
}
1049

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

1053
        if (!tty)
637✔
1054
                return true;
1055

1056
        tty = skip_dev_prefix(tty);
637✔
1057

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

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

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

1069
static bool exec_context_may_touch_tty(const ExecContext *ec) {
46,930✔
1070
        assert(ec);
46,930✔
1071

1072
        return ec->tty_reset ||
93,294✔
1073
                ec->tty_vhangup ||
46,364✔
1074
                ec->tty_vt_disallocate ||
46,364✔
1075
                exec_input_is_terminal(ec->std_input) ||
46,364✔
1076
                ec->std_output == EXEC_OUTPUT_TTY ||
93,294✔
1077
                ec->std_error == EXEC_OUTPUT_TTY;
46,254✔
1078
}
1079

1080
bool exec_context_may_touch_console(const ExecContext *ec) {
44,091✔
1081

1082
        return exec_context_may_touch_tty(ec) &&
44,728✔
1083
               tty_may_match_dev_console(exec_context_tty_path(ec));
637✔
1084
}
1085

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

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

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

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

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

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

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

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

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

1128
        prefix = strempty(prefix);
×
1129

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

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

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

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

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

1169
        assert(c);
430✔
1170
        assert(f);
430✔
1171

1172
        prefix = strempty(prefix);
430✔
1173

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1437
                _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
406✔
1438

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1515
        strv_dump(f, prefix, "SupplementaryGroups", c->supplementary_groups);
430✔
1516

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1649
        if (c->syscall_errno > 0) {
430✔
1650
                fprintf(f, "%sSystemCallErrorNumber: ", prefix);
429✔
1651

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

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

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

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

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

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

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

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

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

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

1704
        return false;
×
1705
}
1706

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

1710
        assert(c);
15,684✔
1711

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

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

1719
        return ioprio_normalize(p);
15,616✔
1720
}
1721

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

1725
        /* Explicit setting wins */
1726
        if (c->mount_apivfs >= 0)
58,286✔
1727
                return c->mount_apivfs > 0;
119✔
1728

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

1733
        return false;
1734
}
1735

1736
bool exec_context_get_effective_bind_log_sockets(const ExecContext *c) {
45,726✔
1737
        assert(c);
45,726✔
1738

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

1744
        if (c->bind_log_sockets >= 0)
45,718✔
1745
                return c->bind_log_sockets > 0;
2✔
1746

1747
        if (exec_context_get_effective_mount_apivfs(c))
45,716✔
1748
                return true;
1749

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

1754
        return false;
1755
}
1756

1757
void exec_context_free_log_extra_fields(ExecContext *c) {
100,914✔
1758
        assert(c);
100,914✔
1759

1760
        FOREACH_ARRAY(field, c->log_extra_fields, c->n_log_extra_fields)
106,499✔
1761
                free(field->iov_base);
5,585✔
1762

1763
        c->log_extra_fields = mfree(c->log_extra_fields);
100,914✔
1764
        c->n_log_extra_fields = 0;
100,914✔
1765
}
100,914✔
1766

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

1773
        assert(c);
2,839✔
1774

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1871
        assert(c);
7,056✔
1872
        assert(ret);
7,056✔
1873

1874
        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
42,336✔
1875
                if (c->directories[t].n_items > 0)
35,280✔
1876
                        mask |= 1U << t;
447✔
1877

1878
        *ret = mask;
7,056✔
1879
        return 0;
7,056✔
1880
}
1881

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

1885
        assert(c);
7,842✔
1886

1887
        if (c->oom_score_adjust_set)
7,842✔
1888
                return c->oom_score_adjust;
815✔
1889

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

1894
        return n;
7,027✔
1895
}
1896

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

1902
        assert(c);
7,842✔
1903

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

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

1916
        return n;
7,842✔
1917
}
1918

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

1922
        assert(c);
7,842✔
1923

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

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

1934
        return n;
1935
}
1936

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

1940
        assert(c);
7,842✔
1941

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

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

1949
        return n < 0 ? SCHED_OTHER : n;
7,842✔
1950
}
1951

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

1956
        assert(c);
7,842✔
1957

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

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

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

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

1971
        assert(c);
7,842✔
1972

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

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

1980
        return (uint64_t) MAX(r, 0);
7,842✔
1981
}
1982

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

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

1989
        return c->user || c->dynamic_user || c->pam_name;
32,602✔
1990
}
1991

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

1995
        assert(c);
7,842✔
1996

1997
#if HAVE_SECCOMP
1998
        if (dlopen_libseccomp(LOG_DEBUG) < 0)
7,842✔
1999
                return strv_new(NULL);
×
2000

2001
        void *id, *val;
7,842✔
2002
        HASHMAP_FOREACH_KEY(val, id, c->syscall_filter) {
65,888✔
2003
                _cleanup_free_ char *name = NULL;
58,046✔
2004
                const char *e = NULL;
58,046✔
2005
                char *s;
58,046✔
2006
                int num = PTR_TO_INT(val);
58,046✔
2007

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

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

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

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

2031
        strv_sort(l);
7,842✔
2032
#endif
2033

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

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

2040
        assert(c);
7,842✔
2041

2042
#if HAVE_SECCOMP
2043
        void *id;
7,842✔
2044
        SET_FOREACH(id, c->syscall_archs) {
8,012✔
2045
                const char *name;
170✔
2046

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

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

2055
        strv_sort(l);
7,842✔
2056
#endif
2057

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

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

2064
        assert(c);
7,842✔
2065

2066
#if HAVE_SECCOMP
2067
        if (dlopen_libseccomp(LOG_DEBUG) < 0)
7,842✔
2068
                return strv_new(NULL);
×
2069

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

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

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

2082
        strv_sort(l);
7,842✔
2083
#endif
2084

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

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

2092
        assert(c);
7,842✔
2093

2094
        SET_FOREACH(af, c->address_families) {
8,381✔
2095
                const char *name;
539✔
2096

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

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

2105
        strv_sort(l);
7,842✔
2106

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

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

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

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

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

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

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

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

2137
bool exec_context_with_rootfs(const ExecContext *c) {
108,365✔
2138
        assert(c);
108,365✔
2139

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

2142
        return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd || c->root_mstack;
108,365✔
2143
}
2144

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

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

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

2155
int exec_context_has_vpicked_extensions(const ExecContext *context) {
30,457✔
2156
        int r;
30,457✔
2157

2158
        assert(context);
30,457✔
2159

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

2171
        return 0;
2172
}
2173

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

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

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

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

2190
        if (s->pid != pid)
3,622✔
2191
                *s = (ExecStatus) {
10✔
2192
                        .pid = pid,
2193
                };
2194

2195
        dual_timestamp_now(&s->exit_timestamp);
3,622✔
2196

2197
        s->code = code;
3,622✔
2198
        s->status = status;
3,622✔
2199

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

2204
void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
14,826✔
2205
        assert(s);
14,826✔
2206
        assert(ucred);
14,826✔
2207
        assert(ts);
14,826✔
2208

2209
        if (ucred->pid != s->pid)
14,826✔
2210
                *s = (ExecStatus) {
13✔
2211
                        .pid = ucred->pid,
2212
                };
2213

2214
        s->handoff_timestamp = *ts;
14,826✔
2215
}
14,826✔
2216

2217
void exec_status_reset(ExecStatus *s) {
34,553✔
2218
        assert(s);
34,553✔
2219

2220
        *s = (ExecStatus) {};
34,553✔
2221
}
34,553✔
2222

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

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

2230
        prefix = strempty(prefix);
42✔
2231

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

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

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

2252
        if (dual_timestamp_is_set(&s->exit_timestamp)) {
42✔
2253

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

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

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

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

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

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

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

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

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

2299
        prefix = strempty(prefix);
222✔
2300

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

2305
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
38,311✔
2306
        ExecCommand *end;
38,311✔
2307

2308
        assert(l);
38,311✔
2309
        assert(e);
38,311✔
2310

2311
        if (*l) {
38,311✔
2312
                /* It's kind of important, that we keep the order here */
2313
                end = LIST_FIND_TAIL(command, *l);
597✔
2314
                LIST_INSERT_AFTER(command, *l, end, e);
345✔
2315
        } else
2316
                *l = e;
37,966✔
2317
}
38,311✔
2318

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

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

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

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

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

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

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

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

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

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

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

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

2363
        return 0;
2364
}
2365

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

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

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

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

2379
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
426,950✔
2380
        assert(rt);
426,950✔
2381

2382
        if (rt->manager)
426,950✔
2383
                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
462✔
2384

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

2393
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
426,916✔
2394
        if (!rt)
426,916✔
2395
                return NULL;
2396

2397
        exec_shared_runtime_done(rt);
426,916✔
2398
        return mfree(rt);
426,916✔
2399
}
2400

2401
DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
508✔
2402
DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
436,729✔
2403

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

2408
        assert(rt->n_ref > 0);
120✔
2409
        rt->n_ref--;
120✔
2410

2411
        if (rt->n_ref > 0)
120✔
2412
                return NULL;
2413

2414
        rt->tmp_dir = destroy_tree(rt->tmp_dir);
120✔
2415
        rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir);
120✔
2416

2417
        return exec_shared_runtime_free(rt);
120✔
2418
}
2419

2420
static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
426,916✔
2421
        _cleanup_free_ char *id_copy = NULL;
853,832✔
2422
        ExecSharedRuntime *n;
426,916✔
2423

2424
        assert(ret);
426,916✔
2425

2426
        id_copy = strdup(id);
426,916✔
2427
        if (!id_copy)
426,916✔
2428
                return -ENOMEM;
2429

2430
        n = new(ExecSharedRuntime, 1);
426,916✔
2431
        if (!n)
426,916✔
2432
                return -ENOMEM;
2433

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

2441
        *ret = n;
426,916✔
2442
        return 0;
426,916✔
2443
}
2444

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

2455
        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
462✔
2456
        int r;
462✔
2457

2458
        assert(m);
462✔
2459
        assert(id);
462✔
2460
        assert(tmp_dir);
462✔
2461
        assert(var_tmp_dir);
462✔
2462

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

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

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

2473
        rt->tmp_dir = TAKE_PTR(*tmp_dir);
462✔
2474
        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
462✔
2475

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

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

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

2491
        rt->manager = m;
462✔
2492

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

2500
static int exec_shared_runtime_make(
20,243✔
2501
                Manager *m,
2502
                const ExecContext *c,
2503
                const char *id,
2504
                ExecSharedRuntime **ret) {
2505

2506
        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
20,243✔
2507
        _cleanup_close_pair_ int userns_storage_socket[2] = EBADF_PAIR, netns_storage_socket[2] = EBADF_PAIR, ipcns_storage_socket[2] = EBADF_PAIR;
60,729✔
2508
        int r;
20,243✔
2509

2510
        assert(m);
20,243✔
2511
        assert(c);
20,243✔
2512
        assert(id);
20,243✔
2513

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

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

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

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

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

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

2542
        if (exec_needs_network_namespace(c))
149✔
2543
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
71✔
2544
                        return -errno;
×
2545

2546
        if (exec_needs_ipc_namespace(c))
149✔
2547
                if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
62✔
2548
                        return -errno;
×
2549

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

2554
        return 1;
2555
}
2556

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

2561
        assert(m);
20,556✔
2562
        assert(id);
20,556✔
2563
        assert(ret);
20,556✔
2564

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

2570
        if (!create) {
20,243✔
2571
                *ret = NULL;
×
2572
                return 0;
×
2573
        }
2574

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

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

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

2595
        assert(m);
216✔
2596
        assert(f);
216✔
2597
        assert(fds);
216✔
2598

2599
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
558✔
2600
                fprintf(f, "exec-runtime=%s", rt->id);
342✔
2601

2602
                if (rt->tmp_dir)
342✔
2603
                        fprintf(f, " tmp-dir=%s", rt->tmp_dir);
168✔
2604

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2668
                fputc('\n', f);
342✔
2669
        }
2670

2671
        return 0;
216✔
2672
}
2673

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

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

2684
        assert(u);
436,267✔
2685
        assert(key);
436,267✔
2686
        assert(value);
436,267✔
2687

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

2695
        if (u->manager) {
436,267✔
2696
                if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
436,267✔
2697
                        return log_oom();
×
2698

2699
                rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
436,267✔
2700
        }
2701
        if (!rt) {
436,267✔
2702
                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
426,454✔
2703
                        return log_oom();
×
2704

2705
                rt = rt_create;
426,454✔
2706
        }
2707

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

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

2716
        } else if (streq(key, "netns-socket-0")) {
436,267✔
2717

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

2723
        } else if (streq(key, "netns-socket-1")) {
436,267✔
2724

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

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

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

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

2746
        return 1;
2747
}
2748

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

2756
        assert(m);
313✔
2757
        assert(fds);
313✔
2758

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2884
        assert(m);
1,936✔
2885

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

2888
        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
2,249✔
2889
                if (rt->n_ref > 0)
313✔
2890
                        continue;
313✔
2891

2892
                (void) exec_shared_runtime_free(rt);
×
2893
        }
2894
}
1,936✔
2895

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

2907
        assert(unit);
20,556✔
2908
        assert(context);
20,556✔
2909
        assert(ret);
20,556✔
2910

2911
        if (!shared && !creds && !exec_needs_ephemeral(context)) {
20,556✔
2912
                *ret = NULL;
20,048✔
2913
                return 0;
20,048✔
2914
        }
2915

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

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

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

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

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

2941
        *ret = TAKE_PTR(rt);
508✔
2942
        return 1;
508✔
2943
}
2944

2945
ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
101,016✔
2946
        if (!rt)
101,016✔
2947
                return NULL;
2948

2949
        exec_shared_runtime_unref(rt->shared);
508✔
2950
        dynamic_creds_unref(rt->dynamic_creds);
508✔
2951

2952
        rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy);
508✔
2953

2954
        safe_close_pair(rt->ephemeral_storage_socket);
508✔
2955
        return mfree(rt);
508✔
2956
}
2957

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3031
        exec_params_shallow_clear(p);
34✔
3032
}
3033

3034
void exec_directory_done(ExecDirectory *d) {
504,560✔
3035
        if (!d)
504,560✔
3036
                return;
3037

3038
        FOREACH_ARRAY(i, d->items, d->n_items) {
507,924✔
3039
                free(i->path);
3,364✔
3040
                strv_free(i->symlinks);
3,364✔
3041
        }
3042

3043
        d->items = mfree(d->items);
504,560✔
3044
        d->n_items = 0;
504,560✔
3045
        d->mode = 0755;
504,560✔
3046
}
3047

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

3052
        FOREACH_ARRAY(i, d->items, d->n_items)
10,086✔
3053
                if (path_equal(i->path, path))
3,649✔
3054
                        return i;
3055

3056
        return NULL;
3057
}
3058

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

3065
        assert(d);
6,452✔
3066
        assert(path);
6,452✔
3067

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

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

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

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

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

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

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

3098
        return 1; /* new item is added */
6,437✔
3099
}
3100

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

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

3108
void exec_directory_sort(ExecDirectory *d) {
286,487✔
3109
        assert(d);
286,487✔
3110

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

3116
        if (d->n_items <= 1)
286,487✔
3117
                return;
3118

3119
        typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
279✔
3120

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

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

3132
        assert(s);
4✔
3133

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

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

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

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

3157
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
26,099✔
3158

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

3174
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
52,617✔
3175

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

3182
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
24,534✔
3183

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

3191
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
33,666✔
3192

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

3202
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_symlink, ExecDirectoryType);
14✔
3203

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

UNCOV
3212
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type_mode, ExecDirectoryType);
×
3213

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

3225
DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
471✔
3226

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

3233
DEFINE_STRING_TABLE_LOOKUP(exec_keyring_mode, ExecKeyringMode);
25,322✔
3234

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

3242
DEFINE_STRING_TABLE_LOOKUP(exec_memory_thp, ExecMemoryTHP);
24,915✔
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